PowerSaving for companions

This commit is contained in:
Kevin Le committed 2026-10-22 21:12:56 +07:00
1 parent af23f95041
commit b522479d11
6 files changed
+188 -4

No files matched your search

+43 -2
View File
@@ -925,7 +925,14 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe
_prefs.tx_power_dbm = LORA_TX_POWER;
_prefs.gps_enabled = 0; // GPS disabled by default
_prefs.gps_interval = 0; // No automatic GPS updates by default
_prefs.radio_fem_rxgain = 1;
// PowerSaving variant
#ifdef RADIO_FEM_RXGAIN
_prefs.radio_fem_rxgain = RADIO_FEM_RXGAIN;
#else
_prefs.radio_fem_rxgain = 0;
#endif
#ifdef RADIO_FEM_TXGAIN
_prefs.radio_fem_txgain = RADIO_FEM_TXGAIN; // board-specific default for PA
#else
@@ -940,6 +947,15 @@ MyMesh::MyMesh(mesh::Radio &radio, mesh::RNG &rng, mesh::RTCClock &rtc, SimpleMe
_prefs.rx_boosted_gain = 1; // enabled by default
#endif
#endif
// PowerSaving variant
// PowerSaving defaults
_prefs.powersaving_enabled = 1;
// RXPS defaults - Balanced profile
_prefs.rxps.enabled = 1;
_prefs.rxps.level = 6;
_prefs.rxps.preamble = 16;
}
void MyMesh::begin() {
@@ -1003,6 +1019,10 @@ void MyMesh::begin() {
_store->loadChannels(this);
radio_driver.setParams(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
// PowerSaving variant
_prefs.getRadioPrefs()->applyRxPowerSaving(_prefs.sf, _prefs.bw);
radio_driver.setTxPower(_prefs.tx_power_dbm);
radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain);
@@ -1448,6 +1468,10 @@ void MyMesh::handleCmdFrame(size_t len) {
savePrefs();
radio_driver.setParams(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr);
// PowerSaving variant
_prefs.getRadioPrefs()->applyRxPowerSaving(_prefs.sf, _prefs.bw);
MESH_DEBUG_PRINTLN("OK: CMD_SET_RADIO_PARAMS: f=%d, bw=%d, sf=%d, cr=%d", freq, bw, (uint32_t)sf,
(uint32_t)cr);
@@ -1705,7 +1729,11 @@ void MyMesh::handleCmdFrame(size_t len) {
telemetry.addTemperature(TELEM_CHANNEL_SELF, temperature); // Built-in MCU Temperature
}
// PowerSaving variant
// query other sensors -- target specific
if (sensors.getLocationProvider() != NULL && sensors.getLocationProvider()->isPowerSavingEnabled()) {
sensors.getLocationProvider()->syncTime(); // Request for GPS sync if GPS is in PowerSaving
}
sensors.querySensors(0xFF, telemetry);
int i = 0;
@@ -2181,6 +2209,18 @@ bool MyMesh::handleCommand(const char* command, uint32_t sender_timestamp, char*
}
#endif
// PowerSaving variant
if (strcmp(command, "powersaving on") == 0 || strcmp(command, "powersaving off") == 0) {
_prefs.powersaving_enabled = strcmp(command, "powersaving on") == 0 ? 1 : 0;
savePrefs();
strcpy(reply, _prefs.powersaving_enabled ? "on" : "off");
return true;
}
if (strcmp(command, "powersaving") == 0) {
strcpy(reply, _prefs.powersaving_enabled ? "on" : "off");
return true;
}
if (strcmp(command, "board") == 0) {
strcpy(reply, board.getManufacturerName());
return true;
@@ -2448,7 +2488,8 @@ bool MyMesh::advert() {
}
}
// PowerSaving variant
// To check if there is pending work
bool MyMesh::hasPendingWork() const {
return _mgr->getOutboundTotal() > 0 || dirty_contacts_expiry != 0;
return _mgr->getOutboundTotal() > 0 || dirty_contacts_expiry != 0 || isTransmitting() || _prefs.getRadioPrefs()->isRxPowerSavingCalibrationActive();
}
+37 -1
View File
@@ -37,6 +37,10 @@ public:
uint32_t gps_interval = 0; // GPS read interval in seconds
uint8_t autoadd_config = 0; // bitmask for auto-add contacts config
uint8_t rx_boosted_gain = 0; // SX126x RX boosted gain mode (0=power saving, 1=boosted)
// PowerSaving variant
RxPowerSavingConfig rxps;
uint8_t radio_fem_rxgain = 0; // external LoRa FEM RX gain (LNA)
uint8_t radio_fem_txgain = 0; // external LoRa FEM TX gain (low by default)
uint8_t _client_repeat = 0; // DEPRECATED -> use repeat.disable_fwd
@@ -48,6 +52,10 @@ public:
char default_scope_name[31];
uint8_t default_scope_key[16];
int8_t tz_offset = 0;
// PowerSaving variant
uint8_t powersaving_enabled = 0; // boolean
#ifdef ENABLE_WIFI_INTERFACE
#ifndef WIFI_SSID
#define WIFI_SSID ""
@@ -81,10 +89,19 @@ private:
def("agc_int", _parent->agc_reset_interval);
def("hash_mode", _parent->path_hash_mode);
def("multi_ack", _parent->multi_acks);
// PowerSaving variant
def("rxps_en", _parent->rxps.enabled);
def("rxps_rx_us", _parent->rxps.rx_us);
def("rxps_sleep_us", _parent->rxps.sleep_us);
def("rxps_level", _parent->rxps.level);
def("rxps_preamble", _parent->rxps.preamble);
}
public:
RadioPrefs(NodePrefs* parent) : _parent(parent) { }
// PowerSaving variant
RxPowerSavingConfig* getRxPowerSavingConfig() override { return &_parent->rxps; }
// CommonRadioPrefs interface
float getFreq() const override { return _parent->freq; }
void setFreq(float f) override { _parent->freq = f; markDirty(); }
@@ -172,6 +189,18 @@ private:
};
CompanionPrefs companion;
// PowerSaving variant
class PowerPrefs : public ConfigSerializer { // same keys as CommonCLI
NodePrefs* _parent;
protected:
void structure() override {
def("pwr_sav_en", _parent->powersaving_enabled);
}
public:
PowerPrefs(NodePrefs* parent) : _parent(parent) { }
};
PowerPrefs power;
DynamicConfigSerializer custom;
#ifdef ENABLE_WIFI_INTERFACE
@@ -200,13 +229,17 @@ protected:
def("gps", gps);
def("repeat", repeat);
def("comp", companion);
// PowerSaving variant
def("power", power);
def("custom", custom);
#ifdef ENABLE_WIFI_INTERFACE
def("wifi", wifi);
#endif
}
public:
NodePrefs() : radio(this), gps(this), companion(this), custom(&radio)
NodePrefs() : radio(this), gps(this), companion(this), power(this), custom(&radio)
#ifdef ENABLE_WIFI_INTERFACE
, wifi(this)
#endif
@@ -220,6 +253,9 @@ public:
void setRepeatEn(bool en) { repeat.disable_fwd = en ? 0 : 1; }
CommonRadioPrefs* getRadioPrefs() { return &radio; }
// PowerSaving variant
const CommonRadioPrefs* getRadioPrefs() const { return &radio; }
KeyValueStore* getCustom() { return &custom; }
bool isDirty() const override { return ConfigSerializer::isDirty() || radio.isDirty() || custom.isDirty(); }
+73
View File
@@ -0,0 +1,73 @@
#pragma once
#if defined(ESP32_PLATFORM)
// PowerSaving for companions
static void enableCompanionPowerSaving() {
#if defined(BLE_PIN_CODE) && !CONFIG_IDF_TARGET_ESP32C6
// Enable BLE sleep
esp_bt_sleep_enable();
#endif
// Configure Power Management
#if CONFIG_IDF_TARGET_ESP32C3
esp_pm_config_esp32c3_t pm_config;
#elif CONFIG_IDF_TARGET_ESP32S3
esp_pm_config_esp32s3_t pm_config;
#elif CONFIG_IDF_TARGET_ESP32
esp_pm_config_esp32_t pm_config;
#elif CONFIG_IDF_TARGET_ESP32C6
esp_pm_config_t pm_config;
#endif
#if (defined(ARDUINO_USB_CDC_ON_BOOT) && ARDUINO_USB_CDC_ON_BOOT) || defined(ENABLE_USB_INTERFACE)
// Disable automatic light sleep for USB CDC Serial and USB Companion
pm_config = { .max_freq_mhz = 80, .min_freq_mhz = 40, .light_sleep_enable = false };
#else
pm_config = { .max_freq_mhz = 80, .min_freq_mhz = 40, .light_sleep_enable = true };
#endif
esp_err_t errPM = esp_pm_configure(&pm_config);
if (errPM == ESP_OK) {
Serial.println("Power Management configured successfully");
} else {
Serial.printf("Power Management failed to configure: %d\r\n", errPM);
}
}
static void disableCompanionPowerSaving() {
// Disable BLE sleep
esp_bt_sleep_disable();
// Configure Power Management
#if CONFIG_IDF_TARGET_ESP32C3
esp_pm_config_esp32c3_t pm_config;
#elif CONFIG_IDF_TARGET_ESP32S3
esp_pm_config_esp32s3_t pm_config;
#elif CONFIG_IDF_TARGET_ESP32
esp_pm_config_esp32_t pm_config;
#elif CONFIG_IDF_TARGET_ESP32C6
esp_pm_config_t pm_config;
#endif
#if defined(ESP32_CPU_FREQ)
pm_config = { .max_freq_mhz = ESP32_CPU_FREQ, .min_freq_mhz = ESP32_CPU_FREQ, .light_sleep_enable = false };
#else
pm_config = { .max_freq_mhz = 80, .min_freq_mhz = 80, .light_sleep_enable = false };
#endif
esp_err_t errPM = esp_pm_configure(&pm_config);
if (errPM == ESP_OK) {
Serial.println("Power Management configured successfully");
} else {
Serial.printf("Power Management failed to configure: %d\r\n", errPM);
}
}
static void applyCompanionPowerSaving(bool powersaving_enabled) {
if (the_mesh.getNodePrefs()->powersaving_enabled) {
enableCompanionPowerSaving();
} else {
disableCompanionPowerSaving();
}
}
#endif
+27 -1
View File
@@ -2,6 +2,12 @@
#include <Mesh.h>
#include "MyMesh.h"
#ifdef ESP32_PLATFORM
#include "esp_pm.h"
#include "esp_bt.h"
#include "PowerSaving.h"
#endif
// Believe it or not, this std C function is busted on some platforms!
static uint32_t _atoi(const char* sp) {
uint32_t n = 0;
@@ -287,6 +293,10 @@ void setup() {
#endif
board.onBootComplete();
#if defined(ESP32_PLATFORM)
applyCompanionPowerSaving(the_mesh.getNodePrefs()->powersaving_enabled);
#endif
}
void loop() {
@@ -302,12 +312,28 @@ void loop() {
external_watchdog.loop();
#endif
if (!the_mesh.hasPendingWork()) {
if (the_mesh.getNodePrefs()->powersaving_enabled && !the_mesh.hasPendingWork()) {
#if defined(NRF52_PLATFORM)
board.sleep(0); // nrf ignores seconds param, sleeps whenever possible
#elif defined(ESP32_PLATFORM)
#if defined(BLE_PIN_CODE)
if (!bluetooth_interface.isReadBusy() && !bluetooth_interface.isWriteBusy()) { // BLE is not busy
vTaskDelay(pdMS_TO_TICKS(10)); // attempt to sleep
}
#elif defined(ENABLE_USB_INTERFACE)
vTaskDelay(pdMS_TO_TICKS(10)); // attempt to sleep
#endif
#endif
}
#if defined(ESP32_PLATFORM)
static uint8_t applied_powersaving = the_mesh.getNodePrefs()->powersaving_enabled;
if (the_mesh.getNodePrefs()->powersaving_enabled != applied_powersaving) { // changed via CLI
applied_powersaving = the_mesh.getNodePrefs()->powersaving_enabled;
applyCompanionPowerSaving(applied_powersaving);
}
#endif
#ifdef ENABLE_WIFI_INTERFACE
if (wifi_enabled) {
// RP2040 has no WiFi event callbacks, so poll the link state instead
+5
View File
@@ -98,6 +98,9 @@ public:
virtual Packet* removeOutboundByIdx(int i) = 0;
virtual void queueInbound(Packet* packet, uint32_t scheduled_for) = 0;
virtual Packet* getNextInbound(uint32_t now) = 0;
// PowerSaving variant
virtual int getInboundTotal() const { return 0; } // delayed (rxdelay) packets not yet processed
};
typedef uint32_t DispatcherAction;
@@ -173,6 +176,8 @@ protected:
virtual unsigned long getDutyCycleWindowMs() const { return 3600000; }
public:
// PowerSaving variant
bool isTransmitting() const { return outbound != nullptr; }
void begin();
void loop();
+3
View File
@@ -38,4 +38,7 @@ public:
mesh::Packet* removeOutboundByIdx(int i) override;
void queueInbound(mesh::Packet* packet, uint32_t scheduled_for) override;
mesh::Packet* getNextInbound(uint32_t now) override;
// PowerSaving variant
int getInboundTotal() const override { return rx_queue.count(); }
};