Skip to content
Draft
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
2 changes: 1 addition & 1 deletion examples/simple_repeater/MyMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1197,5 +1197,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();
}
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.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
}
}
}
13 changes: 12 additions & 1 deletion src/MeshCore.h
Original file line number Diff line number Diff line change
Expand Up @@ -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; }
Expand All @@ -49,9 +53,16 @@ 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) return;

MESH_DEBUG_PRINTLN("Entering sleep mode, wakeup scheduled in %u seconds", secs);
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;
Expand Down
13 changes: 13 additions & 0 deletions src/helpers/NRF52Board.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@ static void disconnect_callback(uint16_t conn_handle, uint8_t reason) {

void NRF52Board::begin() {
startup_reason = BD_STARTUP_NORMAL;
rx_wait_sem = xSemaphoreCreateBinary();
xSemaphoreGive(rx_wait_sem);
xSemaphoreTake(rx_wait_sem, portMAX_DELAY);
}

void NRF52Board::sleep(uint32_t secs) {
MESH_DEBUG_PRINTLN("Suspending loop for powersaving, set wakeup timer in %u seconds", secs);
xSemaphoreTake(rx_wait_sem, pdMS_TO_TICKS(secs * 1000));
}

void NRF52Board::onRXInterrupt() {
xSemaphoreGive(rx_wait_sem);
}

void NRF52BoardDCDC::begin() {
Expand Down Expand Up @@ -55,6 +67,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()
Expand Down
5 changes: 5 additions & 0 deletions src/helpers/NRF52Board.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
#if defined(NRF52_PLATFORM)

class NRF52Board : public mesh::MainBoard {
private:
SemaphoreHandle_t rx_wait_sem;

protected:
uint8_t startup_reason;

Expand All @@ -14,6 +17,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;
};

/*
Expand Down
16 changes: 10 additions & 6 deletions src/helpers/radiolib/RadioLibWrappers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,36 @@

#define STATE_IDLE 0
#define STATE_RX 1
#define STATE_TX_WAIT 3
#define STATE_TX_WAIT 2
#define STATE_TX_DONE 4
#define STATE_INT_READY 16

#define NUM_NOISE_FLOOR_SAMPLES 64
#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;
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