-
-
Notifications
You must be signed in to change notification settings - Fork 8.2k
ESP32 enhancement: support automatic light-sleep / dynamic freq scaling #5459
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
Comments
Update: I had not fully understood how much the auto-light-sleep mode affects gpio pins as well as uarts. My sense is that this makes its use very questionable in a general-purpose firmware such as micropython. Clever coding and getting some settings just right can probably work around many issues, but in the end it's also likely to create a never ending stream of issues and unmaintainable tricky code. I looked into something related, which is the dynamic frequency scaling, e.g. setting a 10Mhz lower clock frequency. That seems like a more productive direction because, with some caveats, most peripherals continue to work properly. The main caveat is that the clocks need to be chosen carefully so they're not affected by CPU freq scaling. Another related option may be to run single-core and turn one CPU off altogether. My plan is:
One open issue I don't quite know how to resolve is the FreeRTOS tickless-idle: I don't know whether there is a downside to enabling it in general. I believe there isn't, but I don't know enough about its side-effects. |
Proposal for implementing what I've figured out so far: #5473 |
…n-main Translations update from Weblate
Just a note on FreeRTOS tickless idle. I've been using it for a few years in an nRF52832 project, and at several points have had to delve into the relevant code a bit to explore while troubleshooting some issues (which turned out not to be directly related). Possibly a few comments will help others understand it better, assuming the implementation on nRF is similar on your own system. Roughly speaking, tickless idle consists of a mechanism whereby while the code is active (i.e. not attempting to go idle) the RTC is allowed to generate interrupts every tick, which increments the system tick counter and which processes any completed time-based events. When the system wants to go idle, because there's nothing known to be active except some delays that are far enough in the future to warrant it, the per-tick RTC interrupt is shut off, and a comparator interrupt is turned on instead for the future time, and a sleep instruction is issued ( When the system wakes up, either because the RTC has overflowed or hit the comparator value, or because some other unexpected event occurred (e.g. other peripheral interrupt), the code figures out how long it was actually asleep and fixes up the tick counter, and then things continue normally. So basically, it's capable of turning off the tick interrupt when it wants to idle sleep ("tickless idle"), and making suitable adjustments when it comes out of idle sleep again, and that's about it. |
gpio_hold_en can apparently maintain GPIO state, right? What if you had a new user function to manually enter auto light sleep, with the understanding that you won't actually be able to do much. From there you could sit in an idle loop waiting for commands, and manually exit when you get one. |
Update: I've got auto light sleep working perfectly well under manual control, snd it saves a ton of power. I've patched the time.sleep() function to calculate the longest possible block of time, so that auto light sleep can kick in, and I'm working on a function cancel_sleep() to call from an interrupt to stop a time.sleep(). I don't have that fully working, but even just being able to sleep for a fixed interval without losing the WiFi connection is a really big deal, I'm still saving a lot of power just by waking up once a second to see if the user is holding the wake button. Coming from Arduino, there's a vast number of sketches based on the "Sleep for a while, do something, sleep some more" pattern, and I think an auto_sleep(val) covers a lot of the most common use cases. https://github.com/EternityForest/micropython/tree/dev-branch |
@EternityForest were you able to get further on your auto_sleep functionality? |
@jonnor I haven't had time to work on this one in a long time, so pretty much everything is just in the(Very far out of sync) repo. If there's actually interest in merging something like this to upstream I could possibly work on a PR, but it seemed like everyone was too busy to think much about it, so I didn't go any further. |
Thanks for the quick response @EternityForest ! I am not a maintainer, so I cannot speak for them. But I found your suggested approach with a manual function to enter/leave the mode to be a pragmatic way forward. I just today wanted to build a low-power Bluetooth, and the current state of MicroPython is quite limiting. Being able to access the ESP32 autosleep functionality would seemingly make a large difference, and manually calling machine.autosleep() seems like it would work great in my case. So I would at least be willing to test and give feedback on a PR :) |
Uh oh!
There was an error while loading. Please reload this page.
On the esp32 automatic light sleep / dynamic frequency scaling / power management puts the CPU to sleep when all tasks are idle thereby reducing the power consumption to about 1mA instead of around 40mA @ 160Mhz. The special feature of auto light sleep is that it can maintain a Wifi association by waking up at the appropriate times to receive AP beacons and respond to them. This ticket proposes to discuss how to enable auto light-sleep.
The first issue is that
FREERTOS_USE_TICKLESS_IDLE
needs to be enabled in sdkconfig.h and thatmachine.freq()
must setpm.light_sleep_enable = true
.The next issue is that
mp_hal_stdin_rx_chr
andmp_hal_delay_ms
inmphalport.h
do not callvTaskDelay
and instead callulTaskNotifyTake
which does not seem to enable auto light sleep (need to dig into this). Specifically, in the idle loop without wifi enabledulTaskNotifyTake
almost always returns after 100ms, which seems to be too little for light sleep to kick in.The use of
vTaskDelay
got switched toulTaskNotifyTake
as part of 14ab81e because it affects interrupt latency too much, see #3894.Adding a call to
vTaskDelay(4)
in there for test purposes results in the expected power savings (a value of 4 is the minimum for light sleep to kick in). However, it messes up gpio: they all(?) go low, which is unexpected (I expected ESP-IDF to handle this appropriately). The gpio issue also affects the uart: the console no longer works unless one manages to get a char in while mp is polling. Mysteries...Update: I had not understood the second parameter of
ulTaskNotifyTake
: setting that to 4 also causes light sleep to kick in.The text was updated successfully, but these errors were encountered: