-
Notifications
You must be signed in to change notification settings - Fork 363
NRF52 Repeater Powersaving #1238
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
base: dev
Are you sure you want to change the base?
Conversation
|
I intended to do the same before. So I ended up just do some power saving trick to keep the power down to 8.5mA with few code changes only. Hope we can push the power down more for NRF52 |
Yes, there is no real sleep mode for NRF52 so either put the CPU in idle to save power or shut it down completely. The latter requires full reinit at wakeup.
In which case? With the MCU shut down and only the RF module running in RX mode? That would be in that ballpark, yes.
Sorry, I don't get what you mean here.
Actually it doesn't look complex to me at all. It's pretty straight forward. Instead of letting the CPU run continuously, it stops it and resumes it after the idle interval is over or a packet is received. What exactly looks complex to you?
What "trick" would that be? Do you mean And if it would work, it would do the same thing: put the CPU in idle, right? My approach does it in a platform-agnostic way, which is better IMHO. |
|
This feature has been long overdue. I tested it a bit with Heltec t114 (no screen) repeater. With stock FW I am seeing around 12 mA idle current on my cheap usb power meter. It is actually quite good - it used to be around 17 mA with stock FW not so long ago. Then I flashed FW from this branch and immediately went for "set idle.interval 10000". Now my cheapo usb meter shows current at 0 mA, which occasionally jumps briefly to 10 mA while idling. If I send a message from my companion then the repeater wakes, repeats and then goes back to idling. Now, the 0mA is probably just an artifact of my usb meter. I will try some additional tests tomorrow, but what I see so far - the power consumption at idle is noticeably lower and repeater still seems to be repeating messages and responding to admin commands etc What is the intended reasonable idle interval? Something like 1 to 3 seconds? |
|
May be I will add the CLI for esp32 based boards. Should we share the same CLI to enable/disable power saving to both esp32 and nrf52? Like set powersave 1? In your PR, you set the idle period? How about wake up period? |
|
@ngavars You have to measure the current at the battery cable by power meter. A usbc, it needs to turn on unneccessary components like uart chip, led... |
In my opinion we should use the same approach for ESP32 and NRF52 altogether. You should be able to take my generic implementation and simply add the
What would that be good for? A single parameter that sets the length of the sleep/idle interval is enough. If set to zero (default) there is no change compared to the current implementation in the main branch. Repeater admins can then decide if they want to save power by increasing the interval.
The idle interval is the time the CPU is idling/sleeping. The wake interval is hardcoded to five seconds or three minutes after reboot or after CLI activity. I don't think this needs to be a parameter for the user to change. |
The |
|
@ngavars Thanks for testing by the way! |
mtlynch
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm just a user sharing my thoughts and have no authority in this project.
Thanks for your review anyway! Very much appreciated! |
ba6e6aa to
1dfb6d7
Compare
|
I've had this running on my test repeater (RAK4630) for 3 days now and I can't see any negative or unexpected results. I currently do not have any ESP32 hardware to test, so if someone would be willing to help out that would be very much appreciated! 😃 |
1dfb6d7 to
37d0fe1
Compare
Can you attach/upload somewhere an bin file for v3 for testing? |
|
@SaschaKt You can find an archive with all repeater firmwares built by the GitHub CI here: https://github.com/fschrempf/MeshCore/actions/runs/20347701758/artifacts/4916433935 |
I flashed it. Sorry for feedback after a few seconds. for v3 the powersaving is not so efficient like Iotthinks solution. I'm measuring over Usb-C and the consumption goes down from 48mA to 39mA with your solution, with Iotthinks solution it goes down to 13mA measured over Usb-C. Measuring on battery input pins will be a few mA less. I don't think that the combination of both solutions will give more power saving because I think that Lightsleep at esp's with iotthink solution also reduce CPU activity |
|
@SaschaKt Thanks for testing! This is the expected behavior. This PR does not (yet) include the code for ESP32 sleep. And yes, the combination of both solutions won't give more power savings for ESP32 than #1107. The reason for my PR is that it provides a generic approach that is also applicable for other platforms than ESP32 while still providing the possibility to be extended by platform-specific sleep. If your test shows a slightly reduced power consumption and the repeater still works fine that's a success. |
It works, I could go normal to cli and start OTA and flash with Iotthink variant again. Now again 13mA. 39mA is still too much. nrF have per default enabled power saving internally and are at the small consumption level, with tweaking maybe a few mA savings. But esp's needs an enabled lightsleep to be useful as a repeater. The advantage of an nrF is that still with enabled BT the power consumption is not noticeable higher then without. Where is the problem to have two power routines, one for esp's and one for NRFs? Also Iotthink made powersavings for nrF too which will reduce from 12.5 to 8.5mA, that's about 32% |
@SaschaKt I think we are talking past each other. There is no problem here. What I want to achieve is a two step solution:
Apart from the differences mentioned in #1107 (review) this is purely a strategic difference from what #1107 does. |
You're right. I can confirm that with v3 the powerconsumption goes from 48mA to 39mA. That's an improvement..I hope that it would not interfere with hardware specific power saving ontop like iotthink has made..this has to be tested |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for working on this @fschrempf 👍 It would be good to see some more power saving for my Solar RAK19003 node :)
Having said that, I wonder if if would be better if the idle interval would be computed automatically rather than using a fixed duration. For example, in The Netherlands the mesh has grown exponentially. Where 2 months ago the mesh saw some activity, now I see hundreds of daily messages (let alone adverts) and (some parts of) Belgium and Germany have been connected. I wouldn't be surprised to see more of Germany and France starting to appear. An idle interval that worked a couple of weeks ago (maybe even days ago) may not work as well today.
Additionally, if CPU idling is triggered, how would one be able to enter remote management mode? IMHO when trying to use remote management or fetching telemetry, idling should be cancelled and the idle timer should be reset.
examples/simple_repeater/MyMesh.cpp
Outdated
| _prefs.advert_loc_policy = ADVERT_LOC_PREFS; | ||
|
|
||
| _prefs.adc_multiplier = 0.0f; // 0.0f means use default board multiplier | ||
| _prefs.idle_interval = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps add a comment to better explain what this means:
| _prefs.idle_interval = 0; | |
| // CPU Idle Interval (in seconds). | |
| // | |
| // If enabled (> 0), the CPU will start idling if there has not been any radio activity for | |
| // the specified number of seconds. | |
| // | |
| // Note: When the CPU is idling, remote management and / or UI will cease to function! | |
| _prefs.idle_interval = 0; |
Or shorter:
| _prefs.idle_interval = 0; | |
| _prefs.idle_interval = 0; // CPU will idle when no RF activity during this interval (in sec, 0 = disabled) |
It will wake up on any RX event. Remote management works nicely, at least in all my tests so far. |
@4np How is the idle interval related to the mesh activity? During idle the repeater will still respond to incoming packets. Only outgoing packets will be left waiting until the timeout expires. |
Thanks for the additional tests. This corresponds pretty nicely to the results of my own tests on RAK4631. |
@fschrempf , what I meant is an idle interval that works today, may not work tomorrow. If I read the PR, and correct me if I'm wrong, in your case you set the idle interval to 30 mins. So if there's no rx during a 30 min window, the CPU will start idling. To illustrate the issue, this summer I didn't hear anything at all on MeshCore and there were almost no repeaters or companions. In September / October, we had a small regional mesh with several messages per hour. Today, we have most of The Netherlands covered with the one of the most dense meshes in Europe and I receive several packets per second. So an idle timeout of, in your case, 30 mins that in my case would have been valid in September or summer would never trigger today. As such IMHO it would be better to have an algorithm compute the best idle interval and have it adjust automatically on changing conditions. New repeaters pop up every single day. ps. I think ill configured repeaters doing flood adverts at short intervals are the primary cause of the many packets per second, and putting a strain on the mesh. Reducing the number of configuration options could be beneficial as well. |
Idle time is how long the CPU should be idle and then wake up for doing stuff, or it is waken before throw an rx. After a Rx/tx I have measured on my own a fast falloff of the current and on Rx a short time normal state, repeat, idle again. |
|
@4np I think you misread the PR. The idle interval just determines how long the CPU will be in idle and therefore no processing of outgoing packets, serial user input or UI takes place. This interval can be interrupted at any time by incoming packets. The time after which the idling starts is hardcoded to 3 minutes after startup and 5 seconds during normal operation. So the worst that will happen in a busy mesh is that it will never go to idle because there are always incoming packets within the 5 second interval. But it will not affect the performance of the repeater or anything. |
|
Thank you for the clarification @fschrempf and @SaschaKt 👍 |
There is no reason to not use the reset pin as the RAK4630/31 module has it connected internally. Signed-off-by: Frieder Schrempf <[email protected]>
This makes the code easier to read and allows for easier changing of the hardcoded values. Signed-off-by: Frieder Schrempf <[email protected]>
When a CLI command is issued through the serial interface, extend the timeout for going to sleep to give the user more time for issuing more commands. Signed-off-by: Frieder Schrempf <[email protected]>
This uses the core functions suspendLoop() and resumeLoop() to suspend the main task and put the CPU in a low power idle mode. The wakeup occurs either through the specified timeout using a timer interrupt or through an RX interrupt from the radio module. Signed-off-by: Frieder Schrempf <[email protected]>
37d0fe1 to
9a659fa
Compare
|
With #1266 being merged in dev, this has now been reworked to work on top of this. I will let the new version run on my RAK4630 test repeater. Here is a link to the repeater firmware archive from the CI for any of you who want to do another test themselves. As a reminder, the new version requires powersaving to be turned on via CLI command |
|
The PR looks great. Next week, I will push a PR to enable powersaving for ALL ESP32-based board too. Hope we can have PowerSaving for both NRF52 and ESP32 repeaters soon. |
It's running on my spare RAK based repeater and so far all seems to be working well. |
|
@4np Thanks for testing. On my repeater it seems like the node becomes unresponsive after some time. It might be unrelated, but I need to do some debugging. |
|
@fschrempf this morning the repeater has become unresponsive for me as well, so there does appear to be some genuine issue. It looks like on nRF52 platforms a timer callback runs in the interrupt/scheduler context, not in the main loop. Another concern could be that resumeLoop() from setFlag() may be executing from a different context as well ( Also, the wake by RX logic does not cancel the timer so it keeps on running. Lastly, it may be preferable to safeguard against invalid interval values. Maybe clamp them between a min and a max value ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As the radio appears to become unresponsive after a period of low RX (overnight, at least in my case) it may be that the timer wasn’t actually armed (create/start/reset failed or got wedged), but suspendLoop() is still getting called. With no RX there’s no other wake source and the repeaters stays suspended forever.
However, it also does not wake on RX so it may seem to point to the RX interrupt not firing at all? It looks like this may happen when the loop is suspended while the IRQ isn’t serviced/cleared, causing it to stay locked and resulting in the RX interrupt not being executed.
| startup_reason = BD_STARTUP_NORMAL; | ||
| } | ||
|
|
||
| void NRF52Board::sleep(uint32_t secs) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Psuedo code (I am not sure about the PIN numbers):
// NRF52Board.cpp (file scope)
static SoftwareTimer sleep_timer;
static bool sleep_timer_inited = false;
static volatile bool sleeping = false;
static void sleep_timer_callback(TimerHandle_t) {
sleeping = false;
resumeLoop();
}
void NRF52Board::sleep(uint32_t secs) {
if (secs == 0) return;
// Convert seconds->ms with overflow clamp
uint64_t ms64 = (uint64_t)secs * 1000ULL;
uint32_t ms = (ms64 > 0xFFFFFFFFULL) ? 0xFFFFFFFFUL : (uint32_t)ms64;
// Helper: detect SX1262 DIO1 asserted (pending/latched IRQ).
auto dio1_high = []() -> bool {
#if defined(PIN_LORA_DIO1)
return digitalRead(PIN_LORA_DIO1) == HIGH;
#elif defined(LORA_DIO1)
return digitalRead(LORA_DIO1) == HIGH;
#else
return false; // if unknown, we can't gate (better than breaking build)
#endif
};
// SX1262/RAK4630: if DIO1 is HIGH (IRQ latched), do NOT suspendLoop() or you can deadlock.
if (dio1_high()) return;
sleeping = true;
// Cancel any previous schedule to avoid stale wake callbacks.
sleep_timer.stop();
// Initialize + (re)configure timer
if (!sleep_timer_inited) {
sleep_timer_inited = true;
}
sleep_timer.begin(ms, sleep_timer_callback, nullptr, /*repeating=*/false);
sleep_timer.start();
// Optional extra safety: re-check after arming timer to avoid a tiny race window
// where an IRQ asserts DIO1 between the first check and suspendLoop().
if (dio1_high()) {
sleeping = false;
sleep_timer.stop(); // don't leave a stray timer armed
return;
}
suspendLoop();
}
void NRF52Board::wakeFromInterrupt() {
// Optional micro-optimization: if we're not sleeping, no need to stop the timer.
// But still safe to call resumeLoop() unconditionally.
if (sleeping) {
sleeping = false;
sleep_timer.stop(); // prevent later spurious callback
}
resumeLoop();
}So this should likely fix the unresponsive repeater issue:
// SX1262/RAK4630: if DIO1 is HIGH (IRQ latched), do NOT suspendLoop() or you can deadlock.
if (dio1_high()) return;
In my case, I think I saw it come back to life when trying a bit later. That makes me think the timer wakeup works correctly but the RX wakeup fails. But I'm not sure at all.
Hm, that could happen if we go to sleep with the interrupt being disabled. But I don't see why the interrupt would be disabled at that point. As far as I know everything runs synchronously and we are suspending the loop task from within the loop. The same flow has been working flawlessly with my previous implementation using a FreeRTOS semaphore to block the loop task (see fschrempf@7e7f091) which makes me think there might some other issue here. |
|
It became unresponsive for me again, during daytime / peak RF. I'm going to test some code changes on my end as well. |
|
I had another look and IMHO there are a few things that stand out:
Thanks again for working on reducing power consumption! I think this will be a great addition, particularly for solar repeaters. |
True. |
|
I have merged a minimum piece of this code. This check "digitalRead(PIN_LORA_DIO1) == HIGH" seems to fix the "stuck" problem. BTW, we may need to move the definition of PIN_LORA_DIO1... into platform.ini instead. The suspendLoop and resumeLoop are very neat approach. |
Yeah that makes sense.
I have some changes where I added a
The core changes I made were these: NRF52Board.h // Return the LoRa DIO1 GPIO pin used for radio IRQ/wake on this board.
// Boards without a known LoRa DIO1 pin should return -1.
// This is used to gate sleep to avoid deadlocks when SX126x keeps DIO1 asserted.
virtual int16_t getLoRaDio1Pin() const { return -1; }and I implemented $ grep -r "getLoRaDio1Pin" --after-context=2
./variants/wio-tracker-l1/WioTrackerL1Board.h: int16_t getLoRaDio1Pin() const override {
./variants/wio-tracker-l1/WioTrackerL1Board.h- return P_LORA_DIO_1;
./variants/wio-tracker-l1/WioTrackerL1Board.h- }
--
./variants/xiao_nrf52/XiaoNrf52Board.h: int16_t getLoRaDio1Pin() const override {
./variants/xiao_nrf52/XiaoNrf52Board.h- return P_LORA_DIO_1;
./variants/xiao_nrf52/XiaoNrf52Board.h- }
--
./variants/heltec_mesh_solar/MeshSolarBoard.h: int16_t getLoRaDio1Pin() const override {
./variants/heltec_mesh_solar/MeshSolarBoard.h- return P_LORA_DIO_1;
./variants/heltec_mesh_solar/MeshSolarBoard.h- }
--
./variants/wio_wm1110/WioWM1110Board.h: int16_t getLoRaDio1Pin() const override {
./variants/wio_wm1110/WioWM1110Board.h- return P_LORA_DIO_1;
./variants/wio_wm1110/WioWM1110Board.h- }
--
./variants/minewsemi_me25ls01/MinewsemiME25LS01Board.h: int16_t getLoRaDio1Pin() const override {
./variants/minewsemi_me25ls01/MinewsemiME25LS01Board.h- return LORA_DIO_1;
./variants/minewsemi_me25ls01/MinewsemiME25LS01Board.h- }
--
./variants/thinknode_m1/ThinkNodeM1Board.h: int16_t getLoRaDio1Pin() const override {
./variants/thinknode_m1/ThinkNodeM1Board.h- return P_LORA_DIO_1;
./variants/thinknode_m1/ThinkNodeM1Board.h- }
--
./variants/ikoka_nano_nrf/IkokaNanoNRFBoard.h: int16_t getLoRaDio1Pin() const override {
./variants/ikoka_nano_nrf/IkokaNanoNRFBoard.h- return P_LORA_DIO_1;
./variants/ikoka_nano_nrf/IkokaNanoNRFBoard.h- }
--
./variants/promicro/PromicroBoard.h: int16_t getLoRaDio1Pin() const override {
./variants/promicro/PromicroBoard.h- return P_LORA_DIO_1;
./variants/promicro/PromicroBoard.h- }
--
./variants/mesh_pocket/MeshPocket.h: int16_t getLoRaDio1Pin() const override {
./variants/mesh_pocket/MeshPocket.h- return P_LORA_DIO_1;
./variants/mesh_pocket/MeshPocket.h- }
--
./variants/rak_wismesh_tag/RAKWismeshTagBoard.h: int16_t getLoRaDio1Pin() const override {
./variants/rak_wismesh_tag/RAKWismeshTagBoard.h- return P_LORA_DIO_1;
./variants/rak_wismesh_tag/RAKWismeshTagBoard.h- }
--
./variants/ikoka_stick_nrf/IkokaStickNRFBoard.h: int16_t getLoRaDio1Pin() const override {
./variants/ikoka_stick_nrf/IkokaStickNRFBoard.h- return P_LORA_DIO_1;
./variants/ikoka_stick_nrf/IkokaStickNRFBoard.h- }
--
./variants/ikoka_handheld_nrf/IkokaNrf52Board.h: int16_t getLoRaDio1Pin() const override {
./variants/ikoka_handheld_nrf/IkokaNrf52Board.h- return P_LORA_DIO_1;
./variants/ikoka_handheld_nrf/IkokaNrf52Board.h- }
--
./variants/keepteen_lt1/KeepteenLT1Board.h: int16_t getLoRaDio1Pin() const override {
./variants/keepteen_lt1/KeepteenLT1Board.h- return P_LORA_DIO_1;
./variants/keepteen_lt1/KeepteenLT1Board.h- }
--
./variants/lilygo_techo/TechoBoard.h: int16_t getLoRaDio1Pin() const override {
./variants/lilygo_techo/TechoBoard.h- return P_LORA_DIO_1;
./variants/lilygo_techo/TechoBoard.h- }
--
./variants/heltec_t114/T114Board.h: int16_t getLoRaDio1Pin() const override {
./variants/heltec_t114/T114Board.h- return P_LORA_DIO_1;
./variants/heltec_t114/T114Board.h- }
--
./variants/nano_g2_ultra/nano-g2.h: int16_t getLoRaDio1Pin() const override {
./variants/nano_g2_ultra/nano-g2.h- return P_LORA_DIO_1;
./variants/nano_g2_ultra/nano-g2.h- }
--
./variants/lilygo_techo_lite/TechoBoard.h: int16_t getLoRaDio1Pin() const override {
./variants/lilygo_techo_lite/TechoBoard.h- return P_LORA_DIO_1;
./variants/lilygo_techo_lite/TechoBoard.h- }
--
./variants/sensecap_solar/SenseCapSolarBoard.h: int16_t getLoRaDio1Pin() const override {
./variants/sensecap_solar/SenseCapSolarBoard.h- return P_LORA_DIO_1;
./variants/sensecap_solar/SenseCapSolarBoard.h- }
--
./variants/rak4631/RAK4631Board.h: int16_t getLoRaDio1Pin() const override {
./variants/rak4631/RAK4631Board.h- return P_LORA_DIO_1; // GPIO 47
./variants/rak4631/RAK4631Board.h- }
--
./variants/t1000-e/T1000eBoard.h: int16_t getLoRaDio1Pin() const override {
./variants/t1000-e/T1000eBoard.h- return P_LORA_DIO_1;
./variants/t1000-e/T1000eBoard.h- }The rest of the core changes I made: NRF52Board.cpp static bool sleep_timer_inited = false;
static volatile bool sleeping = false;
static bool ota_enabled = false;
...
static void sleep_timer_callback(TimerHandle_t) {
// Timer callbacks run in the FreeRTOS timer task, not in loop().
// Only resume the loop if we are actually sleeping.
if (!sleeping) return;
MESH_DEBUG_PRINTLN("Waking from sleep by timer callback");
sleeping = false;
resumeLoop();
}
void NRF52Board::sleep(uint32_t secs) {
if (secs == 0) return;
// Don't sleep when OTA is enabled.
if (ota_enabled) {
MESH_DEBUG_PRINTLN("Skip sleeping %lu s (OTA enabled)", (unsigned long)secs);
return;
}
int16_t dio1 = getLoRaDio1Pin();
if (dio1 < 0) {
MESH_DEBUG_PRINTLN("Could not sleep %lu s (unknown LoRa DIO1 pin)", (unsigned long)secs);
return;
}
uint64_t ms64 = (uint64_t)secs * 1000ULL;
uint32_t ms = (ms64 > 0xFFFFFFFFULL) ? 0xFFFFFFFFUL : (uint32_t)ms64;
auto dio1_high = [&]() -> bool {
return digitalRead((uint8_t)dio1) == HIGH;
};
if (dio1_high()) {
MESH_DEBUG_PRINTLN("Skipped sleeping %lu s (DIO1 is HIGH - pending/latched IRQ)", (unsigned long)secs);
return;
}
// From this point on, we logically consider ourselves asleep
sleeping = true;
// Lazily initialize the sleep timer once, and reuse it.
if (!sleep_timer_inited) {
sleep_timer.begin(ms, sleep_timer_callback, nullptr, /*repeating=*/false);
sleep_timer_inited = true;
} else {
sleep_timer.stop();
sleep_timer.setPeriod(ms);
}
sleep_timer.start();
if (dio1_high()) {
MESH_DEBUG_PRINTLN("Skipped sleeping %lu s (DIO1 went HIGH after arming timer)", (unsigned long)secs);
sleeping = false;
sleep_timer.stop();
return;
}
MESH_DEBUG_PRINTLN("Sleeping for %lu s", (unsigned long)secs);
suspendLoop();
// Defensive: should never reach here
sleeping = false;
}
...
bool NRF52BoardOTA::startOTAUpdate(const char *id, char reply[]) {
ota_enabled = true;
// Disable sleeping then OTA is enabled.
if (sleep_timer_inited) {
sleep_timer.stop();
sleeping = false;
}
...
}main.cpp // Power saving (if enabled)
//
// The interval of no activity after which the board will sleep to save power.
constexpr unsigned long POWER_SAVING_TRIGGER_INTERVAL_SEC = 1 * 60; // 1 minute
// The sleep duration when the board has entered power saving sleep (or sooner on RX).
constexpr unsigned long POWER_SAVING_SLEEP_DURATION_SEC = 30 * 60; // 30 minutes
// // When waking up from sleep, or when there is work pending, work a bit.
// constexpr unsigned long POWER_SAVING_WAKE_WORK_DURATION_SEC = 5; // 5 seconds
// The interval (in sec) after which we will sleep (if powersaving is enabled).
unsigned long nextPowerSavingIntervalInSec = POWER_SAVING_TRIGGER_INTERVAL_SEC;
...
void loop() {
...
if (the_mesh.getNodePrefs()->powersaving_enabled &&
the_mesh.millisHasNowPassed(the_mesh.getLastRX() + nextPowerSavingIntervalInSec * 1000)) {
// If we still have work to do, wait a little longer before sleeping.
if (the_mesh.hasPendingWork()) {
MESH_DEBUG_PRINTLN("Skipped powersaving (work pending, work another %lu s)", (unsigned long)POWER_SAVING_WAKE_WORK_DURATION_SEC);
nextPowerSavingIntervalInSec += POWER_SAVING_WAKE_WORK_DURATION_SEC;
} else {
MESH_DEBUG_PRINTLN("Start powersaving");
// Sleep and wake up after the sleep duration, or when receiving a LoRa packet.
board.sleep(POWER_SAVING_SLEEP_DURATION_SEC);
MESH_DEBUG_PRINTLN("Resuming work for %lu s", (unsigned long)POWER_SAVING_TRIGGER_INTERVAL_SEC);
nextPowerSavingIntervalInSec = POWER_SAVING_TRIGGER_INTERVAL_SEC;
}
}
}MyMesh.h unsigned long lastRX; // The last time a RX event happened.
...
public:
...
unsigned long getLastRX() {
return lastRX;
}And in MyMesh.cpp I set Hopefully this will be helpful to move this PR along :) |





In order to reduce the power consumption of NRF52 repeaters, this implements CPU idling by suspending the main task that runs the Arduino
loop().During the idle interval the CLI and the UI (if available) will be unresponsive. The RF module will be kept in RX mode and upon receiving a packet, the interrupt will cause the processing loop to continue immediately before going back to idle after all outgoing packets have been transferred.
On a RAK4631 repeater this can reduce the power consumption during RX mode from around 12 mA to around 7.5 mA.
Feedback, tests, reviews and questions welcome!