Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,9 +166,10 @@ therefore intentionally a configurable command key, not the second Mic switch.

On battery, the display dims after two minutes and enters desk sleep after five
minutes. Dock Mode is detected from USB input power and extends those intervals
to ten and thirty minutes. The default wireless image turns off the shared
AMOLED/audio/motor rail during desk sleep while keeping BLE alive; the optional
USB-mic image keeps its audio rail on and uses brightness-only desk sleep.
to ten and thirty minutes. Both images use brightness-only desk sleep and keep
the shared AMOLED/audio/motor rail powered so a touch or button can restore the
display reliably while BLE remains online. This uses more standby power than
cutting the shared rail.
Travel Mode is a real PM1 shutdown, so Agent alerts are missed until the power
button or USB power starts the device again. The red-button double-click uses a
firmware-confirmed clean shutdown instead of PM1's immediate hardware cut; a
Expand Down
6 changes: 3 additions & 3 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,9 @@ Codex 会完成终端工作,但 macOS 仍可能要求用户亲自:
而不是第二个 Mic switch。

电池供电时,屏幕会在两分钟后变暗、五分钟后进入桌面休眠;接入 USB 电源后会
自动进入 Dock Mode,并把这两个时间延长到十分钟和三十分钟。默认无线固件会在
桌面休眠时关闭 AMOLED/音频/马达共用电源轨,但保持 BLE 在线;可选 USB 麦克风
固件为了继续录音,会保持共用电源轨开启,并使用亮度归零式桌面休眠。旅行模式使用
自动进入 Dock Mode,并把这两个时间延长到十分钟和三十分钟。两种固件都使用
亮度归零式桌面休眠,并保持 AMOLED/音频/马达共用电源轨开启,使触屏或按键可以
可靠地恢复显示,同时保持 BLE 在线;代价是待机功耗高于切断共用电源轨。旅行模式使用
PM1 真关机,因此在
按电源键或接入 USB 重新启动前,设备会错过 Agent 在线提醒。红键双击由固件先
显示关机提示并释放 HID,再请求 PM1 关机;长按红色电源键仍保留为硬件恢复 /
Expand Down
25 changes: 14 additions & 11 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,24 +63,25 @@ promote the link to `CODEX LIVE`.

## Power lifecycle

The default wireless image has four display/power states:
Both firmware images have four display/power states:

1. active;
2. dimmed;
3. desk sleep, with the CO5300 put to sleep and the shared AMOLED/audio/motor
L3B rail disabled while the ESP32 and BLE continue running;
3. desk sleep, with display brightness set to zero while the CO5300 and shared
AMOLED/audio/motor L3B rail remain powered alongside the ESP32 and BLE;
4. Travel Mode, using M5PM1 shutdown so BLE and Agent alerts stop completely.

The pinned M5GFX framebuffer wrapper did not forward physical AMOLED sleep or
repeat initialization. `scripts/patch_m5gfx_amoled_sleep.py` applies a narrow,
checked build-time patch so the controller is put to sleep before G8 is cut and
can replay its initialization sequence after G8/G5 power-reset recovery.
repeat initialization. `scripts/patch_m5gfx_amoled_sleep.py` still applies a
narrow, checked build-time patch so recovery experiments can replay the panel
initialization sequence, but normal desk sleep does not enter that path.

USB input voltage selects Dock Mode: battery policy dims/sleeps after 2/5
minutes, while Dock Mode uses 10/30 minutes. The optional USB-mic image cannot
remove L3B without also losing its microphone, so it keeps the rail on and uses
brightness-only desk sleep. A short red-power-button click toggles desk sleep; a
fast double-click runs the clean Travel shutdown path. The firmware disables
minutes, while Dock Mode uses 10/30 minutes. Both images keep L3B powered and
use brightness-only desk sleep. Physical C152 testing showed that cutting and
restoring the rail could leave the display black even though BLE, touch, and
buttons were awake. A short red-power-button click toggles desk sleep; a fast
double-click runs the clean Travel shutdown path. The firmware disables
PM1's immediate double-click cut so it can release HID controls and show the
offline warning first. A six-second center-dial hold provides a slower fallback.
The red button's hardware long-hold download path remains available for
Expand All @@ -96,5 +97,7 @@ insertion for cold wake; no IMU or scheduled RTC wake is configured.
connected.
- Companion stops updating; the UI marks data stale after three minutes.
- Reset countdown reaches zero before a refreshed snapshot arrives.
- Brightness-only desk sleep uses more standby power than cutting L3B, but
avoids the observed black-display wake failure.
- A future M5GFX source change no longer matches the checked AMOLED patch; the
build fails instead of silently producing brightness-only sleep.
build fails instead of silently dropping the recovery support.
12 changes: 6 additions & 6 deletions docs/PORTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@
11. Only then connect the companion to the live App Server rate-limit feed.
12. Verify the battery percentage, charging bolt, Dock color, and all four
health combinations, including `CODEX LIVE` together with `SYNC STALE`.
13. On battery, short-click the red button for desk sleep. Confirm AMOLED,
speaker, and motor power are off while a real Agent completion still wakes
the display and plays the chime. In the USB-mic image, verify that an
active host capture skips the chime without interrupting audio, while an
idle host permits the local chime and restores microphone capture. Repeat
at least 100 sleep/wake cycles.
13. On battery, short-click the red button for desk sleep. Confirm the screen is
black while BLE remains connected, then verify touch and red-button wake
both restore the display. Record the higher brightness-only standby draw.
In the USB-mic image, verify that an active host capture skips the chime
without interrupting audio, while an idle host permits the local chime and
restores microphone capture. Repeat at least 100 sleep/wake cycles.
14. With USB power attached, confirm Dock Mode uses the longer idle policy and
a full battery still remains in Dock Mode even when not actively charging.
15. Unplug USB and rapidly double-click the red power button. Confirm the
Expand Down
15 changes: 9 additions & 6 deletions docs/TECHNICAL.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,12 +76,15 @@ every two seconds. Dock detection uses two consecutive samples with 4.0 V
entry and 3.5 V exit thresholds, rather than charge state, because a full
battery may stop charging while USB power remains present.

