Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions examples/simple_repeater/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ void setup() {

the_mesh.begin(fs);

board.initWatchdog(the_mesh.getNodePrefs()->wdt_timeout_secs);

#ifdef DISPLAY_CLASS
ui_task.begin(the_mesh.getNodePrefs(), FIRMWARE_BUILD_DATE, FIRMWARE_VERSION);
#endif
Expand All @@ -106,6 +108,7 @@ void setup() {
}

void loop() {
board.feedWatchdog();
int len = strlen(command);
while (Serial.available() && len < sizeof(command)-1) {
char c = Serial.read();
Expand Down
3 changes: 3 additions & 0 deletions examples/simple_room_server/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ void setup() {

the_mesh.begin(fs);

board.initWatchdog(the_mesh.getNodePrefs()->wdt_timeout_secs);

#ifdef DISPLAY_CLASS
ui_task.begin(the_mesh.getNodePrefs(), FIRMWARE_BUILD_DATE, FIRMWARE_VERSION);
#endif
Expand All @@ -83,6 +85,7 @@ void setup() {
}

void loop() {
board.feedWatchdog();
int len = strlen(command);
while (Serial.available() && len < sizeof(command)-1) {
char c = Serial.read();
Expand Down
3 changes: 3 additions & 0 deletions examples/simple_sensor/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ void setup() {

the_mesh.begin(fs);

board.initWatchdog(the_mesh.getNodePrefs()->wdt_timeout_secs);

#ifdef DISPLAY_CLASS
ui_task.begin(the_mesh.getNodePrefs(), FIRMWARE_BUILD_DATE, FIRMWARE_VERSION);
#endif
Expand All @@ -117,6 +119,7 @@ void setup() {
}

void loop() {
board.feedWatchdog();
int len = strlen(command);
while (Serial.available() && len < sizeof(command)-1) {
char c = Serial.read();
Expand Down
4 changes: 4 additions & 0 deletions src/MeshCore.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ class MainBoard {
virtual bool getBootloaderVersion(char* version, size_t max_len) { return false; }
virtual bool startOTAUpdate(const char* id, char reply[]) { return false; } // not supported

// Watchdog interface — override on platforms that support hardware WDT
virtual void initWatchdog(uint8_t timeout_secs) { }
virtual void feedWatchdog() { }

// Power management interface (boards with power management override these)
virtual bool isExternalPowered() { return false; }
virtual uint16_t getBootVoltage() { return 0; }
Expand Down
27 changes: 25 additions & 2 deletions src/helpers/CommonCLI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,8 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {
file.read((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166
file.read((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170
file.read((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290
// next: 291
file.read((uint8_t *)&_prefs->wdt_timeout_secs, sizeof(_prefs->wdt_timeout_secs)); // 291
// next: 292

// sanitise bad pref values
_prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f);
Expand Down Expand Up @@ -119,6 +120,7 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) {

// sanitise settings
_prefs->rx_boosted_gain = constrain(_prefs->rx_boosted_gain, 0, 1); // boolean
// wdt_timeout_secs: 0 is valid (disabled), no constrain needed — uint8_t can't exceed 255

file.close();
}
Expand Down Expand Up @@ -180,7 +182,8 @@ void CommonCLI::savePrefs(FILESYSTEM* fs) {
file.write((uint8_t *)&_prefs->adc_multiplier, sizeof(_prefs->adc_multiplier)); // 166
file.write((uint8_t *)_prefs->owner_info, sizeof(_prefs->owner_info)); // 170
file.write((uint8_t *)&_prefs->rx_boosted_gain, sizeof(_prefs->rx_boosted_gain)); // 290
// next: 291
file.write((uint8_t *)&_prefs->wdt_timeout_secs, sizeof(_prefs->wdt_timeout_secs)); // 291
// next: 292

file.close();
}
Expand Down Expand Up @@ -656,6 +659,19 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep
savePrefs();
strcpy(reply, "OK");
}
} else if (memcmp(config, "watchdog ", 9) == 0) {
uint32_t val = _atoi(&config[9]);
if (val > 255) {
strcpy(reply, "Error, must be 0 (off) or 1-255 (seconds)");
} else {
_prefs->wdt_timeout_secs = (uint8_t)val;
savePrefs();
#if defined(NRF52_PLATFORM)
sprintf(reply, "OK - Watchdog %s (reboot to apply)", val == 0 ? "disabled" : "enabled");
#else
sprintf(reply, "OK - Watchdog currently not implemented on this platform")
#endif
}
} else if (memcmp(config, "tx ", 3) == 0) {
_prefs->tx_power_dbm = atoi(&config[3]);
savePrefs();
Expand Down Expand Up @@ -808,6 +824,13 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep
} else {
strcpy(reply, "> strict");
}
} else if (memcmp(config, "watchdog", 8) == 0 && (config[8] == 0 || config[8] == ' ')) {
#if defined(NRF52_PLATFORM)
sprintf(reply, "> %u secs%s", _prefs->wdt_timeout_secs,
_prefs->wdt_timeout_secs == 0 ? " (disabled)" : " (active after reboot)");
#else
strcpy(reply, "> not supported on this platform");
#endif
} else if (memcmp(config, "tx", 2) == 0 && (config[2] == 0 || config[2] == ' ')) {
sprintf(reply, "> %d", (int32_t) _prefs->tx_power_dbm);
} else if (memcmp(config, "freq", 4) == 0) {
Expand Down
1 change: 1 addition & 0 deletions src/helpers/CommonCLI.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ struct NodePrefs { // persisted to file
uint8_t rx_boosted_gain; // power settings
uint8_t path_hash_mode; // which path mode to use when sending
uint8_t loop_detect;
uint8_t wdt_timeout_secs; // 0=disabled, 1-255=seconds (applies on reboot)
};

class CommonCLICallbacks {
Expand Down
43 changes: 43 additions & 0 deletions src/helpers/NRF52Board.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,49 @@ static void disconnect_callback(uint16_t conn_handle, uint8_t reason) {

void NRF52Board::begin() {
startup_reason = BD_STARTUP_NORMAL;
// NOTE: WDT is NOT started here. It is started from the application's
// setup() AFTER loading prefs, by calling initWatchdog(prefs.wdt_timeout_secs).
// This allows the timeout to be configured via CLI (set wdt N).
}

void NRF52Board::initWatchdog(uint8_t timeout_secs) {
// 0 = watchdog disabled
if (timeout_secs == 0) {
MESH_DEBUG_PRINTLN("WDT: Disabled by configuration (set wdt <secs> to enable)");
return;
}

// WDT can only be configured once after reset (before TASKS_START).
// After START, CRV and CONFIG are read-only and locked until next reset.
if (_wdt_running) return;

// Convert seconds to 32.768 kHz ticks
uint32_t ticks = (uint32_t)timeout_secs * 32768UL;

// Configure WDT behavior:
// Bit 0 (SLEEP): 1 = Run in SLEEP mode (keep counting during WFE/WFI)
// Bit 3 (HALT): 1 = Run in HALT (debug) mode
// This is the safest configuration — WDT counts in all power states.
//
// NOTE: SLEEP_Run means the WDT keeps counting during board.sleep() / WFE.
// This is safe because the Adafruit nRF52 BSP runs FreeRTOS with a ~1ms
// tick timer that wakes the CPU from WFE frequently, so loop() and
// feedWatchdog() run well within the timeout window. If a future change
// enables FreeRTOS tickless idle or any sleep >timeout without waking,
// the timeout must be increased or SLEEP_Pause used instead.
NRF_WDT->CONFIG = (WDT_CONFIG_SLEEP_Run << WDT_CONFIG_SLEEP_Pos) |
(WDT_CONFIG_HALT_Run << WDT_CONFIG_HALT_Pos);

NRF_WDT->CRV = ticks;
NRF_WDT->RREN = WDT_RREN_RR0_Enabled << WDT_RREN_RR0_Pos;
NRF_WDT->TASKS_START = 1;

_wdt_running = true;

// Initial kick so the full timeout window is available
NRF_WDT->RR[0] = WDT_RR_RR_Reload;

MESH_DEBUG_PRINTLN("WDT: Started with %u second timeout", (unsigned)timeout_secs);
}

#ifdef NRF52_POWER_MANAGEMENT
Expand Down
6 changes: 6 additions & 0 deletions src/helpers/NRF52Board.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include <Arduino.h>
#include <nrf.h>
#include <MeshCore.h>

#if defined(NRF52_PLATFORM)
Expand Down Expand Up @@ -48,6 +49,11 @@ class NRF52Board : public mesh::MainBoard {
NRF52Board(char *otaname) : ota_name(otaname) {}
virtual void begin();
virtual uint8_t getStartupReason() const override { return startup_reason; }

void initWatchdog(uint8_t timeout_secs) override;
inline void feedWatchdog() override { if (_wdt_running) NRF_WDT->RR[0] = WDT_RR_RR_Reload; }
bool _wdt_running = false;

virtual float getMCUTemperature() override;
virtual void reboot() override { NVIC_SystemReset(); }
virtual bool getBootloaderVersion(char* version, size_t max_len) override;
Expand Down