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
Idle core0 on esp32 platform #2021
Conversation
I also tested the Lua to make sure adding a delay didn't have some weird effect on it. Code for profiling: diff --git a/src/lua/elrsV3.lua b/src/lua/elrsV3.lua
index 649a3e04..343b461f 100644
--- a/src/lua/elrsV3.lua
+++ b/src/lua/elrsV3.lua
@@ -571,11 +571,38 @@ local function refreshNext()
linkstatTimeout = time + 100
elseif time > fieldTimeout and fields_count ~= 0 then
if #loadQ > 0 then
+ -- init stats
+ if lstats == nil then
+ lstats = {}
+ lstats.start = time
+ lstats.cnt = 0
+ lstats.tot = 0
+ lstats.currstart = 0
+ lstats.timeout = 0
+ end
+
+ -- collect stats
+ if fieldTimeout ~= 0 then -- timeout or reload field
+ lstats.timeout = lstats.timeout + 1
+ elseif lstats.currstart ~= 0 then
+ local currtime = time - lstats.currstart
+ lstats.cnt = lstats.cnt + 1
+ lstats.tot = lstats.tot + currtime
+ end
+
+ lstats.currstart = time
crossfireTelemetryPush(0x2C, { deviceId, handsetId, loadQ[#loadQ], fieldChunk })
fieldTimeout = time + 50 -- 0.5s
end
end
+ -- report stats
+ if #loadQ == 0 and lstats ~= nil then
+ print(string.format("LoadStats wall=%d cnt=%d tot=%d (%.1fea) to=%d",
+ time - lstats.start, lstats.cnt, lstats.tot, lstats.tot / lstats.cnt, lstats.timeout))
+ lstats = nil
+ end
+
if time > titleShowWarnTimeout then
-- if elrsFlags bit set is bit higher than bit 0 and bit 1, it is warning flags
titleShowWarn = (elrsFlags > 3 and not titleShowWarn) or nil The value here I was checking was
|
6d98287
to
4a93b8d
Compare
Glad to see someone doing this. My QX7 eats batteries fast since changing over from R9M. Now have a Happymodel es900tx |
nonchalantly clicks the v3.3 tag |
Will this potentially slow down / delay things that need to run in |
No, core 1 is the loop core which runs all the essential devices and core 0 is used for all the ancillary devices. |
So there are no impacts at all to any code that is in |
Correct, loop is not affected by the PR. loop runs full steam ahead all the time. |
Adds a throttle to the main
loop()
code on ESP32 platform (both TX and RX) to allow core0 to idle and reduce power consumption by roughly 20mA at the 3.3V rail. Did anyone miss mywordycomprehensive PRs?Details
ExpressLRS runs all cores on all platforms balls-to-the-wall (except when in wifi mode). This prevents the ESP32 from enabling any power save functionality despite the fact that we don't need to run all our tasks at 100000Hz. Each core's power can be reduced by up to 25mA (50mA total) by simply allowing it to idle. This PR reduces the update frequency of the core0 tasks down to a target of either 500Hz or 1000Hz depending on the packet rate, and makes use of the FreeRTOS function
xTaskDelayUntil()
to idle the remainder of loop duration. Compare this to simply callingdelay()
every loop, which will always delay and possibly greatly reduce the loop frequency under periods of high load-- delayUntil will not delay if the scheduler is already behind.I've been experimenting with various power save methods for half the year and this seems to meet my requirements for enough savings with what appears to be absolutely no discernable impact to operation. I've tried downclocking, periodic downclocking, straight delays, single-core task aggregation, undervolting, and even a more complicated timeslice scheduler but all of those had disadvantages. I can't find any subsystem that is degraded by the chosen solution.
A 1000Hz loop rate achieves over 90% of the power savings of a 500Hz loop rate, and slower rates didn't appear to show any more power decrease (tested down to ~91Hz / 11ms delay). Therefore, I chose to use a 1000Hz loop for all 1000Hz modes (F1000, D500, D250) and a 500Hz loop for all other packet rates.
Results
Every ESP32 target I tested showed measurable reduction in power usage. In receivers, there is approximately 20-23mA current reduction (250Hz 10mW) that lowers the load on the LDO, reducing heat generation, and the ESP32 chip itself also runs cooler. The transmitter module current savings are roughly the same, but the amount is less noticeable in the overall handset power usage where 20mA @ 3.3V is only maybe 8mA @ 8V. I found it easiest to test both RX and TX by tying the delay to a switch to be able to turn it on and off (rxtx_common.cpp).
Tested
Future Work
At some point we need to get core1 away from
while (1) if (Serial.available()) process(Serial.read())
with a DMA-based solution with an interruptible sleep. This can achieve similar power savings as in the testing I did with a bare ESP32-PICO-D4 dev board, each core that runs at 100% utilization draws roughly 25mA extra power over one that loops at 1000Hz.