-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enable WiFi/BLE automatic light sleep mode on ESP32 #9463
Comments
I don't think we need another API. Seems like the existing light sleep API should work. Is this ESP32 only or does it apply to more of the ESP line? |
As far as I know, every popular Espressif chip has an equivalent, I don't think I've ever seen or used one without it. Adding it to the existing API seems reasonable, but I'm not sure what it would look like, since there are several similar modes, and the WiFi and CPU have separate sleep settings, and the power consumption is more than full light sleep without automatic wakeups for the WiFi hardware. |
When I was working on ESP32-nn sleep, I gave up on using light sleep because of the first paragraph below:
But the second paragraph says you can... I don't remember exactly why I gave up. Perhaps it was because I could not maintain the state of certain other peripherals, and our intention was that a light sleep did not break anything else. |
I had it mostly working at one point in Micropython, but never made a PR because nobody replied to the thread and eventually someone else made one. But now with the C6 and 802.11ax, there's also target wake time, which I believe they say can last for years on a battery, so the APIs should probably support that. And it seems there's also the ability to sleep for more than one DTIM interval for a similar effect on older WiFi, but it can break broadcast traffic. Seems like everyone else is using an enum to let you set whether to care about broadcast traffic or not, and we could add an extra wake_interval param for TWT capable boards? So the API would be: # For WiFi 5 and earlier, sleep 3 beacons
radio.beacon_sleep=3
# For WiFi 6, sleep 0.3 seconds
radio.wake_interval=0.3
# Idle using automatic light sleep,
# waking as needed for WiFi, until an alarm
# wakes us. Can't just use time.sleep as we want
# to be able to handle pin interrupts
idle_until_alarm() |
Could you translate a wake_interval to beacon count? That way you only need one more setting. There is an existing light_sleep API: We'd be happy to merge this in if you get it working. |
I was thinking of that, but I'm not sure how you would get the beacon
interval, I'm not seeing any APIs for that in the ESP-IDF, maybe I haven't
looked hard enough but I suspect it's not even sent by the AP.
You could assume it's 100ms, or measure it? But the value of 1 beacon is
special regardless of the interval because of broadcasting, some users will
likely want to do DTIM1 regardless of time, so we'd want the ability to
explicitly set DTIM1, right?
…On Mon, Jul 29, 2024, 12:45 PM Scott Shawcroft ***@***.***> wrote:
Could you translate a wake_interval to beacon count? That way you only
need one more setting.
There is an existing light_sleep API: alarm.light_sleep_until_alarms()
<https://docs.circuitpython.org/en/latest/shared-bindings/alarm/index.html#alarm.light_sleep_until_alarms>
We'd be happy to merge this in if you get it working.
—
Reply to this email directly, view it on GitHub
<#9463 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAFZCHZJCOUMKUVVX7BITFLZO2EUHAVCNFSM6AAAAABLPXY7TCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDENJWGY2TOMJRHA>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
Start with the bare minimum. We can always add things that folks want. So, I'd suggest getting the light sleep working under the existing API first. |
I think I might have some free time so I was planning to work on this in a bit. If I understand correctly, the idea for now is: Have the existing Add a radio.listen_interval property that enables WiFi power saving, matching the API that the IDF exposes, letting you set it in terms of number of DTIM intervals, since the most common bare minimum is just going to be setting it to 1 Target Wake Time gets ignored for now, and can be figured out later. |
Sounds good @EternityForest! Let us know if you need any guidance about where code lives. #circuitpython-dev on Discord is a good place for realtime help. |
Right now the only thing that seems to be confusing is this file: https://github.com/adafruit/circuitpython/blob/2f626121867efa429b7484fa3ca5a315021cc506/ports/espressif/common-hal/alarm/__init__.c It uses port_idle_until_interrupt(), which seems to call xTaskNotifyWait, but I don't see how that enters actual light sleep, unless CONFIG_FREERTOS_USE_TICKLESS_IDLE is set somewhere outside the repo. Does light sleep until alarms actually sleep? The comment Then, in the documentation, they seem to say things like audio playback are supposed to keep going in light sleep, but the ESP's implementation of light sleep seems to pause things like the UART(You can receive but the send buffer just waits). Is it sleeping somewhere I'm not seeing, or did they at some point decide to not bother with that to keep the strong guarantees of their sleep API? Maybe I need to dig out my USB power meter for this and see what it's actually doing! Edit: I am not actually seeing any power savings in sleep mode. It just hovers around 80mA with 160mA spikes, that's with the screen on powered from a USB wall charger, 5 seconds of time.sleep(), 5 seconds of light sleep until alarms. Unless I'm missing something, it seems like significant power savings would be a somewhat breaking change or require a new parameter. ESP does have a wake locks feature and the set of peripherals that stays on is pretty vaguely specified though, so could we change the API of light sleep to just accept that some peripherals might not work unless they use the locks? Or, could we add a new full_sleep_until_alarms() that turns off peripherals if needed? This seems like a bigger project that it looked like even though the actual implementation is simple, just because of the API guarantees! |
I think it idles but it hasn't been optimized. We focused on deep sleep when we were working on this. I don't remember if I tried tickless idle. It was my intention to rely on freertos idle for light sleep.
Ya, we focused on deep sleep. To optimize light sleep you could track what user code is doing and either freertos idle or actually call the IDF's light sleep.
No, light sleep should keep everything running. However, if nothing is running, then it should sleep as deep as possible. The main distinction between deep sleep vs light sleep is that light keeps ram and cpu state so you pick up where you left off. Deep sleep turns everything it can off and restarts on wake.
No, let's not add another API. That would just add another thing to implement. Instead, make light sleep smarter.
Yup! Most optimization tasks are a rabbit hole of improvements. Any improvements you can do are welcome. |
Thank you all for all the support on this project! You guys really seem to
care about making CircuitPython the best it can be!
I have basic WiFi power saving working in the draft PR, and I'm working on
tickless idle at the moment.
I'm getting down to 20mA while remaining ping-able, and some of that may be
the USB serial chip.
My current approach is:
* Enable tickless idle only during light sleep until alarms
* Disable the tick timer during that period
* Set everything back to the way it was at the end to minimize the chance
of interfering with something.
I'm not sure how to handle the supervisor serial console through, and other
similar background tasks.
There seems to be no way at all to receive anything on a UART in sleep,
just to wake on transitions and miss the first character, so any usable
sleep mode will be a breaking change to some degree as far as I can tell.
What about waiting a few minutes after boot for a serial connection before
enabling sleep, and just accepting any new connections after that will lose
the first char and give some kind of error, unless you have native USB?
I don't see a lot of other options short of software bitbang reception on
the first char, or adding a way to manually disable the supervisor repl
from user code.
…On Wed, Jul 31, 2024, 12:29 PM Scott Shawcroft ***@***.***> wrote:
Right now the only thing that seems to be confusing is this file:
https://github.com/adafruit/circuitpython/blob/2f626121867efa429b7484fa3ca5a315021cc506/ports/espressif/common-hal/alarm/__init__.c
It uses port_idle_until_interrupt(), which seems to call xTaskNotifyWait,
but I don't see how that enters actual light sleep, unless
CONFIG_FREERTOS_USE_TICKLESS_IDLE is set somewhere outside the repo.
Does light sleep until alarms actually sleep? The comment // We cannot
esp_light_sleep_start() here because it shuts down all non-RTC peripherals.
seems to suggest it doesn't and can't sleep without being a breaking API
change?
I think it idles but it hasn't been optimized. We focused on deep sleep
when we were working on this.
I don't remember if I tried tickless idle. It was my intention to rely on
freertos idle for light sleep.
Then, in the documentation, they seem to say things like audio playback
are supposed to keep going in light sleep, but the ESP's implementation of
light sleep seems to pause things like the UART(You can receive but the
send buffer just waits).
Is it sleeping somewhere I'm not seeing, or did they at some point decide
to not bother with that to keep the strong guarantees of their sleep API?
Ya, we focused on deep sleep. To optimize light sleep you could track what
user code is doing and either freertos idle or actually call the IDF's
light sleep.
Unless I'm missing something, it seems like significant power savings
would be a somewhat breaking change or require a new parameter.
ESP does have a wake locks feature and the set of peripherals that stays
on is pretty vaguely specified though, so could we change the API of light
sleep to just accept that some peripherals might not work unless they use
the locks?
No, light sleep should keep everything running. However, if nothing is
running, then it should sleep as deep as possible. The main distinction
between deep sleep vs light sleep is that light keeps ram and cpu state so
you pick up where you left off. Deep sleep turns everything it can off and
restarts on wake.
Or, could we add a new full_sleep_until_alarms() that turns off
peripherals if needed?
No, let's not add another API. That would just add another thing to
implement. Instead, make light sleep smarter.
This seems like a bigger project that it looked like even though the
actual implementation is simple, just because of the API guarantees!
Yup! Most optimization tasks are a rabbit hole of improvements. Any
improvements you can do are welcome.
—
Reply to this email directly, view it on GitHub
<#9463 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAFZCH4U5MZJQJM4MBEVIVTZPEUJNAVCNFSM6AAAAABLPXY7TCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDENRRGE2TMOBYGU>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
More updates: My very early progress on using auto light sleep is here. Sleeping for too long will trigger the watchdog and there's some peripherals that may break, but it's a start. Considering the USB chip likely use 18mA or so, 20mA power consumption is pretty good! https://github.com/EternityForest/circuitpython/tree/auto-light-sleep |
You don't need to wait too long I think. I think the first character is known to be unreliable. I think it is common to do multiple ctrl-c.
Can you run CP background tasks after every time the chip wakes for wifi work? |
I haven't seen a lot of unreliability with the first char myself but it does seem like the sort of thing applications and users should be able to handle. We could just limit the sleep time to 1s or 100ms at a time(I think usual WiFi sleep is 100ms but will be longer when target wake time is common), regardless of what WiFi is up to, and run a CP/supervisor tick in between each sleep? |
I'd only do it if the tick timer is enabled. Will light sleep be woken by interrupts? Much of what background tasks are used for is queued by an interrupt. |
The tick timer doesn't seem to be compatible with sleep at all, since the IDF automatic light sleep doesn't trigger when there's stuff scheduled every millisecond, right now I'm just disabling it when entering sleep and re-enabling it on exit. Does that conflict with some other process that might be enabling or disabling ticks? Interrupts work in light sleep unless the peripheral is just disabled entirely like most of them are, but I'm not sure if/how you can detect the automatic background wakeups in general, so the main thread is only going to get notified by stuff that specifically calls the port wake main thread function. What about the keypad and the other activities that happen in the polled supervisor tick? Could we make it so disabling ticks still keeps the supervisor awake at 5Hz or so? |
Do you know what is enabling ticks? In theory it should only be on if used.
I think its safe to assume we should wake the main thread when needed. Doing so is required.
When using keypad, we shouldn't light sleep. We can sleep less deep because we need the tick. |
Looks like the ticks getting enabled was probably just some other unrelated code that I didn't notice because I thought they were supposed to be always-on. I think this might be closer to being ready than I thought! It seems like a lot of ESP32 boards don't have the alarm module at all though, I wonder how many actually have space for it? |
Is that due to the 4MB flash limit? I think we plan on changing the partition layout with CP 10 so that those boards can have more features like BLE. |
I do remember seeing some scattered discussion somewhere about getting rid
of the OTA partition
…On Thu, Aug 8, 2024, 3:04 PM Scott Shawcroft ***@***.***> wrote:
It seems like a lot of ESP32 boards don't have the alarm module at all
though, I wonder how many actually have space for it?
Is that due to the 4MB flash limit? I think we plan on changing the
partition layout with CP 10 so that those boards can have more features
like BLE.
—
Reply to this email directly, view it on GitHub
<#9463 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAFZCHY64E47OZG5DZB42J3ZQPMPBAVCNFSM6AAAAABLPXY7TCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDENZWGY2DSMJWGU>
.
You are receiving this because you were mentioned.Message ID:
***@***.***>
|
The ESP32 can do sub-milliamp current while remaining connected, but I don't see anything about enabling automatic light sleep for WiFi.
Since there's already alarms, would it be possible to add some way to idle in the ESP's automatic light sleep mode rather than sleeping the whole time and messing up WiFi?
The text was updated successfully, but these errors were encountered: