Conversation
Runtime CPU clock switching plus a refcounted PerfLock guard (esp-rs#5634). set_cpu_clock(CpuClock) switches via ClockConfig::configure, which applies only the diff, so moving between two presets that share a PLL is divider-only with no recalibration. PerfLevel { Base, High } and set_perf_levels(base, high) set the two levels; PerfLock::request(High) runs the CPU at High until dropped and back to Base on the last release. State sits behind esp_sync::NonReentrantMutex. APB is untouched: every CpuClock is PLL-sourced and APB is derived independently of the CPU divider. Opt-in. Builds on esp32, esp32s3, esp32c3, esp32c6, esp32h2.
kokroo
requested review from
JurajSadel,
MabezDev,
SergioGasquez,
bjoernQ,
bugadani and
playfulFence
as code owners
September 9, 2026 11:44
bugadani
marked this pull request as draft
September 9, 2026 12:20
Enables btdm modem sleep (sleep_mode MODE_1) on esp32c3 and esp32s3: implements the btdm_sleep_* OS callbacks, adds a selectable sleep clock (BleSleepClock), turns sleep on at runtime with btdm_controller_enable_sleep, and wakes a sleeping controller before each host->controller HCI send (else the send hangs). Also lets esp-rtos keep the main XTAL powered across light sleep, for boards without a 32 kHz crystal. Ported from ESP-IDF bt.c. HW-validated on ESP32-S3: modem sleep engages (check_duration fires, ~66 sleep cycles/s under a connection) and the BLE link stays up. Off by default.
kokroo
force-pushed
the
helius-esp-power
branch
from
September 9, 2026 14:25
c81b0be to
b8f21d6
Compare
|
New commits in main have made this PR unmergeable. Please resolve the conflicts. |
…bing Light-sleep power work for the BLE (btdm) + esp-rtos path on ESP32-S3. - rtc_cntl: WakeLock gains set/clear/sleep_deadline + holders(); a driver that releases its lock for a bounded gap can now bound how long the idle hook sleeps. - sleep: enable/disable_bt_wakeup() exposes the Bt light-sleep wake source (c3/s3). - uart: UartRx drops its lifetime wake lock once enable_wakeup arms the RX line as a wake source, so an always-armed serial no longer blocks light sleep. - esp-rtos idle hook: honor the sleep deadline; add gate/residency diag counters. - btdm: count active connections from the HCI event stream and arm the pre-event wake deadline + Bt wake source ONLY while connected. Idle advertising skips them and light-sleeps the whole gap (advertising CPU light-sleep ~54% -> ~66%), while a held connection keeps the SoC awake for its events (link survives). Deadline is set in enter_phase2 paired with the wakelock release so exit_phase3 always clears it. Phase callbacks gated to c3/s3 so esp32-classic still builds. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Diagnostic for light-sleep power work: sleep_hist() returns 8 ms-bucketed counts of the requested sleep length at each committed light sleep, so a bench can see the wake cadence (what limits sleep length). Cheap relaxed counters in the hook. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Collaborator
For the sake of being able to revert broken PRs and also ease review burden, splitting this into two independent PRs makes sense to me. |
Contributor
|
This will most likely not be merged as is, I'll be cherry-picking the bits when I get to this topic. |
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.
Submission Checklist 📝
cargo xtask fmtcommand to ensure that all changed code is formatted correctly.skip-changelogormanual-changeloglabel as appropriate.Extra:
Pull Request Details 📖
Description
All of this was possible thanks to peeking into the ESP-IDF source.
Two power saving additions related to each other.
The first one is the "dynamic" frequency scaling from #5634.
set_cpu_clockswitches the CPU clock at runtime viaClockConfig::configure, which only applies the diff, so moving between two presets that share a PLL is just a divider change with no recalibration.PerfLevelandset_perf_levelsdefine a Base and a High clock, andPerfLock::request(High)is a refcounted guard. The CPU runs at High while at least one is held and returns to Base on the last drop. The state sits behindesp_sync::NonReentrantMutex. Nothing scales the clock unless you ask it to, and APB is never affected since everyCpuClockis PLL sourced and APB is derived independently of the CPU divider.The second enables btdm controller modem sleep (
sleep_modeMODE_1) on esp32c3 and esp32s3. It fills in thebtdm_sleep_*OS callbacks, adds aBleSleepClockconfig to select the low-power clock, and manages the controller wake lock so the SoC can light-sleep between connection events. esp-rtos gets an optin to keep the main XTAL powered across light sleep, which is what boards without a 32.768 kHz crystal need to hold a connection while sleeping. The callbacks follow ESP-IDF'sbt.c. Off by default (don't know what to do, best to keep it off by default I guess).The two commits are independent if you would rather take only the clock change first.
Testing
Tested on a crystal-less ESP32-S3 (Heltec V3). Booted at 240 MHz, settled to a Base of 80 MHz, and ran an 80 -> 240 -> 80 round trip through
PerfLockat runtime while a BLE connection and LoRa traffic stayed up, with no dropped events. The clock module builds for esp32, esp32s3, esp32c3, esp32c6 and esp32h2.The BLE modem sleep is less mature. I have seen lower average current with a connection held by measuring battery drain over hours (so not accurate), but I do not have PPK2 measurements yet, which is why it is off by default.
Changelog
esp-hal
set_cpu_clockto change the CPU clock at runtime, andPerfLevel/PerfLock/set_perf_levelsfor refcounted dynamic frequency scaling.esp-radio
sleep_modeMODE_1) on esp32c3 and esp32s3, with aBleSleepClockoption. Off by default.esp-rtos
set_main_xtal_powered_in_light_sleepto keep the main XTAL powered during automatic light sleep.