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
16 changes: 11 additions & 5 deletions examples/simple_repeater/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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.sleep(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
}
}
}
11 changes: 11 additions & 0 deletions src/helpers/NRF52Board.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <bluefruit.h>

static BLEDfu bledfu;
static SoftwareTimer sleep_timer;

static void connect_callback(uint16_t conn_handle) {
(void)conn_handle;
Expand All @@ -17,10 +18,20 @@ static void disconnect_callback(uint16_t conn_handle, uint8_t reason) {
MESH_DEBUG_PRINTLN("BLE client disconnected");
}

static void sleep_timer_callback(TimerHandle_t xTimerID) {
resumeLoop();
}

void NRF52Board::begin() {
startup_reason = BD_STARTUP_NORMAL;
}

void NRF52Board::sleep(uint32_t secs) {
Copy link

@4np 4np Jan 5, 2026

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;

sleep_timer.begin(secs * 1000, sleep_timer_callback, NULL, false);
sleep_timer.start();
suspendLoop();
}

void NRF52BoardDCDC::begin() {
NRF52Board::begin();

Expand Down
1 change: 1 addition & 0 deletions src/helpers/NRF52Board.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ 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);
};

/*
Expand Down
5 changes: 5 additions & 0 deletions src/helpers/radiolib/RadioLibWrappers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ static
void setFlag(void) {
// we sent a packet, set the flag
state |= STATE_INT_READY;

#ifdef NRF52_PLATFORM
// NRF52 uses suspendLoop() for powersaving, resume on RX interrupt here
if (state & STATE_RX) resumeLoop();
#endif
}

void RadioLibWrapper::begin() {
Expand Down
2 changes: 1 addition & 1 deletion variants/rak4631/RAK4631Board.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down