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
2 changes: 1 addition & 1 deletion examples/companion_radio/MyMesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2242,7 +2242,7 @@ bool MyMesh::advert() {
}
}

// To check if there is pending work
// Check if there is pending work (packets to send or pending contacts write)
bool MyMesh::hasPendingWork() const {
return _mgr->getOutboundTotal() > 0 || dirty_contacts_expiry != 0;
}
35 changes: 35 additions & 0 deletions examples/companion_radio/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,11 @@ MyMesh the_mesh(radio_driver, fast_rng, rtc_clock, tables, store
#endif
);

// Power saving timing variables
unsigned long lastActive = 0; // Last time there was activity
unsigned long nextSleepInSecs = 120; // Wait 2 minutes before first sleep
const unsigned long WORK_TIME_SECS = 5; // Stay awake 5 seconds after wake/activity

/* END GLOBAL OBJECTS */

void halt() {
Expand Down Expand Up @@ -240,6 +245,9 @@ void setup() {
#endif

board.onBootComplete();

// Initialize power saving timer
lastActive = millis();
}

void loop() {
Expand All @@ -265,4 +273,31 @@ void loop() {
last_wifi_reconnect_attempt = millis();
}
#endif

// Power saving when BLE/WiFi is disabled
// Don't sleep if GPS is enabled - it needs continuous operation to maintain fix
// Note: Disabling BLE/WiFi via UI actually turns off the radio to save power
if (!serial_interface.isEnabled() && !the_mesh.getNodePrefs()->gps_enabled) {
// Check for pending work and update activity timer
if (the_mesh.hasPendingWork()) {
lastActive = millis();
if (nextSleepInSecs < 10) {
nextSleepInSecs += 5; // Extend work time by 5s if still busy
}
}

// Only sleep if enough time has passed since last activity
if (the_mesh.millisHasNowPassed(lastActive + (nextSleepInSecs * 1000))) {
#ifdef PIN_USER_BTN
// Sleep for 30 minutes, wake on LoRa packet, timer, or button press
board.enterLightSleep(1800, PIN_USER_BTN);
#else
// Sleep for 30 minutes, wake on LoRa packet or timer
board.enterLightSleep(1800);
#endif
// Just woke up - reset timers
lastActive = millis();
nextSleepInSecs = WORK_TIME_SECS; // Stay awake for 5s after wake
}
}
}
24 changes: 24 additions & 0 deletions src/helpers/ESP32Board.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
#include <Wire.h>
#include "soc/rtc.h"
#include "esp_system.h"
#include "driver/rtc_io.h"
#include "driver/gpio.h"

class ESP32Board : public mesh::MainBoard {
protected:
Expand Down Expand Up @@ -66,6 +68,28 @@ class ESP32Board : public mesh::MainBoard {
return P_LORA_DIO_1; // default for SX1262
}

void enterLightSleep(uint32_t secs, int pin_wake_btn = -1) {
#if defined(CONFIG_IDF_TARGET_ESP32S3) && defined(P_LORA_DIO_1) // Supported ESP32 variants
if (rtc_gpio_is_valid_gpio((gpio_num_t)P_LORA_DIO_1)) { // Only enter sleep mode if P_LORA_DIO_1 is RTC pin
esp_sleep_pd_config(ESP_PD_DOMAIN_RTC_PERIPH, ESP_PD_OPTION_ON);

esp_sleep_enable_ext1_wakeup((1L << P_LORA_DIO_1), ESP_EXT1_WAKEUP_ANY_HIGH); // Wake on LoRa packet

// Wake on button press (active-LOW: pin is HIGH when idle, LOW when pressed)
if (pin_wake_btn >= 0) {
gpio_wakeup_enable((gpio_num_t)pin_wake_btn, GPIO_INTR_LOW_LEVEL);
esp_sleep_enable_gpio_wakeup();
}

if (secs > 0) {
esp_sleep_enable_timer_wakeup(secs * 1000000); // To wake up every hour to do periodically jobs
}

esp_light_sleep_start(); // CPU enters light sleep
}
#endif
}

void sleep(uint32_t secs) override {
// Skip if not allow to sleep
if (inhibit_sleep) {
Expand Down
1 change: 1 addition & 0 deletions src/helpers/NRF52Board.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ class NRF52Board : public mesh::MainBoard {
virtual bool startOTAUpdate(const char *id, char reply[]) override;
virtual void sleep(uint32_t secs) override;
bool isExternalPowered() override;
void enterLightSleep(uint32_t secs, int pin_wake_btn = -1) { sleep(secs); }

#ifdef NRF52_POWER_MANAGEMENT
uint16_t getBootVoltage() override { return boot_voltage_mv; }
Expand Down
22 changes: 21 additions & 1 deletion src/helpers/esp32/SerialWifiInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,38 @@
void SerialWifiInterface::begin(int port) {
// wifi setup is handled outside of this class, only starts the server
server.begin(port);

// Store WiFi credentials for re-enable
#ifdef WIFI_SSID
_ssid = WIFI_SSID;
_password = WIFI_PWD;
_isEnabled = true; // WiFi starts enabled
#else
_ssid = nullptr;
_password = nullptr;
#endif
}

// ---------- public methods
void SerialWifiInterface::enable() {
void SerialWifiInterface::enable() {
if (_isEnabled) return;

_isEnabled = true;
clearBuffers();

// Re-enable WiFi with stored credentials
if (_ssid != nullptr && _password != nullptr) {
WiFi.mode(WIFI_STA);
WiFi.begin(_ssid, _password);
}
}

void SerialWifiInterface::disable() {
_isEnabled = false;

// Actually turn off WiFi to save power
WiFi.disconnect(true); // Disconnect and clear config
WiFi.mode(WIFI_OFF); // Turn off WiFi radio
}

size_t SerialWifiInterface::writeFrame(const uint8_t src[], size_t len) {
Expand Down
4 changes: 4 additions & 0 deletions src/helpers/esp32/SerialWifiInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ class SerialWifiInterface : public BaseSerialInterface {
bool _isEnabled;
unsigned long _last_write;
unsigned long adv_restart_time;
const char* _ssid;
const char* _password;

WiFiServer server;
WiFiClient client;
Expand Down Expand Up @@ -39,6 +41,8 @@ class SerialWifiInterface : public BaseSerialInterface {
deviceConnected = false;
_isEnabled = false;
_last_write = 0;
_ssid = nullptr;
_password = nullptr;
send_queue_len = recv_queue_len = 0;
received_frame_header.type = 0;
received_frame_header.length = 0;
Expand Down