diff --git a/src/MeshCore.h b/src/MeshCore.h index fa0bc7a4..46dd9f19 100644 --- a/src/MeshCore.h +++ b/src/MeshCore.h @@ -53,6 +53,10 @@ public: virtual void onAfterTransmit() { } virtual void reboot() = 0; virtual void powerOff() { /* no op */ } + // Reload an already-running system watchdog without enabling one. Long, + // internally bounded operations can use this while retaining their own + // timeout. Boards without an explicit watchdog need no implementation. + virtual void serviceWatchdog() { /* no op */ } // Called by example setup() functions to signal that boot is complete. // Boards may override to stop a boot-indicator LED sequence or similar. // Default no-op: boards that don't care need not implement anything. diff --git a/src/helpers/NRF52Board.cpp b/src/helpers/NRF52Board.cpp index 85816c02..69731874 100644 --- a/src/helpers/NRF52Board.cpp +++ b/src/helpers/NRF52Board.cpp @@ -56,6 +56,19 @@ void NRF52Board::begin() { startup_reason = BD_STARTUP_NORMAL; } +#if NRF52_WATCHDOG_TIMEOUT_SECONDS > 0 +static void reloadWatchdogChannels() { + // Ordinarily only RR0 is enabled. If a bootloader left the watchdog running + // with a different reload channel, service every enabled channel. + const uint32_t enabled_channels = NRF_WDT->RREN & 0xFFUL; + for (uint8_t channel = 0; channel < 8; channel++) { + if (enabled_channels & (1UL << channel)) { + NRF_WDT->RR[channel] = WDT_RR_RR_Reload; + } + } +} +#endif + void NRF52Board::feedWatchdog(bool enabled) { #if NRF52_WATCHDOG_TIMEOUT_SECONDS > 0 // The nRF52 watchdog cannot be stopped after it starts. When the persisted @@ -77,20 +90,18 @@ void NRF52Board::feedWatchdog(bool enabled) { NRF_WDT->TASKS_START = 1; } - // Ordinarily only RR0 is enabled. If a bootloader left the watchdog running - // with a different reload channel, service every enabled channel rather - // than assuming ownership of RR0 and immediately entering a reset loop. - const uint32_t enabled_channels = NRF_WDT->RREN & 0xFFUL; - for (uint8_t channel = 0; channel < 8; channel++) { - if (enabled_channels & (1UL << channel)) { - NRF_WDT->RR[channel] = WDT_RR_RR_Reload; - } - } + reloadWatchdogChannels(); #else (void)enabled; #endif } +void NRF52Board::serviceWatchdog() { +#if NRF52_WATCHDOG_TIMEOUT_SECONDS > 0 + if (NRF_WDT->RUNSTATUS != 0) reloadWatchdogChannels(); +#endif +} + #ifdef NRF52_POWER_MANAGEMENT // Power Management global variables uint32_t g_nrf52_reset_reason = 0; // Reset/Startup reason diff --git a/src/helpers/NRF52Board.h b/src/helpers/NRF52Board.h index f8a46530..d2d45496 100644 --- a/src/helpers/NRF52Board.h +++ b/src/helpers/NRF52Board.h @@ -58,6 +58,7 @@ public: // A loop blocked forever in SoftDevice-backed flash I/O will stop feeding it // and be reset automatically. void feedWatchdog(bool enabled = true); + void serviceWatchdog() override; virtual uint8_t getStartupReason() const override { return startup_reason; } virtual float getMCUTemperature() override; virtual void reboot() override { NVIC_SystemReset(); } diff --git a/src/helpers/radiolib/CustomSX1276.h b/src/helpers/radiolib/CustomSX1276.h index d34c028c..e6e5e107 100644 --- a/src/helpers/radiolib/CustomSX1276.h +++ b/src/helpers/radiolib/CustomSX1276.h @@ -1,6 +1,7 @@ #pragma once #include +#include "MeshCore.h" #define RH_RF95_MODEM_STATUS_CLEAR 0x10 #define RH_RF95_MODEM_STATUS_HEADER_INFO_VALID 0x08 @@ -72,7 +73,7 @@ class CustomSX1276 : public SX1276 { | RH_RF95_MODEM_STATUS_HEADER_INFO_VALID)) != 0; } - int tryScanChannel(unsigned long timeout_ms = 2000UL) { + int tryScanChannel(unsigned long timeout_ms, mesh::MainBoard& board) { // start CAD int16_t state = startChannelScan(); RADIOLIB_ASSERT(state); @@ -81,12 +82,18 @@ class CustomSX1276 : public SX1276 { // completed, so wait on its IRQ pins with the same bounded policy used // by the generic wrapper. const unsigned long started = millis(); + unsigned long last_watchdog_service = started; while(!this->mod->hal->digitalRead(this->mod->getIrq())) { this->mod->hal->yield(); if(this->mod->hal->digitalRead(this->mod->getGpio())) { return(RADIOLIB_PREAMBLE_DETECTED); } - if(millis() - started >= timeout_ms) { + const unsigned long now = millis(); + if(now - last_watchdog_service >= 1000UL) { + board.serviceWatchdog(); + last_watchdog_service = now; + } + if(now - started >= timeout_ms) { return(RADIOLIB_ERR_RX_TIMEOUT); } } diff --git a/src/helpers/radiolib/CustomSX1276Wrapper.h b/src/helpers/radiolib/CustomSX1276Wrapper.h index 5f8138dc..3d04210c 100644 --- a/src/helpers/radiolib/CustomSX1276Wrapper.h +++ b/src/helpers/radiolib/CustomSX1276Wrapper.h @@ -42,7 +42,7 @@ public: protected: int16_t performChannelScan() override { - return ((CustomSX1276 *)_radio)->tryScanChannel(); + return ((CustomSX1276 *)_radio)->tryScanChannel(CAD_SCAN_TIMEOUT_MS, *_board); } bool radioDeepInit() override { diff --git a/src/helpers/radiolib/RadioLibWrappers.cpp b/src/helpers/radiolib/RadioLibWrappers.cpp index 5576de9a..a457477a 100644 --- a/src/helpers/radiolib/RadioLibWrappers.cpp +++ b/src/helpers/radiolib/RadioLibWrappers.cpp @@ -621,9 +621,16 @@ int16_t RadioLibWrapper::performChannelScan() { if (result != RADIOLIB_ERR_NONE) return result; const unsigned long started = millis(); - while (millis() - started < 2000UL) { + unsigned long last_watchdog_service = started; + while (millis() - started < CAD_SCAN_TIMEOUT_MS) { result = _radio->getChannelScanResult(); if (result != RADIOLIB_ERR_UNKNOWN) return result; + + const unsigned long now = millis(); + if (now - last_watchdog_service >= 1000UL) { + _board->serviceWatchdog(); + last_watchdog_service = now; + } yield(); } diff --git a/src/helpers/radiolib/RadioLibWrappers.h b/src/helpers/radiolib/RadioLibWrappers.h index 06192250..b1e029b6 100644 --- a/src/helpers/radiolib/RadioLibWrappers.h +++ b/src/helpers/radiolib/RadioLibWrappers.h @@ -12,6 +12,7 @@ class RadioLibWrapper : public mesh::Radio { protected: + static constexpr unsigned long CAD_SCAN_TIMEOUT_MS = 10000UL; PhysicalLayer* _radio; mesh::MainBoard* _board; uint32_t n_recv, n_sent, n_recv_errors;