From a7a4e52947b3f077547fd49016a79ea2a1d8f70b Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Mon, 20 Jul 2026 16:19:27 +0700 Subject: [PATCH] GPS PowerSaving v1.1: Added support for BLE companions --- examples/companion_radio/main.cpp | 12 ++++ examples/simple_repeater/main.cpp | 12 ++++ src/MeshCore.h | 9 +++ src/helpers/CommonCLI.cpp | 6 +- .../sensors/EnvironmentSensorManager.cpp | 64 +++++++++---------- .../sensors/EnvironmentSensorManager.h | 1 + src/helpers/sensors/LocationProvider.h | 26 +++++--- 7 files changed, 85 insertions(+), 45 deletions(-) diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index f10cb170..364a5bb5 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -234,6 +234,18 @@ void setup() { #error "need to define filesystem" #endif +#if ENV_INCLUDE_GPS == 1 + // Apply PowerSaving profile for GPS + if(sensors.getLocationProvider() != NULL) { + // Enable by default + sensors.powersaving_enabled = true; + sensors.getLocationProvider()->enablePowerSaving(true); + + // Set GPS on and off duration in seconds + sensors.getLocationProvider()->setPowerSavingProfile(600, 300); // Max 10 minutes, 5 minutes + } +#endif + sensors.begin(); #if ENV_INCLUDE_GPS == 1 diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index 82e2a212..b2c3d056 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -87,6 +87,18 @@ void setup() { command[0] = 0; +#if ENV_INCLUDE_GPS == 1 + // Apply PowerSaving profile for GPS + if (sensors.getLocationProvider() != NULL) { + // Let CLI "gps on" call setSettingValue to enable the PowerSaving mode + // sensors.powersaving_enabled = true; + // sensors.getLocationProvider()->enablePowerSaving(true); + + // GPS on and off duration in seconds + sensors.getLocationProvider()->setPowerSavingProfile(600, 86400); // Max 10 minutes, 1 day + } +#endif + sensors.begin(); the_mesh.begin(fs); diff --git a/src/MeshCore.h b/src/MeshCore.h index 4c23b415..c3f417a4 100644 --- a/src/MeshCore.h +++ b/src/MeshCore.h @@ -37,6 +37,15 @@ #define BRIDGE_DEBUG_PRINTLN(...) {} #endif +#if POWERSAVING_DEBUG && ARDUINO + #include + #define POWERSAVING_DEBUG_PRINT(F, ...) Serial.printf("POWERSAVING: " F, ##__VA_ARGS__) + #define POWERSAVING_DEBUG_PRINTLN(F, ...) Serial.printf("POWERSAVING: " F "\n", ##__VA_ARGS__) +#else + #define POWERSAVING_DEBUG_PRINT(...) {} + #define POWERSAVING_DEBUG_PRINTLN(...) {} +#endif + namespace mesh { #define BD_STARTUP_NORMAL 0 // getStartupReason() codes diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 77f7a13d..5e5cddb4 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -436,9 +436,9 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re int sats = l->satellitesCount(); bool active = !strcmp(_sensors->getSettingByKey("gps"), "1"); - if (_prefs->powersaving_enabled && l->getGPSPowerSaving()) { // GPS Power Saving + if (_prefs->powersaving_enabled && l->isPowerSavingEnabled()) { // GPS Power Saving if (enabled) { - unsigned long mins = (l->getNextGPSOff() - millis()) / 60000UL; + unsigned long mins = (l->getNextSleep() - millis()) / 60000UL; sprintf(reply, "on (powersaving, sleep in %luh %lum), %s, %s, %d sats", mins / 60UL, mins % 60UL, @@ -446,7 +446,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re fix ? "fix" : "no fix", sats); } else { - unsigned long mins = (l->getNextGPSOn() - millis()) / 60000UL; + unsigned long mins = (l->getNextWake() - millis()) / 60000UL; sprintf(reply, "off (powersaving, wake in %luh %lum)", mins / 60UL, mins % 60UL); diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index fc66651a..12f196b9 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -621,12 +621,6 @@ static const SensorDef SENSOR_TABLE[] = { static const size_t SENSOR_TABLE_SIZE = (sizeof(SENSOR_TABLE) / sizeof(SENSOR_TABLE[0])) - 1; -// ============================================================ -// Power Saving GPS -// ============================================================ -static unsigned long GPS_ON_DURATION_SECS = 600; // 10 minutes -static unsigned long GPS_OFF_DURATION_SECS = 86400; // 1 day - // ============================================================ // begin() — scan the I2C bus, then initialize only what was // found. A sensor whose address does not ACK during the scan @@ -737,13 +731,13 @@ bool EnvironmentSensorManager::setSettingValue(const char* name, const char* val if (gps_detected && strcmp(name, "gps") == 0) { if (strcmp(value, "0") == 0) { if (powersaving_enabled) { - _location->setGPSPowerSaving(false); + _location->enablePowerSaving(false); } stop_gps(); } else { if (powersaving_enabled) { - _location->setGPSPowerSaving(true); + _location->enablePowerSaving(true); } start_gps(); @@ -885,6 +879,13 @@ bool EnvironmentSensorManager::gpsIsAwake(uint8_t ioPin){ void EnvironmentSensorManager::start_gps() { gps_active = true; + + if (powersaving_enabled && _location->isPowerSavingEnabled()) { + gps_wake = true; // gps_active is true + _location->syncTime(); // Clear GPS data and force sync time + _location->setNextSleep(); // Next time to off + } + #ifdef RAK_WISBLOCK_GPS pinMode(gpsResetPin, OUTPUT); digitalWrite(gpsResetPin, HIGH); @@ -897,22 +898,19 @@ void EnvironmentSensorManager::start_gps() { #ifndef PIN_GPS_EN MESH_DEBUG_PRINTLN("Start GPS is N/A on this board. Actual GPS state unchanged"); #endif - - if (powersaving_enabled && _location->getGPSPowerSaving()) { - _location->syncTime(); // Clear GPS data and force sync time - _location->setNextGPSOff(millis() + GPS_ON_DURATION_SECS * 1000UL); // Next time to off - } } void EnvironmentSensorManager::stop_gps() { - gps_active = false; - - if (powersaving_enabled && _location->getGPSPowerSaving()) { + if (powersaving_enabled && _location->isPowerSavingEnabled()) { + gps_wake = false; // gps_active is unchanged (true) even the GPS sleep (e.g: off) _location->stopTimeSync(); // Stop time sync - _location->setNextGPSOn(millis() + GPS_OFF_DURATION_SECS * 1000UL); // Next time to on + _location->setNextWake(); // Next time to on + } else { + gps_active = false; + gps_wake = false; // When GPS is off, wake is false to be sure } -#ifdef RAK_WISBLOCK_GPS + #ifdef RAK_WISBLOCK_GPS pinMode(gpsResetPin, OUTPUT); digitalWrite(gpsResetPin, LOW); return; @@ -934,37 +932,35 @@ void EnvironmentSensorManager::loop() { // PowerSaving if (powersaving_enabled) { - if (gps_detected && _location->getGPSPowerSaving()) { - if (gps_active && ((int32_t)(millis() - _location->getNextGPSOff()) >= 0 || + if (gps_detected && _location->isPowerSavingEnabled()) { + if (gps_wake && ((int32_t)(millis() - _location->getNextSleep()) >= 0 || !_location->waitingTimeSync())) { // Time to off or GPS set - // --- TO REMOVE - if ((int32_t)(millis() - _location->getNextGPSOff()) >= 0) Serial.println("Timeout, off"); - else if (!_location->waitingTimeSync()) Serial.println("GPS set, off early"); - // --- TO REMOVE + if ((int32_t)(millis() - _location->getNextSleep()) >= 0) { + POWERSAVING_DEBUG_PRINTLN("GPS wake timeout. Enter sleep"); + } + else if (!_location->waitingTimeSync()) { + POWERSAVING_DEBUG_PRINTLN("GPS set. Enter sleep early"); + } stop_gps(); - } else if (!gps_active && ((int32_t)(millis() - _location->getNextGPSOn()) >= 0)) { // Time to on - // --- TO REMOVE - Serial.println("Timeout, on"); - // --- TO REMOVE + } else if (!gps_wake && ((int32_t)(millis() - _location->getNextWake()) >= 0)) { // Time to on + POWERSAVING_DEBUG_PRINTLN("GPS sleep timeout. Wakeup."); start_gps(); - } else if (!gps_active && _location->waitingTimeSync()) { // On for "gps sync" - // --- TO REMOVE - Serial.println("gps sync CLI, on"); - // --- TO REMOVE + } else if (!gps_wake && _location->waitingTimeSync()) { // On for "gps sync" + POWERSAVING_DEBUG_PRINTLN("CLI gps sync. Wakeup"); start_gps(); } } } - if (gps_active) { + if ((!powersaving_enabled && gps_active) || (powersaving_enabled && gps_wake)) { _location->loop(); } if ((int32_t)(millis() - next_gps_update) >= 0) { - if(gps_active){ + if((!powersaving_enabled && gps_active) || (powersaving_enabled && gps_wake)){ #ifdef RAK_WISBLOCK_GPS if ((i2cGPSFlag || serialGPSFlag) && _location->isValid()) { node_lat = ((double)_location->getLatitude())/1000000.; diff --git a/src/helpers/sensors/EnvironmentSensorManager.h b/src/helpers/sensors/EnvironmentSensorManager.h index 29147c89..40c44e90 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.h +++ b/src/helpers/sensors/EnvironmentSensorManager.h @@ -21,6 +21,7 @@ protected: bool gps_detected = false; bool gps_active = false; + bool gps_wake = false; // In PowerSaving, gps_active can have gps_wake true or false uint32_t gps_update_interval_sec = 1; #if ENV_INCLUDE_GPS diff --git a/src/helpers/sensors/LocationProvider.h b/src/helpers/sensors/LocationProvider.h index 34c2fc2e..81075040 100644 --- a/src/helpers/sensors/LocationProvider.h +++ b/src/helpers/sensors/LocationProvider.h @@ -6,22 +6,32 @@ class LocationProvider { protected: bool _time_sync_needed = true; + bool powersaving_enabled = false; - unsigned long _next_gps_off = 0; - unsigned long _next_gps_on = 0; + unsigned long _wake_duration_secs = 86400; // Full day + unsigned long _sleep_duration_secs = 0; // No off + unsigned long _next_wake = 0; + unsigned long _next_sleep = 0; unsigned long _last_valid_time_sync = 0; public: virtual void syncTime() { _time_sync_needed = true; } virtual bool waitingTimeSync() { return _time_sync_needed; } virtual void stopTimeSync() { _time_sync_needed = false; } - virtual void setGPSPowerSaving(bool enabled) { powersaving_enabled = enabled; _next_gps_off = 0; _next_gps_on = 0; } - virtual bool getGPSPowerSaving() { return powersaving_enabled; } - virtual void setNextGPSOff(unsigned long _millis) { _next_gps_off = _millis; } - virtual unsigned long getNextGPSOff() { return _next_gps_off; } - virtual void setNextGPSOn(unsigned long _millis) { _next_gps_on = _millis; } - virtual unsigned long getNextGPSOn() { return _next_gps_on; } + + virtual void setPowerSavingProfile(unsigned long wake_duration_secs, unsigned long sleep_duration_secs) { + _wake_duration_secs = wake_duration_secs; + _sleep_duration_secs = sleep_duration_secs; + } + + virtual void enablePowerSaving(bool enabled) { powersaving_enabled = enabled; _next_wake = 0; _next_sleep = 0; } + virtual bool isPowerSavingEnabled() { return powersaving_enabled; } + virtual void setNextWake() { _next_wake = millis() + _sleep_duration_secs * 1000UL; } + virtual unsigned long getNextWake() { return _next_wake; } + virtual void setNextSleep() { _next_sleep = millis() + _wake_duration_secs * 1000UL; } + virtual unsigned long getNextSleep() { return _next_sleep; } virtual unsigned long getLastValidTimeSync() { return _last_valid_time_sync; } + virtual long getLatitude() = 0; virtual long getLongitude() = 0; virtual long getAltitude() = 0;