diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 33e32a68a..b4329255e 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -1119,5 +1119,5 @@ void MyMesh::loop() { // To check if there is pending work bool MyMesh::hasPendingWork() const { - return _mgr->getOutboundCount(0xFFFFFFFF) > 0; + return (_mgr->getOutboundCount(0xFFFFFFFF) > 0) || !radio_driver.isInRecvMode(); } diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index 8c745613e..e09bc84ba 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -20,8 +20,12 @@ void halt() { static char command[160]; // For power saving +constexpr unsigned long ACTIVE_TIME_SEC_INUSE = 2 * 60; // 2 minutes +constexpr unsigned long ACTIVE_TIME_SEC_IDLE = 5; // 5 seconds +constexpr unsigned long IDLE_PERIOD_SEC = 30 * 60; // 30 minutes + unsigned long lastActive = 0; // mark last active time -unsigned long nextSleepinSecs = 120; // next sleep in seconds. The first sleep (if enabled) is after 2 minutes from boot +unsigned long nextSleepinSecs = ACTIVE_TIME_SEC_INUSE; // next sleep in seconds void setup() { Serial.begin(115200); @@ -110,6 +114,8 @@ void loop() { Serial.print('\n'); command[len - 1] = 0; // replace newline with C string null terminator char reply[160]; + lastActive = millis(); + nextSleepinSecs = ACTIVE_TIME_SEC_INUSE; the_mesh.handleCommand(0, command, reply); // NOTE: there is no sender_timestamp via serial! if (reply[0]) { Serial.print(" -> "); Serial.println(reply); @@ -125,14 +131,14 @@ void loop() { #endif rtc_clock.tick(); - if (the_mesh.getNodePrefs()->powersaving_enabled && // To check if power saving is enabled + if (the_mesh.getNodePrefs()->powersaving_enabled && the_mesh.millisHasNowPassed(lastActive + nextSleepinSecs * 1000)) { // To check if it is time to sleep if (!the_mesh.hasPendingWork()) { // No pending work. Safe to sleep - board.sleep(1800); // To sleep. Wake up after 30 minutes or when receiving a LoRa packet + board.enterSleep(IDLE_PERIOD_SEC); // To sleep. Wake up after 30 minutes or when receiving a LoRa packet lastActive = millis(); - nextSleepinSecs = 5; // Default: To work for 5s and sleep again + nextSleepinSecs = ACTIVE_TIME_SEC_IDLE; // Default: To work for 5s and sleep again } else { - nextSleepinSecs += 5; // When there is pending work, to work another 5s + nextSleepinSecs += ACTIVE_TIME_SEC_IDLE; // When there is pending work, to work another 5s } } } diff --git a/src/MeshCore.h b/src/MeshCore.h index 718660d3b..b52ac21ac 100644 --- a/src/MeshCore.h +++ b/src/MeshCore.h @@ -41,6 +41,10 @@ namespace mesh { #define BD_STARTUP_RX_PACKET 1 class MainBoard { +protected: + volatile bool prevent_sleep = false; + virtual void sleep(uint32_t secs) { /* no op */ } + public: virtual uint16_t getBattMilliVolts() = 0; virtual float getMCUTemperature() { return NAN; } @@ -49,9 +53,13 @@ class MainBoard { virtual const char* getManufacturerName() const = 0; virtual void onBeforeTransmit() { } virtual void onAfterTransmit() { } + virtual void onRXInterrupt() {} virtual void reboot() = 0; virtual void powerOff() { /* no op */ } - virtual void sleep(uint32_t secs) { /* no op */ } + virtual void enterSleep(uint32_t secs) { + if (!prevent_sleep) sleep(secs); + } + virtual void preventSleep() { prevent_sleep = true; } virtual uint32_t getGpio() { return 0; } virtual void setGpio(uint32_t values) {} virtual uint8_t getStartupReason() const = 0; diff --git a/src/helpers/NRF52Board.cpp b/src/helpers/NRF52Board.cpp index c0d58314e..b8d886abc 100644 --- a/src/helpers/NRF52Board.cpp +++ b/src/helpers/NRF52Board.cpp @@ -4,6 +4,7 @@ #include static BLEDfu bledfu; +static SoftwareTimer sleep_timer; static void connect_callback(uint16_t conn_handle) { (void)conn_handle; @@ -17,8 +18,27 @@ static void disconnect_callback(uint16_t conn_handle, uint8_t reason) { MESH_DEBUG_PRINTLN("BLE client disconnected"); } +static void resume() { + sleep_timer.stop(); + resumeLoop(); +} + +static void sleep_timer_callback(TimerHandle_t xTimerID) { + resume(); +} + void NRF52Board::begin() { startup_reason = BD_STARTUP_NORMAL; + sleep_timer.begin(5000, sleep_timer_callback, NULL, false); +} + +void NRF52Board::sleep(uint32_t secs) { + sleep_timer.setPeriod(secs * 1000); + suspendLoop(); +} + +void NRF52Board::onRXInterrupt() { + resume(); } void NRF52BoardDCDC::begin() { @@ -55,6 +75,7 @@ float NRF52Board::getMCUTemperature() { } bool NRF52BoardOTA::startOTAUpdate(const char *id, char reply[]) { + preventSleep(); // Config the peripheral connection with maximum bandwidth // more SRAM required by SoftDevice // Note: All config***() function must be called before begin() diff --git a/src/helpers/NRF52Board.h b/src/helpers/NRF52Board.h index 0d6c0a431..f1872423e 100644 --- a/src/helpers/NRF52Board.h +++ b/src/helpers/NRF52Board.h @@ -14,6 +14,8 @@ class NRF52Board : public mesh::MainBoard { virtual uint8_t getStartupReason() const override { return startup_reason; } virtual float getMCUTemperature() override; virtual void reboot() override { NVIC_SystemReset(); } + virtual void sleep(uint32_t secs) override; + virtual void onRXInterrupt() override; }; /* diff --git a/src/helpers/radiolib/RadioLibWrappers.cpp b/src/helpers/radiolib/RadioLibWrappers.cpp index e34078211..91c062947 100644 --- a/src/helpers/radiolib/RadioLibWrappers.cpp +++ b/src/helpers/radiolib/RadioLibWrappers.cpp @@ -12,24 +12,28 @@ #define SAMPLING_THRESHOLD 14 static volatile uint8_t state = STATE_IDLE; +static mesh::MainBoard *board; // this function is called when a complete packet // is transmitted by the module -static +static #if defined(ESP8266) || defined(ESP32) - ICACHE_RAM_ATTR + ICACHE_RAM_ATTR #endif -void setFlag(void) { + void radioISR(void) { // we sent a packet, set the flag state |= STATE_INT_READY; + + if (state & STATE_RX) board->onRXInterrupt(); } void RadioLibWrapper::begin() { - _radio->setPacketReceivedAction(setFlag); // this is also SentComplete interrupt + board = _board; + _radio->setPacketReceivedAction(radioISR); // this is also SentComplete interrupt state = STATE_IDLE; if (_board->getStartupReason() == BD_STARTUP_RX_PACKET) { // received a LoRa packet (while in deep sleep) - setFlag(); // LoRa packet is already received + radioISR(); // LoRa packet is already received } _noise_floor = 0; diff --git a/variants/rak4631/RAK4631Board.h b/variants/rak4631/RAK4631Board.h index a181256b0..343f4ebff 100644 --- a/variants/rak4631/RAK4631Board.h +++ b/variants/rak4631/RAK4631Board.h @@ -7,7 +7,7 @@ // LoRa radio module pins for RAK4631 #define P_LORA_DIO_1 47 #define P_LORA_NSS 42 -#define P_LORA_RESET RADIOLIB_NC // 38 +#define P_LORA_RESET 38 #define P_LORA_BUSY 46 #define P_LORA_SCLK 43 #define P_LORA_MISO 45