perf(power): light-sleep power management — BLE-wakeable sleep + 240MHz-when-live (AI FYI) - #121
Open
johnbuckman wants to merge 3 commits into
Open
perf(power): light-sleep power management — BLE-wakeable sleep + 240MHz-when-live (AI FYI)#121johnbuckman wants to merge 3 commits into
johnbuckman wants to merge 3 commits into
Conversation
Enable ESP-IDF automatic light sleep (CONFIG_PM_ENABLE + tickless idle + esp_pm_configure light_sleep=on), with the NimBLE controller's low-power clock on RTC_SLOW (internal RC) since this board has no external 32kHz crystal. New opt-in from-source env [env:esp32s3_pm] carries the sdkconfig. Config flags alone do nothing here: the main loop never blocks, so the FreeRTOS idle task never runs and tickless idle never enters. The fix is a blocking yield at the end of loop(); with it, idle drops 110.6 -> 39.7 mA (-64%) on a V8.1 unit. All runtime bits are guarded by CONFIG_PM_ENABLE, so the change is inert on the stock precompiled build. Serial is hardened for light sleep: UART0 wake-on-rx, plus a stay-awake grace window after serial activity (t_lastSerialActivity) so a command's bytes aren't lost to a mid-command sleep. Functional serial regression is consistently 8/8 with idle power unchanged. AI-written change, posted as an FYI for review; not necessarily to merge. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Make the soft-sleep state (BLE "sleep" cmd 03 0A 04 01) genuinely low-power: in soft-sleep the main loop yields 50ms instead of 10ms, so the SoC light- sleeps in longer chunks between BLE connection events while staying reachable. Builds on the automatic light sleep from the Tier-2 change; deep sleep is unchanged (double-tap and BLE "off" 03 0A 02 still fully power down the radio). Verified over real BLE (macOS central): connected BLE-wakeable soft-sleep drops from 103.8 mA (stock) to 31.4 mA (-70%, 3.3x), wake stays 5/5 at ~105ms with a stable connection. Guarded by CONFIG_PM_ENABLE; inert on the stock build. AI-written change, posted as an FYI for review; not necessarily to merge. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
On the DFS/light-sleep build, hold an ESP_PM_CPU_FREQ_MAX lock while (deviceConnected && !b_softSleep) so on-scale extensions (the upcoming extension mechanism) get full compute during active connected use; release it otherwise so DFS scales down and light sleep engages. Measured over real BLE: connected+live 64.4mA (240 pinned) vs connected+soft-sleep 31.4mA (released -> light sleep, unchanged), wake still 5/5. Guarded by CONFIG_PM_ENABLE; inert on the stock build. AI-written change, posted as an FYI for review; not necessarily to merge. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This was referenced Aug 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Builds on the automatic light sleep in #119 (its two commits are included here) and turns it into a complete, demand-matched power profile. Three commits:
Deep sleep is untouched: double-tap-O and the BLE "off" command (
03 0A 02) still fully power down the radio (µA, button/charger wake only).The resulting power profile (all measured over real BLE)
03 0A 04 01)1–2. BLE-wakeable low-power sleep
Makes the "sleep" state (
03 0A 04 01— scale sleeps but stays connected so the app can wake it) genuinely low-power. Stock "sleep" only turns off the OLED + peripheral rails; the CPU stays at 240 MHz and BLE never sleeps. This lets the SoC light-sleep between connection events.Apples-to-apples, connected + BLE-wakeable sleep: stock 103.8 mA → this 31.4 mA = −70% (3.3×), with wake unchanged (5/5 at ~105 ms, stable connection, 0 disconnects).
3. 240 MHz pin when connected + live
For the upcoming on-scale extension mechanism: hold an
ESP_PM_CPU_FREQ_MAXlock while(deviceConnected && !b_softSleep)so extensions get full compute during active connected use; release it otherwise so DFS scales down and light sleep engages. Measured: connected + live 64.4 mA (240 pinned) vs connected + soft-sleep 31.4 mA (released → light-sleep, unchanged — the sleep savings are fully preserved), wake still 5/5. Using a PM lock rather than a rawsetCpuFrequencyMhzis deliberate — the latter fights DFS.How it was verified
Real BLE central (macOS via tcl-ble-osx): connect, stream weight (~10 Hz), 5 sleep→wake cycles — notifications stop in sleep and resume on wake, wake latency a consistent ~105 ms, 0 disconnects / 0 write errors. This also settled the standing risk that the light-sleep firmware (BLE on the internal RC clock — this board has no 32 kHz crystal) might not hold a BLE connection. It does — rock stable. All runtime code is
#if CONFIG_PM_ENABLE-guarded, so the stock precompiled build is unchanged. Build:pio run -e esp32s3_pm -t upload.What I tried that did NOT help (and reverted)
Requesting a slow BLE connection interval / high slave latency on soft-sleep entry (
ble_gap_update_params) to cut radio wakeups further. Measured: no power change (wake latency stayed 105 ms → macOS as central rejected the request; CoreBluetooth is documented to clamp/ignore peripheral connection-parameter requests) and it broke wake reliability (1/5 + a disconnect from the per-cycle param toggling). Reverted. It may help with a phone central that honors conn-param requests, but that can't be validated on this bench.Known gaps / not verified
b_requireHeartBeat) and can no longer be BLE-woken. Left as-is deliberately (soft-sleep is typically USB-plugged, where the auto-off path is gated off anyway).CONFIG_PM_*flags are compile-time); the[env:esp32s3_pm]env carries them.