From 16fbf335825e98875cdd7b5325af182d0f613499 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Thu, 16 Jul 2026 18:48:39 +0700 Subject: [PATCH 1/4] PowerSaving GPS version 1 for repeaters --- examples/simple_repeater/MyMesh.h | 7 +++ src/helpers/CommonCLI.cpp | 45 ++++++++++--- src/helpers/SensorManager.h | 3 +- .../sensors/EnvironmentSensorManager.cpp | 63 ++++++++++++++++++- src/helpers/sensors/LocationProvider.h | 12 ++++ .../sensors/MicroNMEALocationProvider.h | 5 +- 6 files changed, 120 insertions(+), 15 deletions(-) diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index c2d662a2..5ee85980 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -162,6 +162,13 @@ protected: #if ENV_INCLUDE_GPS == 1 void applyGpsPrefs() { + // If powersaving on, apply powersaving to sensors + if (_prefs.powersaving_enabled) { + sensors.powersaving_enabled = true; + } else { + sensors.powersaving_enabled = false; + } + sensors.setSettingValue("gps", _prefs.gps_enabled?"1":"0"); } #endif diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 23efe7fa..0f1fa699 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -368,7 +368,12 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re if (_sensors->setSettingValue("gps", "1")) { _prefs->gps_enabled = 1; savePrefs(); - strcpy(reply, "ok"); + + if (_prefs->powersaving_enabled) { // Power Saving + strcpy(reply, "on (powersaving)"); + } else { // Normal mode + strcpy(reply, "ok"); + } } else { strcpy(reply, "gps toggle not found"); } @@ -384,7 +389,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re LocationProvider * l = _sensors->getLocationProvider(); if (l != NULL) { l->syncTime(); - strcpy(reply, "ok"); + strcpy(reply, "scheduled"); } else { strcpy(reply, "gps provider not found"); } @@ -430,13 +435,35 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re 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); - } else { - strcpy(reply, "off"); + + if (_prefs->powersaving_enabled && l->getGPSPowerSaving()) { // GPS Power Saving + if (enabled) { + sprintf(reply, "on (powersaving, sleep in %lum), %s, %s, %d sats", + (l->getNextGPSOff() - millis()) / 60000UL, + active?"active":"deactivated", + fix?"fix":"no fix", + sats); + } else { + sprintf(reply, "off (powersaving, wake in %lum)", (l->getNextGPSOn() - millis()) / 60000UL); + } + + // "last sync" from GPS + DateTime dt = DateTime(l->getLastValidTimeSync()); + if (dt.unixtime() == 0) { + sprintf(reply + strlen(reply), ", last sync: none"); + } else { + sprintf(reply + strlen(reply), ", last sync: %02d:%02d - %d/%d/%d UTC", dt.hour(), dt.minute(), + dt.day(), dt.month(), dt.year()); + } + } else { // Normal mode + if (enabled) { + sprintf(reply, "on, %s, %s, %d sats", + active?"active":"deactivated", + fix?"fix":"no fix", + sats); + } else { + strcpy(reply, "off"); + } } } else { strcpy(reply, "Can't find GPS"); diff --git a/src/helpers/SensorManager.h b/src/helpers/SensorManager.h index 89a174c2..2031bbc5 100644 --- a/src/helpers/SensorManager.h +++ b/src/helpers/SensorManager.h @@ -13,8 +13,9 @@ class SensorManager { public: double node_lat, node_lon; // modify these, if you want to affect Advert location double node_altitude; // altitude in meters + bool powersaving_enabled; // powersaving mode - SensorManager() { node_lat = 0; node_lon = 0; node_altitude = 0; } + SensorManager() { node_lat = 0; node_lon = 0; node_altitude = 0; powersaving_enabled = false; } virtual bool begin() { return false; } virtual bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) { return false; } virtual void loop() { } diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index ea57677f..fc66651a 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -621,6 +621,12 @@ 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 @@ -730,8 +736,16 @@ 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) { + if (powersaving_enabled) { + _location->setGPSPowerSaving(false); + } + stop_gps(); } else { + if (powersaving_enabled) { + _location->setGPSPowerSaving(true); + } + start_gps(); } return true; @@ -883,11 +897,22 @@ 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; - #ifdef RAK_WISBLOCK_GPS + + if (powersaving_enabled && _location->getGPSPowerSaving()) { + _location->stopTimeSync(); // Stop time sync + _location->setNextGPSOn(millis() + GPS_OFF_DURATION_SECS * 1000UL); // Next time to on + } + +#ifdef RAK_WISBLOCK_GPS pinMode(gpsResetPin, OUTPUT); digitalWrite(gpsResetPin, LOW); return; @@ -906,11 +931,39 @@ void EnvironmentSensorManager::loop() { #if ENV_INCLUDE_GPS static long next_gps_update = 0; + + // PowerSaving + if (powersaving_enabled) { + if (gps_detected && _location->getGPSPowerSaving()) { + if (gps_active && ((int32_t)(millis() - _location->getNextGPSOff()) >= 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 + + stop_gps(); + } else if (!gps_active && ((int32_t)(millis() - _location->getNextGPSOn()) >= 0)) { // Time to on + // --- TO REMOVE + Serial.println("Timeout, on"); + // --- TO REMOVE + + start_gps(); + } else if (!gps_active && _location->waitingTimeSync()) { // On for "gps sync" + // --- TO REMOVE + Serial.println("gps sync CLI, on"); + // --- TO REMOVE + + start_gps(); + } + } + } + if (gps_active) { _location->loop(); } - if (millis() > next_gps_update) { + if ((int32_t)(millis() - next_gps_update) >= 0) { if(gps_active){ #ifdef RAK_WISBLOCK_GPS if ((i2cGPSFlag || serialGPSFlag) && _location->isValid()) { @@ -929,8 +982,12 @@ void EnvironmentSensorManager::loop() { MESH_DEBUG_PRINTLN("lat %f lon %f alt %f", node_lat, node_lon, node_altitude); } #endif + + // In powersaving mode, GPS is on and off. Only update data when GPS is on + if(powersaving_enabled) next_gps_update = millis() + (gps_update_interval_sec * 1000); } - next_gps_update = millis() + (gps_update_interval_sec * 1000); + + if(!powersaving_enabled) next_gps_update = millis() + (gps_update_interval_sec * 1000); } #endif #if ENV_INCLUDE_BME680_BSEC diff --git a/src/helpers/sensors/LocationProvider.h b/src/helpers/sensors/LocationProvider.h index 81d08652..b59dd868 100644 --- a/src/helpers/sensors/LocationProvider.h +++ b/src/helpers/sensors/LocationProvider.h @@ -6,10 +6,22 @@ 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 _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 unsigned long setNextGPSOff(unsigned long _millis) { _next_gps_off = _millis; } + virtual unsigned long getNextGPSOff() { return _next_gps_off; } + virtual unsigned long setNextGPSOn(unsigned long _millis) { _next_gps_on = _millis; } + virtual unsigned long getNextGPSOn() { return _next_gps_on; } + virtual unsigned long getLastValidTimeSync() { return _last_valid_time_sync; } virtual long getLatitude() = 0; virtual long getLongitude() = 0; virtual long getAltitude() = 0; diff --git a/src/helpers/sensors/MicroNMEALocationProvider.h b/src/helpers/sensors/MicroNMEALocationProvider.h index eec466d3..a670b442 100644 --- a/src/helpers/sensors/MicroNMEALocationProvider.h +++ b/src/helpers/sensors/MicroNMEALocationProvider.h @@ -120,7 +120,7 @@ public : return alt; } long satellitesCount() override { return nmea.getNumSatellites(); } - bool isValid() override { return nmea.isValid(); } + bool isValid() override { return nmea.isValid() && nmea.getYear() < 2030; } // TODO: Pending better validation long getTimestamp() override { DateTime dt(nmea.getYear(), nmea.getMonth(),nmea.getDay(),nmea.getHour(),nmea.getMinute(),nmea.getSecond()); @@ -154,9 +154,10 @@ public : _clock->setCurrentTime(getTimestamp()); _time_sync_needed = false; _last_time_sync = millis(); + _last_valid_time_sync = _clock->getCurrentTime(); } } - if (isValid()) { + if (isValid() && satellitesCount() >= 5) { time_valid ++; } } From cd8711f50734cf595437e4669a20eb42c923b7a5 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Thu, 16 Jul 2026 19:31:39 +0700 Subject: [PATCH 2/4] Show hours and minutes in PowerSaving GPS and add _sensors->powersaving_enabled --- src/helpers/CommonCLI.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index 0f1fa699..77f7a13d 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -438,13 +438,18 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re if (_prefs->powersaving_enabled && l->getGPSPowerSaving()) { // GPS Power Saving if (enabled) { - sprintf(reply, "on (powersaving, sleep in %lum), %s, %s, %d sats", - (l->getNextGPSOff() - millis()) / 60000UL, - active?"active":"deactivated", - fix?"fix":"no fix", + unsigned long mins = (l->getNextGPSOff() - millis()) / 60000UL; + sprintf(reply, "on (powersaving, sleep in %luh %lum), %s, %s, %d sats", + mins / 60UL, + mins % 60UL, + active ? "active" : "deactivated", + fix ? "fix" : "no fix", sats); } else { - sprintf(reply, "off (powersaving, wake in %lum)", (l->getNextGPSOn() - millis()) / 60000UL); + unsigned long mins = (l->getNextGPSOn() - millis()) / 60000UL; + sprintf(reply, "off (powersaving, wake in %luh %lum)", + mins / 60UL, + mins % 60UL); } // "last sync" from GPS @@ -472,10 +477,12 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re } else if (memcmp(command, "powersaving on", 14) == 0) { #if defined(NRF52_PLATFORM) _prefs->powersaving_enabled = 1; + _sensors->powersaving_enabled = 1; savePrefs(); strcpy(reply, "on - Immediate effect"); #elif defined(ESP32) && !defined(WITH_BRIDGE) _prefs->powersaving_enabled = 1; + _sensors->powersaving_enabled = 1; savePrefs(); strcpy(reply, "on - After 2 minutes"); #elif defined(WITH_BRIDGE) @@ -485,6 +492,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re #endif } else if (memcmp(command, "powersaving off", 15) == 0) { _prefs->powersaving_enabled = 0; + _sensors->powersaving_enabled = 0; savePrefs(); strcpy(reply, "off"); } else if (memcmp(command, "powersaving", 11) == 0) { From 93b516ab3521db1d057e6dc01c153a57e779361d Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Thu, 16 Jul 2026 21:46:06 +0700 Subject: [PATCH 3/4] Added I2C support at Grove SDA/SDL for Wio L1 Tracker repeater and companion. --- variants/wio-tracker-l1/platformio.ini | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/variants/wio-tracker-l1/platformio.ini b/variants/wio-tracker-l1/platformio.ini index 7bb175bb..c1fe6c62 100644 --- a/variants/wio-tracker-l1/platformio.ini +++ b/variants/wio-tracker-l1/platformio.ini @@ -36,6 +36,8 @@ build_flags = -D ADMIN_PASSWORD='"password"' -D MAX_NEIGHBOURS=50 -D DISPLAY_CLASS=SH1106Display + -D ENV_PIN_SDA=PIN_WIRE1_SDA + -D ENV_PIN_SCL=PIN_WIRE1_SCL ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 lib_deps = ${WioTrackerL1.lib_deps} @@ -50,6 +52,8 @@ build_flags = ${WioTrackerL1.build_flags} -D ADMIN_PASSWORD='"password"' -D ROOM_PASSWORD='"hello"' -D DISPLAY_CLASS=SH1106Display + -D ENV_PIN_SDA=PIN_WIRE1_SDA + -D ENV_PIN_SCL=PIN_WIRE1_SCL ; -D MESH_PACKET_LOGGING=1 ; -D MESH_DEBUG=1 lib_deps = ${WioTrackerL1.lib_deps} From 38509583fae8ae6e615d98caed7c360d59e4ac59 Mon Sep 17 00:00:00 2001 From: Kevin Le Date: Thu, 16 Jul 2026 22:48:24 +0700 Subject: [PATCH 4/4] Fixed typo at Get methods for GPS --- src/helpers/sensors/LocationProvider.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/helpers/sensors/LocationProvider.h b/src/helpers/sensors/LocationProvider.h index b59dd868..34c2fc2e 100644 --- a/src/helpers/sensors/LocationProvider.h +++ b/src/helpers/sensors/LocationProvider.h @@ -17,9 +17,9 @@ public: 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 unsigned long setNextGPSOff(unsigned long _millis) { _next_gps_off = _millis; } + virtual void setNextGPSOff(unsigned long _millis) { _next_gps_off = _millis; } virtual unsigned long getNextGPSOff() { return _next_gps_off; } - virtual unsigned long setNextGPSOn(unsigned long _millis) { _next_gps_on = _millis; } + virtual void setNextGPSOn(unsigned long _millis) { _next_gps_on = _millis; } virtual unsigned long getNextGPSOn() { return _next_gps_on; } virtual unsigned long getLastValidTimeSync() { return _last_valid_time_sync; } virtual long getLatitude() = 0;