From ec7a1bc719069a4cfe0781dc78083dfb16837b05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jaroslav=20=C5=A0karvada?= Date: Fri, 16 Oct 2026 20:53:01 +0200 Subject: [PATCH] simple_repeater: added 'gps sleep' CLI command Powers off the GPS for the specified interval (in seconds) to save power. The GPS remains powered on for GPS_WAIT_SECONDS_WITH_VALID_FIX (default: 10 seconds with a valid fix) or GPS_TIMEOUT_SECONDS (default: 10 minutes) if no valid fix is acquired within the whole GPS_WAIT_SECONDS_WITH_VALID_FIX period. After this, the GPS powers off for the specified sleep interval and the cycle repeats. The GPS sleep interval is independent of other intervals. For example, if set to 10 minutes and the GPS acquires a valid fix, it stays powered on for 10 seconds and then turns off for 10 minutes. If a time sync event occurs after TIME_SYNC_INTERVAL (originally hardcoded to 30 minutes), time synchronization takes place at the next interval when the GPS is powered on (i.e., at most 10 minutes after the event is triggered in this case). CLI examples: Query GPS sleep interval: > gps sleep Set GPS sleep interval to 0 (i.e. disable this feature, behaviour like without this patch): > gps sleep 0 Set GPS sleep interval to 10 seconds: > gps sleep 10 Switch GPS on daily for 10 seconds (hardcoded) to synchronize the clock then power it off for 24 hours (86400 seconds) to save power: > gps sleep 86400 > gps on --- examples/simple_repeater/MyMesh.h | 5 ++ src/helpers/CommonCLI.cpp | 25 +++++-- src/helpers/CommonCLI.h | 2 + .../sensors/EnvironmentSensorManager.cpp | 72 ++++++++++++++----- .../sensors/EnvironmentSensorManager.h | 3 + .../sensors/MicroNMEALocationProvider.h | 1 + 6 files changed, 84 insertions(+), 24 deletions(-) diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index dc89325861c..df2ff974fa9 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -160,6 +160,11 @@ class MyMesh : public mesh::Mesh, public CommonCLICallbacks { #if ENV_INCLUDE_GPS == 1 void applyGpsPrefs() { sensors.setSettingValue("gps", _prefs.gps_enabled?"1":"0"); + if (_prefs.gps_sleep_interval > 0) { + char interval_str[11]; // 4 294 967 295 (10 digits + null) + sprintf(interval_str, "%u", _prefs.gps_sleep_interval); + sensors.setSettingValue("gps_sleep_interval", interval_str); + } } #endif diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 04199e24d1d..3c495545aa1 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -102,7 +102,8 @@ void CommonCLI::loadPrefsInt(FILESYSTEM* fs, const char* filename) { // Legacy file.read((uint8_t *)&_prefs->flood_max_advert, sizeof(_prefs->flood_max_advert)); // 292 file.read((uint8_t *)&_prefs->radio_fem_rxgain, sizeof(_prefs->radio_fem_rxgain)); // 293 file.read((uint8_t *)&_prefs->cad_enabled, sizeof(_prefs->cad_enabled)); // 294 - // next: 295 + file.read((uint8_t *)&_prefs->gps_sleep_interval, sizeof(_prefs->gps_sleep_interval)); // 295 + // next: 299 // sanitise bad pref values _prefs->rx_delay_base = constrain(_prefs->rx_delay_base, 0, 20.0f); @@ -360,6 +361,15 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re } else { strcpy(reply, "gps toggle not found"); } + } else if (memcmp(command, "gps sleep", 9) == 0) { + if (strlen(command) == 9) { + sprintf(reply, "%u", _prefs->gps_sleep_interval); + } else if (strlen(command) > 9) { + _sensors->setSettingValue("gps_sleep_interval", command + 10); + _prefs->gps_sleep_interval = _atoi(command + 10); + savePrefs(); + strcpy(reply, "ok"); + } } else if (memcmp(command, "gps sync", 8) == 0) { LocationProvider * l = _sensors->getLocationProvider(); if (l != NULL) { @@ -420,15 +430,18 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re } else if (memcmp(command, "gps", 3) == 0) { LocationProvider * l = _sensors->getLocationProvider(); if (l != NULL) { - bool enabled = l->isEnabled(); // is EN pin on ? + bool enabled = _prefs->gps_enabled; bool fix = l->isValid(); // has fix ? int sats = l->satellitesCount(); bool active = !strcmp(_sensors->getSettingByKey("gps"), "1"); if (enabled) { - sprintf(reply, "on, %s, %s, %d sats", - active?"active":"deactivated", - fix?"fix":"no fix", - sats); + if (active) { + sprintf(reply, "on, active, %s, %d sats", + fix?"fix":"no fix", + sats); + } else { + strcpy(reply, "on, deactivated"); + } } else { strcpy(reply, "off"); } diff --git a/src/helpers/CommonCLI.h b/src/helpers/CommonCLI.h index 2759b0c4f0e..b69a3f51f02 100644 --- a/src/helpers/CommonCLI.h +++ b/src/helpers/CommonCLI.h @@ -61,6 +61,7 @@ class NodePrefs : public ConfigSerializer { // Gps settings uint8_t gps_enabled = 0; uint32_t gps_interval = 0; // in seconds + uint32_t gps_sleep_interval = 0; // in seconds uint8_t advert_loc_policy = 0; uint32_t discovery_mod_timestamp = 0; float adc_multiplier = 0; @@ -159,6 +160,7 @@ class NodePrefs : public ConfigSerializer { def("en", _parent->gps_enabled); // boolean def("int", _parent->gps_interval); // interval in seconds def("adv_loc", _parent->advert_loc_policy); + def("sleep", _parent->gps_sleep_interval); } public: GPSPrefs(NodePrefs* parent) : _parent(parent) { } diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index 0dc892c3a52..88a32b960a1 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -2,6 +2,11 @@ #include +// number of seconds to wait with the valid GPS fix before the GPS is powered down +#define GPS_WAIT_SECONDS_WITH_VALID_FIX 10 +// how long to wait for the GPS fix before giving up +#define GPS_TIMEOUT_SECONDS 600 + #if ENV_PIN_SDA && ENV_PIN_SCL #define TELEM_WIRE &Wire1 // Use Wire1 as the I2C bus for Environment Sensors #else @@ -731,8 +736,10 @@ bool EnvironmentSensorManager::setSettingValue(const char* name, const char* val #if ENV_INCLUDE_GPS if (gps_detected && strcmp(name, "gps") == 0) { if (strcmp(value, "0") == 0) { + gps_enabled = false; stop_gps(); } else { + gps_enabled = true; start_gps(); } return true; @@ -742,6 +749,11 @@ bool EnvironmentSensorManager::setSettingValue(const char* name, const char* val gps_update_interval_sec = interval_seconds > 0 ? interval_seconds : 1; return true; } + if (strcmp(name, "gps_sleep_interval") == 0) { + gps_sleep_interval_sec = atoi(value); + next_gps_wakeup = 0; + return true; + } #endif return false; // not supported } @@ -927,30 +939,54 @@ void EnvironmentSensorManager::stop_gps() { void EnvironmentSensorManager::loop() { #if ENV_INCLUDE_GPS + static bool valid_fix = false; + static unsigned long valid_fix_time = 0; static unsigned long next_gps_update = 0; + if (gps_active) { _location->loop(); } - if ((long)(millis() - next_gps_update) > 0) { + if (gps_enabled && gps_sleep_interval_sec > 0 && (long)(millis() - next_gps_wakeup) > 0) { + if (gps_active) { + if ((valid_fix && (long)(millis() - valid_fix_time) >= GPS_WAIT_SECONDS_WITH_VALID_FIX * 1000) || \ + (long)(millis() - next_gps_wakeup) > GPS_TIMEOUT_SECONDS * 1000) { + next_gps_wakeup = millis() + (gps_sleep_interval_sec * 1000); + stop_gps(); + } else { + if (_location->isValid()) { + if (!valid_fix) { + valid_fix_time = millis(); + valid_fix = true; + } + } else { + valid_fix = false; + } + } + } else { + start_gps(); + valid_fix = false; + } + } + if ((long)(millis() - next_gps_update) > 0) { if(gps_active){ - #ifdef RAK_WISBLOCK_GPS - if ((i2cGPSFlag || serialGPSFlag) && _location->isValid()) { - node_lat = ((double)_location->getLatitude())/1000000.; - node_lon = ((double)_location->getLongitude())/1000000.; - MESH_DEBUG_PRINTLN("lat %f lon %f", node_lat, node_lon); - node_altitude = ((double)_location->getAltitude()) / 1000.0; - MESH_DEBUG_PRINTLN("lat %f lon %f alt %f", node_lat, node_lon, node_altitude); - } - #else - if (_location->isValid()) { - node_lat = ((double)_location->getLatitude())/1000000.; - node_lon = ((double)_location->getLongitude())/1000000.; - MESH_DEBUG_PRINTLN("lat %f lon %f", node_lat, node_lon); - node_altitude = ((double)_location->getAltitude()) / 1000.0; - MESH_DEBUG_PRINTLN("lat %f lon %f alt %f", node_lat, node_lon, node_altitude); - } - #endif + #ifdef RAK_WISBLOCK_GPS + if ((i2cGPSFlag || serialGPSFlag) && _location->isValid()) { + node_lat = ((double)_location->getLatitude())/1000000.; + node_lon = ((double)_location->getLongitude())/1000000.; + MESH_DEBUG_PRINTLN("lat %f lon %f", node_lat, node_lon); + node_altitude = ((double)_location->getAltitude()) / 1000.0; + MESH_DEBUG_PRINTLN("lat %f lon %f alt %f", node_lat, node_lon, node_altitude); + } + #else + if (_location->isValid()) { + node_lat = ((double)_location->getLatitude())/1000000.; + node_lon = ((double)_location->getLongitude())/1000000.; + MESH_DEBUG_PRINTLN("lat %f lon %f", node_lat, node_lon); + node_altitude = ((double)_location->getAltitude()) / 1000.0; + MESH_DEBUG_PRINTLN("lat %f lon %f alt %f", node_lat, node_lon, node_altitude); + } + #endif } next_gps_update = millis() + (gps_update_interval_sec * 1000); } diff --git a/src/helpers/sensors/EnvironmentSensorManager.h b/src/helpers/sensors/EnvironmentSensorManager.h index 17706cbc492..537e7273ade 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.h +++ b/src/helpers/sensors/EnvironmentSensorManager.h @@ -22,8 +22,11 @@ class EnvironmentSensorManager : public SensorManager { bool gps_detected = false; bool gps_active = false; uint32_t gps_update_interval_sec = 1; + uint32_t gps_sleep_interval_sec = 0; #if ENV_INCLUDE_GPS + bool gps_enabled = false; + unsigned long next_gps_wakeup = 0; LocationProvider* _location; void start_gps(); void stop_gps(); diff --git a/src/helpers/sensors/MicroNMEALocationProvider.h b/src/helpers/sensors/MicroNMEALocationProvider.h index 37ef5762c48..087041b3b84 100644 --- a/src/helpers/sensors/MicroNMEALocationProvider.h +++ b/src/helpers/sensors/MicroNMEALocationProvider.h @@ -113,6 +113,7 @@ public : } void begin() override { + nmea.clear(); _begin_calls++; claim(); if (_pin_en != -1) {