On the default image, desk sleep sends CO5300 Sleep In, releases QSPI, disables
physical M5IOE1 G8 (zero-based expander pin 7), and leaves BLE polling active.
Wake restores G8, pulses physical G5 (zero-based pin 4), and replays the pinned
panel initialization through the existing framebuffer. The build-time M5GFX
patch is exact-match and idempotent. Travel Mode releases held HID controls,
shows an offline warning, stops local feedback, and issues `M5PM1::shutdown()`.
On both images, desk sleep sets display brightness to zero while leaving the
CO5300 and physical M5IOE1 G8 (zero-based expander pin 7) powered, and BLE
polling remains active. Physical C152 validation showed that this path restores
the display on a touch or button wake; the earlier G8-off recovery could leave
the panel black even though inputs and BLE were already awake. The build-time
M5GFX patch remains exact-match and idempotent for recovery experiments. This
brightness-only policy consumes more standby power. Travel Mode releases held
HID controls, shows an offline warning, stops local feedback, and issues
`M5PM1::shutdown()`.
It is normally triggered by a firmware-detected red-button double-click. PM1's
immediate hardware double-off is disabled and read back at boot so the clean
path runs first. A six-second center-dial hold is the fallback; red-button long
Expand Down
29 changes: 10 additions & 19 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,18 +49,14 @@ constexpr uint32_t kPowerButtonDoubleClickMs = 500;
constexpr uint8_t kActiveBrightness = 120;
constexpr uint8_t kDimBrightness = 24;
// M5Unified's IOExpander_Base API is zero-based: physical M5IOE1 G8 is 7,
// while physical G5 is 4.
// while physical G5 is 4. Physical C152 testing showed that powering the
// AMOLED rail back up can leave the panel black while the ESP32, BLE, touch,
// and buttons are already awake. Keep the stable desk-sleep path
// brightness-only; the panel power helpers remain available for recovery work.
constexpr uint8_t kIoeSharedL3bEnable = 7;
constexpr uint8_t kIoePanelReset = 4;
#if defined(CODEX_STOPWATCH_USB_MIC)
// G8 powers the shared L3B rail, including the microphone path. The optional
// USB-mic image therefore keeps G8 on and uses brightness-only desk sleep.
constexpr bool kSleepPanelControllerInDeskSleep = false;
constexpr bool kCutSharedRailInDeskSleep = false;
#else
// The default wireless image can remove the AMOLED/audio/motor L3B rail while
// leaving the ESP32, touch controller, PM1, and BLE transport alive.
constexpr bool kCutSharedRailInDeskSleep = true;
#endif
#if !defined(CODEX_STOPWATCH_USB_MIC)
constexpr uint32_t kCompletionChimeSampleRate = 12000;
constexpr uint32_t kCompletionChimeDurationMs = 270;
Expand Down Expand Up @@ -164,13 +160,10 @@ void enterDeskSleep() {
if (speakerSuspended) M5.Speaker.end();
#endif
M5.Display.setBrightness(0);
#if !defined(CODEX_STOPWATCH_USB_MIC)
// The USB microphone build keeps the shared AMOLED/audio rail powered and
// uses brightness-only desk sleep. The CO5300 Sleep Out path through the
// framebuffer wrapper is not reliable enough on C152 to use while docked.
M5.Display.sleep();
M5.Display.waitDisplay();
#endif
if (kSleepPanelControllerInDeskSleep) {
M5.Display.sleep();
M5.Display.waitDisplay();
}
if (kCutSharedRailInDeskSleep) {
// The repository-owned M5GFX patch forwards sleep to the physical AMOLED
// and allows a repeat controller init while preserving its framebuffer.
Expand Down Expand Up @@ -201,11 +194,9 @@ void wakeDeskSleep() {
}
displayRailOff = false;
}
#if !defined(CODEX_STOPWATCH_USB_MIC)
else {
else if (kSleepPanelControllerInDeskSleep) {
M5.Display.wakeup();
}
#endif
M5.Display.setRotation(0);
M5.Display.setTextWrap(false);
appliedBrightness = kActiveBrightness;
Expand Down