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 ++; } }