feat: more power saving on idle#801
Conversation
Good question, IMO it won't change much because the 50ms is already relatively large compared to the scheduling ticks. Let's take a time slice of 1000ms (equals to 1000 ticks):
So I suspect that the difference between 100 tasks per sec vs 20 tasks is much more noticeable. Also, there is another reason why I think the 50ms is optimal: in terms of UX, human brain can start to notice if the delay is ~100ms, the 50ms tricks the brain into feeling that there is no added delay. This is only true if it's not repeated though, i.e. not noticeable after some seconds not interact with the device. Hence the 10 seconds I added since the last button pressed. However, I think lower the delay to 3-4 seconds won't hurt, will push a commit to update that.
Actually the wait loop will be removed in #774 , and be replaced with freeRTOS direct-to-task notification. It will allow the |
|
The task delays are all being removed in #774 so this main loop delay should be really the only thing skipping CPU cycles, so I don't think there's a need to change it here. e: ahh nvm got beaten to the comment punch |
|
Lowered to 3 seconds, still I barely notice the delay Also just note that the main loop does a lot more than |
|
Had to confirm I was on the branch, as I couldn't even tell there was a difference, given it takes about 100ms to prepare the buffers for a render (depending on the screen) and then ~400ms to render the screen, the additional 40ms isn't really noticeable at all. |
|
I am interested in trying this improvement, since I currently have two devices, one white and one black, I have installed the same firmware version v1.0.0 on both and I do not see consistent battery life. With the devices off, I have charged them to 100% until the light is green. I repeat the process again, leaving it in HOME without interacting until the next shutdown. Does anyone else have several devices where they can verify if the behavior is consistent? |
|
@pablohc I think the most reliable way is to let the device on for a longer period. In my case, I left it idle to 6 hrs straight, but I think leaving it for a longer period, like 24 hrs, can give a better results. FWIW, I borrowed an oscilloscope from Hugging Face robotics lab today, and the guy who lend it to me suggest that I should try measuring the voltage drop. Honestly that's a bit above of my head and I wasn't success at reading any meaningful results at the end. Also one thing I discovered so far while doing reverse engineering on the compiled firmware, seems like esp32's power management is completely disabled from arduino build, and to enable it, there is no good way but to build our own version of arduino (that's a can of worms that I won't dig into) But I think I can experiment with lowering the CPU frequency when idle in a future PR, probably can be more impactful. For now, I would appreciate if someone can test this in a longer period (note that merging this PR as-is is fine for me, not sure if other maintainers are agree) |
daveallie
left a comment
There was a problem hiding this comment.
Happy to review this as-is as it doesn't seem to impact functionality.
|
FYI, I'm experimenting with lowering the CPU frequency on inactivity (from 160MHz down to 10MHz): https://github.com/crosspoint-reader/crosspoint-reader/compare/master...ngxson:crosspoint-reader:xsn/idle_cpu_freq?expand=1 Will let my device idle for a longer period this time, I'm testing with 12 hrs. Will report the result in 2-3 days. |
* master: feat: Allow screenshot retrieval from device (crosspoint-reader#820) feat: more power saving on idle (crosspoint-reader#801)
## Summary This PR extends the delay in main loop from 10ms to 50ms after the device is idle for a while. This translates to extended battery life in a longer period (see testing section above), while not hurting too much the user experience. With the help from [this patch](https://github.com/ngxson/crosspoint-reader/tree/xsn/measure_cpu_usage), I was able to measure the CPU usage on idle: ``` PR: [20017] [MEM] Free: 150188 bytes, Total: 232092 bytes, Min Free: 150092 bytes [20017] [IDLE] Idle time: 99.62% (CPU load: 0.38%) [30042] [MEM] Free: 150188 bytes, Total: 232092 bytes, Min Free: 150092 bytes [30042] [IDLE] Idle time: 99.63% (CPU load: 0.37%) [40067] [MEM] Free: 150188 bytes, Total: 232092 bytes, Min Free: 150092 bytes [40067] [IDLE] Idle time: 99.62% (CPU load: 0.38%) master: [20012] [MEM] Free: 195016 bytes, Total: 231532 bytes, Min Free: 132460 bytes [20012] [IDLE] Idle time: 98.53% (CPU load: 1.47%) [30017] [MEM] Free: 195016 bytes, Total: 231532 bytes, Min Free: 132460 bytes [30017] [IDLE] Idle time: 98.53% (CPU load: 1.47%) [40022] [MEM] Free: 195016 bytes, Total: 231532 bytes, Min Free: 132460 bytes [40022] [IDLE] Idle time: 98.53% (CPU load: 1.47%) ``` While this is a x3.8 reduce in CPU usage, it doesn't translate to the same amount of battery life extension in real life. The reasons are: 1. The CPU is not shut down completely 2. freeRTOS tick is still running (however, I planned to experiment with tickless functionality) 3. Current leakage to other components, for example: voltage dividers, eink screen, SD card, etc A note on [light-sleep](https://docs.espressif.com/projects/esp-idf/en/stable/esp32c3/api-reference/system/sleep_modes.html) functionality: it is not possible in our use case because: - Light-sleep for 50ms introduce too much overhead on wake up, it has negative effect on battery life - Light-sleep for longer period doesn't work because the ADC GPIO buttons cannot be used as wake up source ## Testing (duration = 6 hrs) To test this, I patched the `CrossPointSettings::getSleepTimeoutMs()` to always returns a timeout of 6 hrs. This allow me to leave the device idle for 6 hrs straight. - On master branch, 6 hrs costs 26% battery life (100% --> 74%), meaning battery life is ~23 hrs - With this PR, 6 hrs costs 20% battery life (100% --> 80%), meaning battery life is ~30 hrs So in theory, this extends the battery by about 7 hrs. Even with some error margin added, I think 3 hrs increase is possible with a normal usage setup (i.e. only read ebooks, no wifi) ## Additional Context Would appreciate if someone can test this with an oscilloscope. --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? **NO**
## Summary Continue my experiment from #801 This PR add the ability to lower the CPU frequency on extended idle period (currently set to 3 seconds). By default, the esp32c3 CPU is set to 160MHz, and now on idle, we can reduce it to just 10MHz. Note that while this functionality is already provided by [esp power management](https://docs.espressif.com/projects/esp-idf/en/v4.3/esp32c3/api-reference/system/power_management.html), the current Arduino build lacks of this, and enabling it is just too complicated (not worth the effort compared to this PR) Update: more info in #852 (comment) ## Testing Pre-condition for each test case: the battery is charged to 100%, and is left plugged in after fully charged for an extra 1 hour. The table below shows how much battery is **used** for a given duration: | case / duration | 6 hrs | 12 hrs | | --- | --- | --- | | `delay(10)` | 26% | 48% | | `delay(50)`, PR #801 | 20% | Not tested | | `delay(50)` + low CPU freq (This PR) | Not tested | 25% | | `delay(10)` + low CPU freq (1) | Not tested | Not tested | (1) I decided not to test this case because it may not make sense. The problem is that CPU frequency vs power consumption do not follow a linear relationship, see [this](https://www.arrow.com/en/research-and-events/articles/esp32-power-consumption-can-be-reduced-with-sleep-modes) as an example. So, tight loop (10ms) + lower CPU freq significantly impact battery life, because the active CPU time is now much higher compared to the wall time. **So in conclusion, this PR improves ~150% to ~200% battery use time per charge.** The projected battery life is now: ~36-48 hrs of reading time (normal reading, no wifi) --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? **NO**
) ## Summary Continue my experiment from crosspoint-reader#801 This PR add the ability to lower the CPU frequency on extended idle period (currently set to 3 seconds). By default, the esp32c3 CPU is set to 160MHz, and now on idle, we can reduce it to just 10MHz. Note that while this functionality is already provided by [esp power management](https://docs.espressif.com/projects/esp-idf/en/v4.3/esp32c3/api-reference/system/power_management.html), the current Arduino build lacks of this, and enabling it is just too complicated (not worth the effort compared to this PR) Update: more info in crosspoint-reader#852 (comment) ## Testing Pre-condition for each test case: the battery is charged to 100%, and is left plugged in after fully charged for an extra 1 hour. The table below shows how much battery is **used** for a given duration: | case / duration | 6 hrs | 12 hrs | | --- | --- | --- | | `delay(10)` | 26% | 48% | | `delay(50)`, PR crosspoint-reader#801 | 20% | Not tested | | `delay(50)` + low CPU freq (This PR) | Not tested | 25% | | `delay(10)` + low CPU freq (1) | Not tested | Not tested | (1) I decided not to test this case because it may not make sense. The problem is that CPU frequency vs power consumption do not follow a linear relationship, see [this](https://www.arrow.com/en/research-and-events/articles/esp32-power-consumption-can-be-reduced-with-sleep-modes) as an example. So, tight loop (10ms) + lower CPU freq significantly impact battery life, because the active CPU time is now much higher compared to the wall time. **So in conclusion, this PR improves ~150% to ~200% battery use time per charge.** The projected battery life is now: ~36-48 hrs of reading time (normal reading, no wifi) --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? **NO**
## Summary This PR extends the delay in main loop from 10ms to 50ms after the device is idle for a while. This translates to extended battery life in a longer period (see testing section above), while not hurting too much the user experience. With the help from [this patch](https://github.com/ngxson/crosspoint-reader/tree/xsn/measure_cpu_usage), I was able to measure the CPU usage on idle: ``` PR: [20017] [MEM] Free: 150188 bytes, Total: 232092 bytes, Min Free: 150092 bytes [20017] [IDLE] Idle time: 99.62% (CPU load: 0.38%) [30042] [MEM] Free: 150188 bytes, Total: 232092 bytes, Min Free: 150092 bytes [30042] [IDLE] Idle time: 99.63% (CPU load: 0.37%) [40067] [MEM] Free: 150188 bytes, Total: 232092 bytes, Min Free: 150092 bytes [40067] [IDLE] Idle time: 99.62% (CPU load: 0.38%) master: [20012] [MEM] Free: 195016 bytes, Total: 231532 bytes, Min Free: 132460 bytes [20012] [IDLE] Idle time: 98.53% (CPU load: 1.47%) [30017] [MEM] Free: 195016 bytes, Total: 231532 bytes, Min Free: 132460 bytes [30017] [IDLE] Idle time: 98.53% (CPU load: 1.47%) [40022] [MEM] Free: 195016 bytes, Total: 231532 bytes, Min Free: 132460 bytes [40022] [IDLE] Idle time: 98.53% (CPU load: 1.47%) ``` While this is a x3.8 reduce in CPU usage, it doesn't translate to the same amount of battery life extension in real life. The reasons are: 1. The CPU is not shut down completely 2. freeRTOS tick is still running (however, I planned to experiment with tickless functionality) 3. Current leakage to other components, for example: voltage dividers, eink screen, SD card, etc A note on [light-sleep](https://docs.espressif.com/projects/esp-idf/en/stable/esp32c3/api-reference/system/sleep_modes.html) functionality: it is not possible in our use case because: - Light-sleep for 50ms introduce too much overhead on wake up, it has negative effect on battery life - Light-sleep for longer period doesn't work because the ADC GPIO buttons cannot be used as wake up source ## Testing (duration = 6 hrs) To test this, I patched the `CrossPointSettings::getSleepTimeoutMs()` to always returns a timeout of 6 hrs. This allow me to leave the device idle for 6 hrs straight. - On master branch, 6 hrs costs 26% battery life (100% --> 74%), meaning battery life is ~23 hrs - With this PR, 6 hrs costs 20% battery life (100% --> 80%), meaning battery life is ~30 hrs So in theory, this extends the battery by about 7 hrs. Even with some error margin added, I think 3 hrs increase is possible with a normal usage setup (i.e. only read ebooks, no wifi) ## Additional Context Would appreciate if someone can test this with an oscilloscope. --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? **NO**
) ## Summary Continue my experiment from crosspoint-reader#801 This PR add the ability to lower the CPU frequency on extended idle period (currently set to 3 seconds). By default, the esp32c3 CPU is set to 160MHz, and now on idle, we can reduce it to just 10MHz. Note that while this functionality is already provided by [esp power management](https://docs.espressif.com/projects/esp-idf/en/v4.3/esp32c3/api-reference/system/power_management.html), the current Arduino build lacks of this, and enabling it is just too complicated (not worth the effort compared to this PR) Update: more info in crosspoint-reader#852 (comment) ## Testing Pre-condition for each test case: the battery is charged to 100%, and is left plugged in after fully charged for an extra 1 hour. The table below shows how much battery is **used** for a given duration: | case / duration | 6 hrs | 12 hrs | | --- | --- | --- | | `delay(10)` | 26% | 48% | | `delay(50)`, PR crosspoint-reader#801 | 20% | Not tested | | `delay(50)` + low CPU freq (This PR) | Not tested | 25% | | `delay(10)` + low CPU freq (1) | Not tested | Not tested | (1) I decided not to test this case because it may not make sense. The problem is that CPU frequency vs power consumption do not follow a linear relationship, see [this](https://www.arrow.com/en/research-and-events/articles/esp32-power-consumption-can-be-reduced-with-sleep-modes) as an example. So, tight loop (10ms) + lower CPU freq significantly impact battery life, because the active CPU time is now much higher compared to the wall time. **So in conclusion, this PR improves ~150% to ~200% battery use time per charge.** The projected battery life is now: ~36-48 hrs of reading time (normal reading, no wifi) --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? **NO**
) ## Summary Continue my experiment from crosspoint-reader#801 This PR add the ability to lower the CPU frequency on extended idle period (currently set to 3 seconds). By default, the esp32c3 CPU is set to 160MHz, and now on idle, we can reduce it to just 10MHz. Note that while this functionality is already provided by [esp power management](https://docs.espressif.com/projects/esp-idf/en/v4.3/esp32c3/api-reference/system/power_management.html), the current Arduino build lacks of this, and enabling it is just too complicated (not worth the effort compared to this PR) Update: more info in crosspoint-reader#852 (comment) ## Testing Pre-condition for each test case: the battery is charged to 100%, and is left plugged in after fully charged for an extra 1 hour. The table below shows how much battery is **used** for a given duration: | case / duration | 6 hrs | 12 hrs | | --- | --- | --- | | `delay(10)` | 26% | 48% | | `delay(50)`, PR crosspoint-reader#801 | 20% | Not tested | | `delay(50)` + low CPU freq (This PR) | Not tested | 25% | | `delay(10)` + low CPU freq (1) | Not tested | Not tested | (1) I decided not to test this case because it may not make sense. The problem is that CPU frequency vs power consumption do not follow a linear relationship, see [this](https://www.arrow.com/en/research-and-events/articles/esp32-power-consumption-can-be-reduced-with-sleep-modes) as an example. So, tight loop (10ms) + lower CPU freq significantly impact battery life, because the active CPU time is now much higher compared to the wall time. **So in conclusion, this PR improves ~150% to ~200% battery use time per charge.** The projected battery life is now: ~36-48 hrs of reading time (normal reading, no wifi) --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? **NO**
) ## Summary Continue my experiment from crosspoint-reader#801 This PR add the ability to lower the CPU frequency on extended idle period (currently set to 3 seconds). By default, the esp32c3 CPU is set to 160MHz, and now on idle, we can reduce it to just 10MHz. Note that while this functionality is already provided by [esp power management](https://docs.espressif.com/projects/esp-idf/en/v4.3/esp32c3/api-reference/system/power_management.html), the current Arduino build lacks of this, and enabling it is just too complicated (not worth the effort compared to this PR) Update: more info in crosspoint-reader#852 (comment) ## Testing Pre-condition for each test case: the battery is charged to 100%, and is left plugged in after fully charged for an extra 1 hour. The table below shows how much battery is **used** for a given duration: | case / duration | 6 hrs | 12 hrs | | --- | --- | --- | | `delay(10)` | 26% | 48% | | `delay(50)`, PR crosspoint-reader#801 | 20% | Not tested | | `delay(50)` + low CPU freq (This PR) | Not tested | 25% | | `delay(10)` + low CPU freq (1) | Not tested | Not tested | (1) I decided not to test this case because it may not make sense. The problem is that CPU frequency vs power consumption do not follow a linear relationship, see [this](https://www.arrow.com/en/research-and-events/articles/esp32-power-consumption-can-be-reduced-with-sleep-modes) as an example. So, tight loop (10ms) + lower CPU freq significantly impact battery life, because the active CPU time is now much higher compared to the wall time. **So in conclusion, this PR improves ~150% to ~200% battery use time per charge.** The projected battery life is now: ~36-48 hrs of reading time (normal reading, no wifi) --- ### AI Usage While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it helps set the right context for reviewers. Did you use AI tools to help write this code? **NO**
Summary
This PR extends the delay in main loop from 10ms to 50ms after the device is idle for a while. This translates to extended battery life in a longer period (see testing section above), while not hurting too much the user experience.
With the help from this patch, I was able to measure the CPU usage on idle:
While this is a x3.8 reduce in CPU usage, it doesn't translate to the same amount of battery life extension in real life. The reasons are:
A note on light-sleep functionality: it is not possible in our use case because:
Testing (duration = 6 hrs)
To test this, I patched the
CrossPointSettings::getSleepTimeoutMs()to always returns a timeout of 6 hrs. This allow me to leave the device idle for 6 hrs straight.So in theory, this extends the battery by about 7 hrs. Even with some error margin added, I think 3 hrs increase is possible with a normal usage setup (i.e. only read ebooks, no wifi)
Additional Context
Would appreciate if someone can test this with an oscilloscope.
AI Usage
While CrossPoint doesn't have restrictions on AI tools in contributing, please be transparent about their usage as it
helps set the right context for reviewers.
Did you use AI tools to help write this code? NO