From 6160509efcc0a3ce57fa32f4d36325c1b98b012e Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Wed, 5 Aug 2026 22:06:23 +0700 Subject: [PATCH 1/2] PowerSaving17: Cleanup for RX PowerSaving --- build-iotthinks.sh | 199 ++++++++-------------- examples/companion_radio/MyMesh.cpp | 50 ++---- examples/simple_repeater/MyMesh.cpp | 5 +- platformio.ini | 8 - src/helpers/CommonCLI.cpp | 101 ++--------- src/helpers/CommonCLI.h | 13 +- src/helpers/radiolib/RXPowerSaving.h | 103 +++++++++++ src/helpers/radiolib/RadioLibWrappers.cpp | 1 + variants/heltec_t096/platformio.ini | 2 - variants/heltec_t114/platformio.ini | 4 - variants/heltec_tracker_v2/platformio.ini | 3 - variants/heltec_v4/platformio.ini | 3 - variants/rak4631/platformio.ini | 2 - variants/t1000-e/platformio.ini | 2 - variants/thinknode_m3/platformio.ini | 2 - variants/xiao_nrf52/platformio.ini | 2 - variants/xiao_s3/platformio.ini | 2 - variants/xiao_s3_wio/platformio.ini | 4 - 18 files changed, 208 insertions(+), 298 deletions(-) create mode 100644 src/helpers/radiolib/RXPowerSaving.h diff --git a/build-iotthinks.sh b/build-iotthinks.sh index 535c202a..f80a55be 100644 --- a/build-iotthinks.sh +++ b/build-iotthinks.sh @@ -1,48 +1,51 @@ # sh ./build-repeaters-iotthinks.sh -export FIRMWARE_VERSION="PowerSaving16" +export FIRMWARE_VERSION="PowerSaving17" ############# Repeaters ############# # Commonly-used boards ## ESP32 - 17 boards sh build.sh build-firmware \ +Heltec_ct62_repeater \ +Heltec_E290_repeater \ Heltec_v3_repeater \ -Heltec_WSL3_repeater \ heltec_v4_repeater \ +heltec_tracker_v2_repeater \ +Heltec_Wireless_Paper_repeater \ +Heltec_Wireless_Tracker_repeater \ +Heltec_WSL3_repeater \ +LilyGo_T3S3_sx1262_repeater \ +LilyGo_TBeam_1W_repeater \ Station_G2_repeater \ T_Beam_S3_Supreme_SX1262_repeater \ Tbeam_SX1262_repeater \ -LilyGo_T3S3_sx1262_repeater \ -Xiao_S3_WIO_repeater \ Xiao_C3_repeater \ Xiao_C6_repeater_ \ -Heltec_E290_repeater \ -Heltec_Wireless_Tracker_repeater \ -LilyGo_TBeam_1W_repeater \ Xiao_S3_repeater \ -heltec_tracker_v2_repeater \ -Heltec_Wireless_Paper_repeater \ -Heltec_ct62_repeater +Xiao_S3_WIO_repeater -## NRF52 - 17 boards +## NRF52 - 21 boards sh build.sh build-firmware \ -RAK_4631_repeater \ -Heltec_t114_repeater \ -Xiao_nrf52_repeater \ -Heltec_mesh_solar_repeater \ -ProMicro_repeater \ -SenseCap_Solar_repeater \ -t1000e_repeater \ -LilyGo_T-Echo_repeater \ -WioTrackerL1_repeater \ -RAK_3401_repeater \ -RAK_WisMesh_Tag_repeater \ GAT562_30S_Mesh_Kit_repeater \ GAT562_Mesh_Tracker_Pro_repeater \ +Heltec_mesh_solar_repeater \ +Heltec_t096_repeater \ +Heltec_t1_repeater \ +Heltec_t114_repeater \ ikoka_nano_nrf_22dbm_repeater \ ikoka_nano_nrf_30dbm_repeater \ ikoka_nano_nrf_33dbm_repeater \ +LilyGo_T-Echo_repeater \ +ProMicro_repeater \ +RAK_3401_repeater \ +RAK_4631_repeater \ +RAK_WisMesh_Tag_repeater \ +SenseCap_Solar_repeater \ +t1000e_repeater \ ThinkNode_M1_repeater \ -Heltec_t096_repeater +ThinkNode_M3_repeater \ +ThinkNode_M6_repeater \ +WioTrackerL1_repeater \ +Xiao_nrf52_repeater ## ESP32, SX1276 - 3 boards sh build.sh build-firmware \ @@ -55,62 +58,63 @@ Tbeam_SX1276_repeater sh build.sh build-firmware \ Heltec_v3_room_server \ heltec_v4_room_server \ -LilyGo_TBeam_1W_room_server \ -Heltec_WSL3_room_server \ -Xiao_S3_room_server \ heltec_tracker_v2_room_server \ -Heltec_Wireless_Paper_room_server +Heltec_Wireless_Paper_room_server \ +Heltec_WSL3_room_server \ +LilyGo_TBeam_1W_room_server \ +Xiao_S3_room_server -# NRF52 - 6 boards +# NRF52 - 7 boards sh build.sh build-firmware \ -RAK_4631_room_server \ +Heltec_t096_room_server \ Heltec_t114_room_server \ -Xiao_nrf52_room_server \ +RAK_3401_room_server \ +RAK_4631_room_server \ t1000e_room_server \ WioTrackerL1_room_server \ -RAK_3401_room_server \ -Heltec_t096_room_server +Xiao_nrf52_room_server ############# Companions BLE ############# -# NRF52 - 12 boards +# NRF52 - 15 boards sh build.sh build-firmware \ -RAK_4631_companion_radio_ble \ +Heltec_t096_companion_radio_ble \ +Heltec_t096_companion_radio_ble_femoff \ +Heltec_t1_companion_radio_ble \ Heltec_t114_companion_radio_ble \ -Xiao_nrf52_companion_radio_ble \ -t1000e_companion_radio_ble \ LilyGo_T-Echo_companion_radio_ble \ -WioTrackerL1_companion_radio_ble \ RAK_3401_companion_radio_ble \ +RAK_4631_companion_radio_ble \ RAK_WisMesh_Tag_companion_radio_ble \ SenseCap_Solar_companion_radio_ble \ +t1000e_companion_radio_ble \ ThinkNode_M1_companion_radio_ble \ -Heltec_t096_companion_radio_ble \ -Heltec_t096_companion_radio_ble_femoff +ThinkNode_M3_companion_radio_ble \ +ThinkNode_M6_companion_radio_ble \ +WioTrackerL1_companion_radio_ble \ +Xiao_nrf52_companion_radio_ble ############# Companions BLE PS ############# -# ESP32 - 18 boards +# ESP32 - 19 boards sh build.sh build-firmware \ -Heltec_v3_companion_radio_ble_ps \ -heltec_v4_companion_radio_ble_ps \ -heltec_v4_3_companion_radio_ble_ps_femoff \ -Xiao_C3_companion_radio_ble_ps \ -Xiao_S3_companion_radio_ble_ps \ -Xiao_S3_WIO_companion_radio_ble_ps \ -Heltec_v2_companion_radio_ble_ps \ -LilyGo_TBeam_1W_companion_radio_ble_ps \ -Heltec_WSL3_companion_radio_ble_ps \ -Heltec_Wireless_Tracker_companion_radio_ble_ps \ -heltec_tracker_v2_companion_radio_ble_ps \ -Heltec_Wireless_Paper_companion_radio_ble_ps \ -LilyGo_TLora_V2_1_1_6_companion_radio_ble_ps \ Heltec_ct62_companion_radio_ble_ps \ +heltec_tracker_v2_companion_radio_ble_ps \ +Heltec_v2_companion_radio_ble_ps \ +Heltec_v3_companion_radio_ble_ps \ +heltec_v4_3_companion_radio_ble_ps_femoff \ +heltec_v4_companion_radio_ble_ps \ +heltec_v4_expansionkit_tft_companion_radio_ble_ps \ +Heltec_Wireless_Paper_companion_radio_ble_ps \ +Heltec_Wireless_Tracker_companion_radio_ble_ps \ +Heltec_WSL3_companion_radio_ble_ps \ +LilyGo_T3S3_sx1262_companion_radio_ble_ps \ +LilyGo_TBeam_1W_companion_radio_ble_ps \ +LilyGo_TLora_V2_1_1_6_companion_radio_ble_ps \ T_Beam_S3_Supreme_SX1262_companion_radio_ble_ps \ Tbeam_SX1262_companion_radio_ble_ps \ -heltec_v4_expansionkit_tft_companion_radio_ble_ps \ -LilyGo_T3S3_sx1262_companion_radio_ble_ps - -# Not working Tbeam_SX1276_companion_radio_ble_ps \ +Xiao_C3_companion_radio_ble_ps \ +Xiao_S3_companion_radio_ble_ps \ +Xiao_S3_WIO_companion_radio_ble_ps ############# Companions USB ############# sh build.sh build-firmware \ @@ -119,78 +123,17 @@ Heltec_t096_companion_radio_usb ############# Sample builds ############# # 14 boards sh build.sh build-firmware \ -Heltec_v3_repeater \ -heltec_v4_repeater \ -Xiao_C3_repeater \ -Xiao_C6_repeater_ \ -RAK_4631_repeater \ -Heltec_t096_repeater \ -Heltec_v3_companion_radio_ble_ps \ -heltec_v4_companion_radio_ble_ps \ -heltec_v4_3_companion_radio_ble_ps_femoff \ -Xiao_C3_companion_radio_ble_ps \ -Xiao_C6_companion_radio_ble_ \ -RAK_4631_companion_radio_ble \ -Heltec_t096_companion_radio_ble \ -Heltec_t096_companion_radio_ble_femoff - -############# GPS PowerSaving ############# -export FIRMWARE_VERSION="PowerSaving16.1.1" -sh build.sh build-firmware \ -heltec_v4_repeater \ -Heltec_t114_repeater \ -Heltec_t096_repeater \ -WioTrackerL1_repeater \ -LilyGo_T-Echo_repeater \ -Station_G2_repeater \ -Heltec_Wireless_Tracker_repeater \ -heltec_tracker_v2_repeater \ -Heltec_mesh_solar_repeater \ -SenseCap_Solar_repeater - -sh build.sh build-firmware \ -Heltec_t114_companion_radio_ble \ -t1000e_companion_radio_ble \ -LilyGo_T-Echo_companion_radio_ble \ -WioTrackerL1_companion_radio_ble \ -SenseCap_Solar_companion_radio_ble \ Heltec_t096_companion_radio_ble \ Heltec_t096_companion_radio_ble_femoff \ -heltec_v4_companion_radio_ble_ps \ +Heltec_t096_repeater \ +Heltec_v3_companion_radio_ble_ps \ +Heltec_v3_repeater \ heltec_v4_3_companion_radio_ble_ps_femoff \ -LilyGo_TBeam_1W_companion_radio_ble_ps \ -Heltec_Wireless_Tracker_companion_radio_ble_ps \ -heltec_tracker_v2_companion_radio_ble_ps \ -T_Beam_S3_Supreme_SX1262 \ -Tbeam_SX1262_companion_radio_ble_ps \ -heltec_v4_expansionkit_tft_companion_radio_ble_ps - -export FIRMWARE_VERSION="PowerSaving16.1.2" -sh build.sh build-firmware \ -RAK_4631_repeater \ -RAK_3401_repeater \ -RAK_WisMesh_Tag_repeater \ -GAT562_30S_Mesh_Kit_repeater \ -GAT562_Mesh_Tracker_Pro_repeater \ +heltec_v4_companion_radio_ble_ps \ +heltec_v4_repeater \ RAK_4631_companion_radio_ble \ -RAK_3401_companion_radio_ble \ -RAK_WisMesh_Tag_companion_radio_ble - -export FIRMWARE_VERSION="PowerSaving16.1.2" -sh build.sh build-firmware \ -Tbeam_SX1262_repeater \ -T_Beam_S3_Supreme_SX1262_repeater \ -LilyGo_TBeam_1W_repeater \ -Tbeam_SX1262_companion_radio_ble_ps \ -T_Beam_S3_Supreme_SX1262_companion_radio_ble_ps \ -LilyGo_TBeam_1W_companion_radio_ble_ps - -export FIRMWARE_VERSION="PowerSaving16.1.2" -sh build.sh build-firmware \ -t1000e_repeater \ -t1000e_companion_radio_ble \ -Heltec_mesh_solar_repeater \ -Heltec_mesh_solar_companion_radio_ble \ -Heltec_Wireless_Tracker_repeater \ -Heltec_Wireless_Tracker_companion_radio_ble_ps \ - +RAK_4631_repeater \ +Xiao_C3_companion_radio_ble_ps \ +Xiao_C3_repeater \ +Xiao_C6_companion_radio_ble_ \ +Xiao_C6_repeater_ \ No newline at end of file diff --git a/examples/companion_radio/MyMesh.cpp b/examples/companion_radio/MyMesh.cpp index 972755bc..9ee67445 100644 --- a/examples/companion_radio/MyMesh.cpp +++ b/examples/companion_radio/MyMesh.cpp @@ -2,6 +2,7 @@ #include // needed for PlatformIO #include +#include "helpers/radiolib/RXPowerSaving.h" #define CMD_APP_START 1 #define CMD_SEND_TXT_MSG 2 @@ -279,47 +280,19 @@ bool MyMesh::getCADEnabled() const { #endif #endif +// RX PowerSaving #ifdef RXPS_FIXED_ENABLED -static uint32_t ceilPositiveFloat(float value) { - uint32_t rounded = (uint32_t)value; - return value > (float)rounded ? rounded + 1 : rounded; -} - -static bool calcFixedRxPowerSaving(uint8_t sf, float bw, uint32_t* rx_us, uint32_t* sleep_us) { - if (RXPS_FIXED_LEVEL < 1 || RXPS_FIXED_LEVEL > 10 || sf < 5 || sf > 12 || - bw <= 0.0f || (RXPS_FIXED_PREAMBLE != 16 && RXPS_FIXED_PREAMBLE != 32)) { - return false; - } - - const float symbol_us = (1000.0f * (float)(1UL << sf)) / bw; - const float amount = (float)(RXPS_FIXED_LEVEL - 1) / 9.0f; - const float rx_start_symbols = RXPS_FIXED_PREAMBLE == 16 ? 12.0f : 16.0f; - const float sleep_start_symbols = RXPS_FIXED_PREAMBLE == 16 ? 2.0f : 15.0f; - const float rx_edge_symbols = 8.0f; - const float sleep_edge_symbols = (float)RXPS_FIXED_PREAMBLE + 4.25f - 8.0f; - - const float rx_symbols = rx_start_symbols + amount * (rx_edge_symbols - rx_start_symbols); - const float sleep_symbols = sleep_start_symbols + amount * (sleep_edge_symbols - sleep_start_symbols); - - *rx_us = ceilPositiveFloat(rx_symbols * symbol_us); - *sleep_us = (uint32_t)(sleep_symbols * symbol_us); - return true; -} - static void applyFixedRxPowerSaving(uint8_t sf, float bw) { uint32_t rx_us, sleep_us; - if (!calcFixedRxPowerSaving(sf, bw, &rx_us, &sleep_us)) { - MESH_DEBUG_PRINTLN("RX Power Saving fixed profile invalid"); + if (!calcRxPowerSavingLevel(RXPS_FIXED_LEVEL, sf, bw, RXPS_FIXED_PREAMBLE, &rx_us, &sleep_us)) { + POWERSAVING_DEBUG_PRINTLN("RX Power Saving fixed profile invalid"); return; } bool ok = radio_driver.setRxPowerSaving(true, rx_us, sleep_us); - MESH_DEBUG_PRINTLN("RX Power Saving fixed level %d p%d: %s (%lu/%lu us)", - RXPS_FIXED_LEVEL, - RXPS_FIXED_PREAMBLE, - ok ? "Enabled" : "Unsupported", - (unsigned long)rx_us, - (unsigned long)sleep_us); + POWERSAVING_DEBUG_PRINTLN("RX Power Saving fixed level %d p%d: %s (%lu/%lu us)", RXPS_FIXED_LEVEL, + RXPS_FIXED_PREAMBLE, ok ? "Enabled" : "Unsupported", (unsigned long)rx_us, + (unsigned long)sleep_us); } #endif @@ -1028,11 +1001,12 @@ void MyMesh::begin(bool has_display) { radio_driver.setTxPower(_prefs.tx_power_dbm); radio_driver.setRxBoostedGainMode(_prefs.rx_boosted_gain); board.setLoRaFemLnaEnabled(_prefs.radio_fem_rxgain); -#ifdef RXPS_FIXED_ENABLED - applyFixedRxPowerSaving(_prefs.sf, _prefs.bw); -#endif MESH_DEBUG_PRINTLN("RX Boosted Gain Mode: %s", radio_driver.getRxBoostedGainMode() ? "Enabled" : "Disabled"); + +#ifdef RXPS_FIXED_ENABLED + applyFixedRxPowerSaving(_prefs.sf, _prefs.bw); // RX PowerSaving +#endif } const char *MyMesh::getNodeName() { @@ -1457,7 +1431,7 @@ void MyMesh::handleCmdFrame(size_t len) { radio_driver.setParams(_prefs.freq, _prefs.bw, _prefs.sf, _prefs.cr); #ifdef RXPS_FIXED_ENABLED - applyFixedRxPowerSaving(_prefs.sf, _prefs.bw); + applyFixedRxPowerSaving(_prefs.sf, _prefs.bw); // RX PowerSaving #endif MESH_DEBUG_PRINTLN("OK: CMD_SET_RADIO_PARAMS: f=%d, bw=%d, sf=%d, cr=%d", freq, bw, (uint32_t)sf, (uint32_t)cr); diff --git a/examples/simple_repeater/MyMesh.cpp b/examples/simple_repeater/MyMesh.cpp index 8502834c..7f950493 100644 --- a/examples/simple_repeater/MyMesh.cpp +++ b/examples/simple_repeater/MyMesh.cpp @@ -1,5 +1,6 @@ #include "MyMesh.h" #include +#include "helpers/radiolib/RXPowerSaving.h" /* ------------------------------ Config -------------------------------- */ @@ -896,8 +897,8 @@ MyMesh::MyMesh(mesh::MainBoard &board, mesh::Radio &radio, mesh::MillisecondCloc _prefs.flood_max_advert = 8; _prefs.interference_threshold = 0; // disabled _prefs.cad_enabled = 0; // hardware CAD before TX (off by default; 'set cad on') - _prefs.rx_ps_rx_us = RX_POWERSAVING_DEFAULT_RX_US; - _prefs.rx_ps_sleep_us = RX_POWERSAVING_DEFAULT_SLEEP_US; + _prefs.rx_ps_rx_us = RX_POWERSAVING_DEFAULT_RX_US; // RX PowerSaving + _prefs.rx_ps_sleep_us = RX_POWERSAVING_DEFAULT_SLEEP_US; // RX PowerSaving // bridge defaults _prefs.bridge_enabled = 1; // enabled diff --git a/platformio.ini b/platformio.ini index 734d8059..e16f7b83 100644 --- a/platformio.ini +++ b/platformio.ini @@ -13,14 +13,6 @@ extra_configs = variants/*/platformio.ini platformio.local.ini -[companion_rxps] -custom_rxps_level = 5 -custom_rxps_preamble = 16 -build_flags = - -D RXPS_FIXED_ENABLED=1 - -D RXPS_FIXED_LEVEL=${companion_rxps.custom_rxps_level} - -D RXPS_FIXED_PREAMBLE=${companion_rxps.custom_rxps_preamble} - [arduino_base] framework = arduino monitor_speed = 115200 diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 0c9fb0e2..2b209a78 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -11,6 +11,8 @@ #define BRIDGE_MAX_BAUD 115200 #endif +#include + // Believe it or not, this std C function is busted on some platforms! static uint32_t _atoi(const char* sp) { uint32_t n = 0; @@ -29,80 +31,6 @@ static bool isValidName(const char *n) { return true; } -static bool isValidRxPowerSavingPeriod(uint32_t us) { - return us >= RX_POWERSAVING_MIN_PERIOD_US && us <= RX_POWERSAVING_MAX_PERIOD_US; -} - -// MeshCore preamble convention used for the RX powersaving timing calculation. -// Must stay in sync with RadioLibWrapper::preambleLengthForSF() (the value the -// radio actually transmits); kept local here because CommonCLI is radio-agnostic. -static uint16_t rxPowerSavingPreambleForSF(uint8_t sf) { - return sf <= 8 ? 32 : 16; -} - -static bool isNumeric(const char* sp) { - if (!sp || !*sp) return false; - while (*sp) { - if (*sp < '0' || *sp > '9') return false; - sp++; - } - return true; -} - -static uint32_t ceilPositiveFloat(float value) { - uint32_t rounded = (uint32_t)value; - return value > (float)rounded ? rounded + 1 : rounded; -} - -static bool calcRxPowerSavingLevel(uint32_t level, uint8_t sf, float bw, uint32_t preamble, - uint32_t* rx_us, uint32_t* sleep_us) { - if (level < 1 || level > 10 || sf < 5 || sf > 12 || bw <= 0.0f || (preamble != 16 && preamble != 32)) { - return false; - } - - const float symbol_us = (1000.0f * (float)(1UL << sf)) / bw; - const float amount = (float)(level - 1) / 9.0f; - const float rx_start_symbols = preamble == 16 ? 12.0f : 16.0f; - const float sleep_start_symbols = preamble == 16 ? 2.0f : 15.0f; - const float rx_edge_symbols = 8.0f; - const float sleep_edge_symbols = (float)preamble + 4.25f - 8.0f; - - const float rx_symbols = rx_start_symbols + amount * (rx_edge_symbols - rx_start_symbols); - const float sleep_symbols = sleep_start_symbols + amount * (sleep_edge_symbols - sleep_start_symbols); - - *rx_us = ceilPositiveFloat(rx_symbols * symbol_us); - *sleep_us = (uint32_t)(sleep_symbols * symbol_us); - return true; -} - -static void ensureRxPowerSavingDefaults(NodePrefs* prefs) { - if (!isValidRxPowerSavingPeriod(prefs->rx_ps_rx_us)) { - prefs->rx_ps_rx_us = RX_POWERSAVING_DEFAULT_RX_US; - } - if (!isValidRxPowerSavingPeriod(prefs->rx_ps_sleep_us)) { - prefs->rx_ps_sleep_us = RX_POWERSAVING_DEFAULT_SLEEP_US; - } -} - -// Recomputes rx_ps_rx_us/rx_ps_sleep_us from the stored level and the current -// radio SF/BW. No-op (returns false) for manual timings (rx_ps_level == 0). -// Lets level-based RX powersaving auto-retune when SF/BW change. -static bool recalcRxPowerSavingFromLevel(NodePrefs* prefs) { - if (prefs->rx_ps_level < 1 || prefs->rx_ps_level > 10) return false; // manual: nothing to recompute - uint32_t preamble = prefs->rx_ps_preamble ? prefs->rx_ps_preamble - : rxPowerSavingPreambleForSF(prefs->sf); - uint32_t rx_us, sleep_us; - if (!calcRxPowerSavingLevel(prefs->rx_ps_level, prefs->sf, prefs->bw, preamble, &rx_us, &sleep_us)) { - return false; - } - if (!isValidRxPowerSavingPeriod(rx_us) || !isValidRxPowerSavingPeriod(sleep_us)) { - return false; - } - prefs->rx_ps_rx_us = rx_us; - prefs->rx_ps_sleep_us = sleep_us; - return true; -} - void CommonCLI::loadPrefs(FILESYSTEM* fs) { if (fs->exists("/com_prefs")) { loadPrefsInt(fs, "/com_prefs"); // new filename @@ -208,13 +136,16 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { _prefs->rx_boosted_gain = constrain(_prefs->rx_boosted_gain, 0, 1); // boolean _prefs->radio_fem_rxgain = constrain(_prefs->radio_fem_rxgain, 0, 1); // boolean _prefs->cad_enabled = constrain(_prefs->cad_enabled, 0, 1); // boolean + _prefs->rx_powersaving_enabled = constrain(_prefs->rx_powersaving_enabled, 0, 1); _prefs->rx_ps_level = constrain(_prefs->rx_ps_level, 0, 10); if (_prefs->rx_ps_preamble != 16 && _prefs->rx_ps_preamble != 32) { _prefs->rx_ps_preamble = 0; // 0 = auto (derive from SF) } - ensureRxPowerSavingDefaults(_prefs); - recalcRxPowerSavingFromLevel(_prefs); // retune level-based timings to the loaded SF/BW + ensureRxPowerSavingDefaults(&_prefs->rx_ps_rx_us, &_prefs->rx_ps_sleep_us); + recalcRxPowerSavingFromLevel(_prefs->rx_ps_level, _prefs->sf, _prefs->bw, _prefs->rx_ps_preamble, + &_prefs->rx_ps_rx_us, + &_prefs->rx_ps_sleep_us); // retune level-based timings to the loaded SF/BW file.close(); } @@ -761,17 +692,17 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep } else { strcpy(reply, "Error: state must be on or off"); } - } else if (memcmp(config, "radio.rxps ", 11) == 0) { + } else if (memcmp(config, "radio.rxps ", 11) == 0) { // RX PowerSaving const char* value = &config[11]; uint8_t enable = _prefs->rx_powersaving_enabled; uint32_t rx_us = _prefs->rx_ps_rx_us; uint32_t sleep_us = _prefs->rx_ps_sleep_us; - uint32_t level = 0; - uint32_t preamble = rxPowerSavingPreambleForSF(_prefs->sf); + uint8_t level = 0; + uint8_t preamble = rxPowerSavingPreambleForSF(_prefs->sf); bool level_requested = false; bool preamble_overridden = false; - ensureRxPowerSavingDefaults(_prefs); + ensureRxPowerSavingDefaults(&_prefs->rx_ps_rx_us, &_prefs->rx_ps_sleep_us); rx_us = _prefs->rx_ps_rx_us; sleep_us = _prefs->rx_ps_sleep_us; @@ -875,7 +806,9 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep _prefs->bw = bw; // Retune level-based RX powersaving to the new SF/BW. Persist only; the // radio itself is "reboot to apply", and begin() re-arms the timings then. - bool rxps_retuned = recalcRxPowerSavingFromLevel(_prefs); + bool rxps_retuned = recalcRxPowerSavingFromLevel( + _prefs->rx_ps_level, _prefs->sf, _prefs->bw, _prefs->rx_ps_preamble, &_prefs->rx_ps_rx_us, + &_prefs->rx_ps_sleep_us); // retune level-based timings to the loaded SF/BW _callbacks->savePrefs(); strcpy(reply, rxps_retuned ? "OK - reboot to apply (rxps retuned)" : "OK - reboot to apply"); } else { @@ -915,7 +848,7 @@ void CommonCLI::handleSetCmd(uint32_t sender_timestamp, char* command, char* rep strcpy(reply, "OK"); } else { strcpy(reply, "Error, max 64"); - } + } } else if (memcmp(config, "flood.max.advert ", 17) == 0) { uint8_t m = atoi(&config[17]); if (m <= 64) { @@ -1121,8 +1054,8 @@ void CommonCLI::handleGetCmd(uint32_t sender_timestamp, char* command, char* rep } else { sprintf(reply, "> %s", _board->isLoRaFemLnaEnabled() ? "on" : "off"); } - } else if (memcmp(config, "radio.rxps", 10) == 0) { - ensureRxPowerSavingDefaults(_prefs); + } else if (memcmp(config, "radio.rxps", 10) == 0) { // RX PowerSaving + ensureRxPowerSavingDefaults(&_prefs->rx_ps_rx_us, &_prefs->rx_ps_sleep_us); sprintf(reply, "> %s,%lu,%lu", _prefs->rx_powersaving_enabled ? "on" : "off", (unsigned long)_prefs->rx_ps_rx_us, (unsigned long)_prefs->rx_ps_sleep_us); } else if (memcmp(config, "rxps.wd", 7) == 0) { diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 6ec5aa01..a4640242 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -19,18 +19,6 @@ #define LOOP_DETECT_MODERATE 2 #define LOOP_DETECT_STRICT 3 -#define RX_POWERSAVING_DEFAULT_RX_US 65625UL -#define RX_POWERSAVING_DEFAULT_SLEEP_US 60000UL -#define RX_POWERSAVING_MIN_PERIOD_US 1000UL -#define RX_POWERSAVING_MAX_PERIOD_US 30000000UL - -// The named profiles are level presets pinned to a 16-symbol preamble: most -// deployed senders still transmit 16-symbol preambles regardless of the newer -// SF-based rule (32 for SF <= 8). Revisit once the field has largely migrated. -#define RX_POWERSAVING_CONSERVATIVE_LEVEL 1UL -#define RX_POWERSAVING_BALANCED_LEVEL 5UL -#define RX_POWERSAVING_PROFILE_PREAMBLE 16UL - struct NodePrefs { // persisted to file float airtime_factor; char node_name[32]; @@ -133,6 +121,7 @@ public: // no op by default }; + // RX PowerSaving virtual bool setRxPowerSaving(bool enable, uint32_t rx_us, uint32_t sleep_us) { return !enable; }; diff --git a/src/helpers/radiolib/RXPowerSaving.h b/src/helpers/radiolib/RXPowerSaving.h new file mode 100644 index 00000000..8cfa6bcf --- /dev/null +++ b/src/helpers/radiolib/RXPowerSaving.h @@ -0,0 +1,103 @@ +#pragma once +#include + +#define RX_POWERSAVING_DEFAULT_RX_US 65625UL +#define RX_POWERSAVING_DEFAULT_SLEEP_US 60000UL +#define RX_POWERSAVING_MIN_PERIOD_US 1000UL +#define RX_POWERSAVING_MAX_PERIOD_US 30000000UL + +// The named profiles are level presets pinned to a 16-symbol preamble: most +// deployed senders still transmit 16-symbol preambles regardless of the newer +// SF-based rule (32 for SF <= 8). Revisit once the field has largely migrated. +#define RX_POWERSAVING_CONSERVATIVE_LEVEL 1 +#define RX_POWERSAVING_BALANCED_LEVEL 5 +#define RX_POWERSAVING_PROFILE_PREAMBLE 16 + +// Fixed settings for companions +#define RXPS_FIXED_ENABLED 1 +#define RXPS_FIXED_LEVEL RX_POWERSAVING_BALANCED_LEVEL +#define RXPS_FIXED_PREAMBLE RX_POWERSAVING_PROFILE_PREAMBLE + +inline bool isValidRxPowerSavingPeriod(uint32_t us) { + return us >= RX_POWERSAVING_MIN_PERIOD_US && us <= RX_POWERSAVING_MAX_PERIOD_US; +} + +// MeshCore preamble convention used for the RX powersaving timing calculation. +// Must stay in sync with RadioLibWrapper::preambleLengthForSF() (the value the +// radio actually transmits); kept local here because CommonCLI is radio-agnostic. +inline uint8_t rxPowerSavingPreambleForSF(uint8_t sf) { + return sf <= 8 ? 32 : 16; +} + +inline bool isNumeric(const char *sp) { + if (!sp || !*sp) return false; + while (*sp) { + if (*sp < '0' || *sp > '9') return false; + sp++; + } + return true; +} + +inline uint32_t ceilPositiveFloat(float value) { + uint32_t rounded = (uint32_t)value; + return value > (float)rounded ? rounded + 1 : rounded; +} + +inline bool calcRxPowerSavingLevel(uint8_t level, uint8_t sf, float bw, uint8_t preamble, uint32_t *rx_us, + uint32_t *sleep_us) { + if (level < 1 || level > 10 || sf < 5 || sf > 12 || bw <= 0.0f || (preamble != 16 && preamble != 32)) { + return false; + } + + const float symbol_us = (1000.0f * (float)(1UL << sf)) / bw; + const float amount = (float)(level - 1) / 9.0f; + const float rx_start_symbols = preamble == 16 ? 12.0f : 16.0f; + const float sleep_start_symbols = preamble == 16 ? 2.0f : 15.0f; + const float rx_edge_symbols = 8.0f; + const float sleep_edge_symbols = (float)preamble + 4.25f - 8.0f; + + const float rx_symbols = rx_start_symbols + amount * (rx_edge_symbols - rx_start_symbols); + const float sleep_symbols = sleep_start_symbols + amount * (sleep_edge_symbols - sleep_start_symbols); + + *rx_us = ceilPositiveFloat(rx_symbols * symbol_us); + *sleep_us = (uint32_t)(sleep_symbols * symbol_us); + return true; +} + +inline void ensureRxPowerSavingDefaults(uint32_t *rx_ps_rx_us, uint32_t *rx_ps_sleep_us) { + if (!isValidRxPowerSavingPeriod(*rx_ps_rx_us)) { + *rx_ps_rx_us = RX_POWERSAVING_DEFAULT_RX_US; + } + + if (!isValidRxPowerSavingPeriod(*rx_ps_sleep_us)) { + *rx_ps_sleep_us = RX_POWERSAVING_DEFAULT_SLEEP_US; + } +} + +// Recomputes rx_ps_rx_us/rx_ps_sleep_us from the stored level and the current +// radio SF/BW. No-op (returns false) for manual timings (rx_ps_level == 0). +// Lets level-based RX powersaving auto-retune when SF/BW change. +inline bool recalcRxPowerSavingFromLevel(uint8_t level, uint8_t sf, float bw, uint8_t preamble, + uint32_t *rx_ps_rx_us, uint32_t *rx_ps_sleep_us) { + if (level < 1 || level > 10) { + return false; // manual: nothing to recompute + } + + if (preamble == 0) { + preamble = rxPowerSavingPreambleForSF(sf); + } + + uint32_t rx_us, sleep_us; + if (!calcRxPowerSavingLevel(level, sf, bw, preamble, &rx_us, &sleep_us)) { + return false; + } + + if (!isValidRxPowerSavingPeriod(rx_us) || !isValidRxPowerSavingPeriod(sleep_us)) { + return false; + } + + *rx_ps_rx_us = rx_us; + *rx_ps_sleep_us = sleep_us; + + return true; +} \ No newline at end of file diff --git a/src/helpers/radiolib/RadioLibWrappers.cpp b/src/helpers/radiolib/RadioLibWrappers.cpp index b44b428e..4f32176e 100644 --- a/src/helpers/radiolib/RadioLibWrappers.cpp +++ b/src/helpers/radiolib/RadioLibWrappers.cpp @@ -292,6 +292,7 @@ bool RadioLibWrapper::isInRecvMode() const { return (state & ~STATE_INT_READY) == STATE_RX; } +// RX PowerSaving bool RadioLibWrapper::setRxPowerSaving(bool enabled, uint32_t rx_us, uint32_t sleep_us) { if (enabled && !supportsRxPowerSaving()) { return false; diff --git a/variants/heltec_t096/platformio.ini b/variants/heltec_t096/platformio.ini index 1324ad82..1961b374 100644 --- a/variants/heltec_t096/platformio.ini +++ b/variants/heltec_t096/platformio.ini @@ -127,7 +127,6 @@ board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld board_upload.maximum_size = 712704 build_flags = ${Heltec_t096.build_flags} - ${companion_rxps.build_flags} -I examples/companion_radio/ui-new -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 @@ -158,7 +157,6 @@ board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld board_upload.maximum_size = 712704 build_flags = ${Heltec_t096.build_flags} - ${companion_rxps.build_flags} -I examples/companion_radio/ui-new -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 diff --git a/variants/heltec_t114/platformio.ini b/variants/heltec_t114/platformio.ini index 643bc5d1..9817bc72 100644 --- a/variants/heltec_t114/platformio.ini +++ b/variants/heltec_t114/platformio.ini @@ -99,7 +99,6 @@ board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld board_upload.maximum_size = 712704 build_flags = ${Heltec_t114.build_flags} - ${companion_rxps.build_flags} -I examples/companion_radio/ui-new -D DISPLAY_CLASS=NullDisplayDriver -D MAX_CONTACTS=350 @@ -123,7 +122,6 @@ board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld board_upload.maximum_size = 712704 build_flags = ${Heltec_t114.build_flags} - ${companion_rxps.build_flags} -I examples/companion_radio/ui-new -D DISPLAY_CLASS=NullDisplayDriver -D MAX_CONTACTS=350 @@ -213,7 +211,6 @@ board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld board_upload.maximum_size = 712704 build_flags = ${Heltec_t114_with_display.build_flags} - ${companion_rxps.build_flags} -I examples/companion_radio/ui-new -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 @@ -238,7 +235,6 @@ board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld board_upload.maximum_size = 712704 build_flags = ${Heltec_t114_with_display.build_flags} - ${companion_rxps.build_flags} -I examples/companion_radio/ui-new -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 diff --git a/variants/heltec_tracker_v2/platformio.ini b/variants/heltec_tracker_v2/platformio.ini index ff5c02c2..d914ce6a 100644 --- a/variants/heltec_tracker_v2/platformio.ini +++ b/variants/heltec_tracker_v2/platformio.ini @@ -136,7 +136,6 @@ lib_deps = extends = Heltec_tracker_v2 build_flags = ${Heltec_tracker_v2.build_flags} - ${companion_rxps.build_flags} -I examples/companion_radio/ui-new -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 @@ -156,7 +155,6 @@ lib_deps = extends = Heltec_tracker_v2 build_flags = ${Heltec_tracker_v2.build_flags} - ${companion_rxps.build_flags} -I examples/companion_radio/ui-new -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 @@ -186,7 +184,6 @@ platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 extends = Heltec_tracker_v2 build_flags = ${Heltec_tracker_v2.build_flags} - ${companion_rxps.build_flags} -I examples/companion_radio/ui-new -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 diff --git a/variants/heltec_v4/platformio.ini b/variants/heltec_v4/platformio.ini index ec90e723..113ba24a 100644 --- a/variants/heltec_v4/platformio.ini +++ b/variants/heltec_v4/platformio.ini @@ -184,7 +184,6 @@ lib_deps = extends = heltec_v4_oled build_flags = ${heltec_v4_oled.build_flags} - ${companion_rxps.build_flags} -I examples/companion_radio/ui-new -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 @@ -204,7 +203,6 @@ lib_deps = extends = heltec_v4_oled build_flags = ${heltec_v4_oled.build_flags} - ${companion_rxps.build_flags} -I examples/companion_radio/ui-new -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 @@ -239,7 +237,6 @@ build_flags = extends = heltec_v4_oled build_flags = ${heltec_v4_oled.build_flags} - ${companion_rxps.build_flags} -I examples/companion_radio/ui-new -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 diff --git a/variants/rak4631/platformio.ini b/variants/rak4631/platformio.ini index 80ab08a0..bcaacc47 100644 --- a/variants/rak4631/platformio.ini +++ b/variants/rak4631/platformio.ini @@ -123,7 +123,6 @@ board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld board_upload.maximum_size = 712704 build_flags = ${rak4631.build_flags} - ${companion_rxps.build_flags} -I examples/companion_radio/ui-new -D PIN_USER_BTN=9 -D PIN_USER_BTN_ANA=31 @@ -145,7 +144,6 @@ board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld board_upload.maximum_size = 712704 build_flags = ${rak4631.build_flags} - ${companion_rxps.build_flags} -I examples/companion_radio/ui-new -D PIN_USER_BTN=9 -D PIN_USER_BTN_ANA=31 diff --git a/variants/t1000-e/platformio.ini b/variants/t1000-e/platformio.ini index 7e662fbc..43a3d93f 100644 --- a/variants/t1000-e/platformio.ini +++ b/variants/t1000-e/platformio.ini @@ -73,7 +73,6 @@ extends = t1000-e board_build.ldscript = boards/nrf52840_s140_v7_extrafs.ld board_upload.maximum_size = 708608 build_flags = ${t1000-e.build_flags} - ${companion_rxps.build_flags} -I examples/companion_radio/ui-orig -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 @@ -97,7 +96,6 @@ extends = t1000-e board_build.ldscript = boards/nrf52840_s140_v7_extrafs.ld board_upload.maximum_size = 708608 build_flags = ${t1000-e.build_flags} - ${companion_rxps.build_flags} -I examples/companion_radio/ui-orig -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 diff --git a/variants/thinknode_m3/platformio.ini b/variants/thinknode_m3/platformio.ini index 9571182d..f23ac87d 100644 --- a/variants/thinknode_m3/platformio.ini +++ b/variants/thinknode_m3/platformio.ini @@ -75,7 +75,6 @@ extends = ThinkNode_M3 board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld board_upload.maximum_size = 708608 build_flags = ${ThinkNode_M3.build_flags} - ${companion_rxps.build_flags} -I examples/companion_radio/ui-orig -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 @@ -99,7 +98,6 @@ extends = ThinkNode_M3 board_build.ldscript = boards/nrf52840_s140_v6_extrafs.ld board_upload.maximum_size = 708608 build_flags = ${ThinkNode_M3.build_flags} - ${companion_rxps.build_flags} -I examples/companion_radio/ui-orig -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 diff --git a/variants/xiao_nrf52/platformio.ini b/variants/xiao_nrf52/platformio.ini index 44fcc56f..f4d1b93e 100644 --- a/variants/xiao_nrf52/platformio.ini +++ b/variants/xiao_nrf52/platformio.ini @@ -46,7 +46,6 @@ board_build.ldscript = boards/nrf52840_s140_v7_extrafs.ld board_upload.maximum_size = 708608 build_flags = ${Xiao_nrf52.build_flags} - ${companion_rxps.build_flags} -I examples/companion_radio/ui-orig -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 @@ -70,7 +69,6 @@ board_build.ldscript = boards/nrf52840_s140_v7_extrafs.ld board_upload.maximum_size = 708608 build_flags = ${Xiao_nrf52.build_flags} - ${companion_rxps.build_flags} -I examples/companion_radio/ui-orig -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 diff --git a/variants/xiao_s3/platformio.ini b/variants/xiao_s3/platformio.ini index 47315b5f..b59e69ae 100644 --- a/variants/xiao_s3/platformio.ini +++ b/variants/xiao_s3/platformio.ini @@ -94,7 +94,6 @@ lib_deps = extends = Xiao_S3 build_flags = ${Xiao_S3.build_flags} - ${companion_rxps.build_flags} -I examples/companion_radio/ui-new -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 @@ -123,7 +122,6 @@ platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 extends = Xiao_S3 build_flags = ${Xiao_S3.build_flags} - ${companion_rxps.build_flags} -I examples/companion_radio/ui-new -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 diff --git a/variants/xiao_s3_wio/platformio.ini b/variants/xiao_s3_wio/platformio.ini index ca28a285..6686de55 100644 --- a/variants/xiao_s3_wio/platformio.ini +++ b/variants/xiao_s3_wio/platformio.ini @@ -132,7 +132,6 @@ lib_deps = extends = Xiao_S3_WIO build_flags = ${Xiao_S3_WIO.build_flags} - ${companion_rxps.build_flags} -I examples/companion_radio/ui-new -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 @@ -155,7 +154,6 @@ lib_deps = extends = Xiao_S3_WIO build_flags = ${Xiao_S3_WIO.build_flags} - ${companion_rxps.build_flags} -I examples/companion_radio/ui-new -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 @@ -184,7 +182,6 @@ platform_packages = framework-arduinoespressif32 @ symlink://D:/esp32-2.0.17 extends = Xiao_S3_WIO build_flags = ${Xiao_S3_WIO.build_flags} - ${companion_rxps.build_flags} -I examples/companion_radio/ui-new -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 @@ -213,7 +210,6 @@ build_src_filter = ${Xiao_S3_WIO.build_src_filter} +<../examples/companion_radio/*.cpp> build_flags = ${Xiao_S3_WIO.build_flags} - ${companion_rxps.build_flags} -I examples/companion_radio/ui-new -D MAX_CONTACTS=350 -D MAX_GROUP_CHANNELS=40 From d3539e88105b99a6544bf77428eee4726b3fb2de Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Wed, 5 Aug 2026 23:32:09 +0700 Subject: [PATCH 2/2] Fixed name for Heltec_t096_companion_radio_ble_femon --- build-iotthinks.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build-iotthinks.sh b/build-iotthinks.sh index f80a55be..85d681bd 100644 --- a/build-iotthinks.sh +++ b/build-iotthinks.sh @@ -77,7 +77,7 @@ Xiao_nrf52_room_server ############# Companions BLE ############# # NRF52 - 15 boards sh build.sh build-firmware \ -Heltec_t096_companion_radio_ble \ +Heltec_t096_companion_radio_ble_femon \ Heltec_t096_companion_radio_ble_femoff \ Heltec_t1_companion_radio_ble \ Heltec_t114_companion_radio_ble \ @@ -123,7 +123,7 @@ Heltec_t096_companion_radio_usb ############# Sample builds ############# # 14 boards sh build.sh build-firmware \ -Heltec_t096_companion_radio_ble \ +Heltec_t096_companion_radio_ble_femon \ Heltec_t096_companion_radio_ble_femoff \ Heltec_t096_repeater \ Heltec_v3_companion_radio_ble_ps \