diff --git a/examples/simple_repeater/MyMesh.h b/examples/simple_repeater/MyMesh.h index 7fae78ba..2e453fc6 100644 --- a/examples/simple_repeater/MyMesh.h +++ b/examples/simple_repeater/MyMesh.h @@ -522,6 +522,7 @@ protected: #if ENV_INCLUDE_GPS == 1 void applyGpsPrefs() { + sensors.setPowerSavingEnabled(_prefs.powersaving_enabled != 0); sensors.setSettingValue("gps", _prefs.gps_enabled?"1":"0"); } #endif diff --git a/src/helpers/CommonCLI.cpp b/src/helpers/CommonCLI.cpp index fd7d2ae2..9fbac89a 100644 --- a/src/helpers/CommonCLI.cpp +++ b/src/helpers/CommonCLI.cpp @@ -1958,7 +1958,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"); } @@ -1974,7 +1979,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"); } @@ -2019,14 +2024,42 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re bool enabled = l->isEnabled(); // is EN pin on ? 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"); + const char* gps_setting = _sensors->getSettingByKey("gps"); + bool active = gps_setting != NULL && strcmp(gps_setting, "1") == 0; + + if (_prefs->powersaving_enabled && l->getGPSPowerSaving()) { // GPS Power Saving + unsigned long now = millis(); + unsigned long next_off = l->getNextGPSOff(); + unsigned long deadline = next_off != 0 ? next_off : l->getNextGPSOn(); + long remaining_ms = deadline == 0 ? 0 : (long)(deadline - now); + unsigned long mins = remaining_ms > 0 ? (unsigned long)remaining_ms / 60000UL : 0; + if (next_off != 0) { + snprintf(reply, 160, "on (powersaving, sleep in %luh %lum), %s, %s, %d sats", + mins / 60UL, mins % 60UL, active ? "active" : "deactivated", + fix ? "fix" : "no fix", sats); + } else { + snprintf(reply, 160, "off (powersaving, wake in %luh %lum)", mins / 60UL, mins % 60UL); + } + + // "last sync" from GPS + unsigned long last_sync = l->getLastValidTimeSync(); + size_t used = strlen(reply); + if (last_sync == 0) { + snprintf(reply + used, 160 - used, ", last sync: none"); + } else { + DateTime dt = DateTime(last_sync); + snprintf(reply + used, 160 - used, ", 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"); @@ -2038,6 +2071,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re strcpy(reply, "Error: USB serial connected"); } else { _prefs->powersaving_enabled = 1; + _sensors->setPowerSavingEnabled(true); savePrefs(); strcpy(reply, "on - Immediate effect"); } @@ -2046,6 +2080,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, char* command, char* re strcpy(reply, "Error: USB serial connected"); } else { _prefs->powersaving_enabled = 1; + _sensors->setPowerSavingEnabled(true); savePrefs(); strcpy(reply, "on - After 2 minutes"); } @@ -2056,6 +2091,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->setPowerSavingEnabled(false); savePrefs(); strcpy(reply, "off"); } else if (memcmp(command, "powersaving", 11) == 0) { diff --git a/src/helpers/SensorManager.h b/src/helpers/SensorManager.h index 15cdebdf..77406348 100644 --- a/src/helpers/SensorManager.h +++ b/src/helpers/SensorManager.h @@ -55,8 +55,9 @@ protected: 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 i2c_probe(TwoWire& wire, uint8_t addr) { return false; } virtual bool begin() { return false; } virtual bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) { return false; } @@ -67,6 +68,7 @@ public: virtual const char* getSettingValue(int i) const { return NULL; } virtual bool setSettingValue(const char* name, const char* value) { return false; } virtual LocationProvider* getLocationProvider() { return NULL; } + virtual void setPowerSavingEnabled(bool enabled) { powersaving_enabled = enabled; } // Helper functions to manage setting by keys (useful in many places ...) const char* getSettingByKey(const char* key) { diff --git a/src/helpers/nrf52/SerialEthernetInterface.cpp b/src/helpers/nrf52/SerialEthernetInterface.cpp index 70891023..7e7b29a5 100644 --- a/src/helpers/nrf52/SerialEthernetInterface.cpp +++ b/src/helpers/nrf52/SerialEthernetInterface.cpp @@ -1,3 +1,5 @@ +#ifdef ETHERNET_ENABLED + #include "SerialEthernetInterface.h" #include "EthernetMac.h" #include @@ -116,7 +118,17 @@ size_t SerialEthernetInterface::writeFrame(const uint8_t src[], size_t len) { } bool SerialEthernetInterface::isWriteBusy() const { - return false; + return send_queue_len >= (FRAME_QUEUE_SIZE * 2 / 3); +} + +bool SerialEthernetInterface::isReadBusy() const { + return _state != RECV_STATE_IDLE; +} + +bool SerialEthernetInterface::hasPendingIO() const { + // The W5100S path is polled and has no wake interrupt. Keep servicing it + // while a client is connected or either direction has queued frame state. + return deviceConnected || send_queue_len > 0 || _state != RECV_STATE_IDLE; } size_t SerialEthernetInterface::checkRecvFrame(uint8_t dest[]) { @@ -262,3 +274,5 @@ bool SerialEthernetInterface::isConnected() const { void SerialEthernetInterface::loop() { Ethernet.maintain(); } + +#endif // ETHERNET_ENABLED diff --git a/src/helpers/nrf52/SerialEthernetInterface.h b/src/helpers/nrf52/SerialEthernetInterface.h index 95ce8a52..fc56faeb 100644 --- a/src/helpers/nrf52/SerialEthernetInterface.h +++ b/src/helpers/nrf52/SerialEthernetInterface.h @@ -1,5 +1,7 @@ #pragma once +#ifdef ETHERNET_ENABLED + #include "helpers/BaseSerialInterface.h" #include #include @@ -7,6 +9,7 @@ #ifndef ETHERNET_TCP_PORT #define ETHERNET_TCP_PORT 5000 #endif + // define ETHERNET_RAW_LINE=1 to use raw line-based CLI instead of framed packets class SerialEthernetInterface : public BaseSerialInterface { @@ -57,7 +60,9 @@ class SerialEthernetInterface : public BaseSerialInterface { bool isEnabled() const override { return _isEnabled; } bool isConnected() const override; + bool isReadBusy() const override; bool isWriteBusy() const override; + bool hasPendingIO() const override; size_t writeFrame(const uint8_t src[], size_t len) override; size_t checkRecvFrame(uint8_t dest[]) override; @@ -76,3 +81,5 @@ class SerialEthernetInterface : public BaseSerialInterface { #define ETHERNET_DEBUG_PRINTLN(...) {} #define ETHERNET_DEBUG_PRINT_IP(...) {} #endif + +#endif // ETHERNET_ENABLED diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index 7810f993..8c472cc9 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -627,6 +627,18 @@ static const SensorDef SENSOR_TABLE[] = { static const size_t SENSOR_TABLE_SIZE = (sizeof(SENSOR_TABLE) / sizeof(SENSOR_TABLE[0])) - 1; +// ============================================================ +// Power Saving GPS +// ============================================================ +#if ENV_INCLUDE_GPS +#ifndef GPS_POWERSAVING_ON_DURATION_SECS +#define GPS_POWERSAVING_ON_DURATION_SECS (10UL * 60UL) +#endif +#ifndef GPS_POWERSAVING_OFF_DURATION_SECS +#define GPS_POWERSAVING_OFF_DURATION_SECS (24UL * 60UL * 60UL) +#endif +#endif + // ============================================================ // begin() — scan the I2C bus, then initialize only what was // found. A sensor whose address does not ACK during the scan @@ -735,7 +747,13 @@ const char* EnvironmentSensorManager::getSettingValue(int i) const { bool EnvironmentSensorManager::setSettingValue(const char* name, const char* value) { #if ENV_INCLUDE_GPS if (gps_detected && strcmp(name, "gps") == 0) { - setGpsTelemetryUserEnabled(strcmp(value, "0") != 0); + bool enabled = strcmp(value, "0") != 0; + bool was_active = gps_active; + _location->setGPSPowerSaving(enabled && powersaving_enabled); + setGpsTelemetryUserEnabled(enabled); + if (enabled && powersaving_enabled && was_active) { + armGpsPowerSavingCycle(); + } return true; } if (strcmp(name, "gps_interval") == 0) { @@ -747,6 +765,29 @@ bool EnvironmentSensorManager::setSettingValue(const char* name, const char* val return false; // not supported } +void EnvironmentSensorManager::setPowerSavingEnabled(bool enabled) { + if (powersaving_enabled == enabled) return; + powersaving_enabled = enabled; + + #if ENV_INCLUDE_GPS + if (!gps_detected) return; + bool gps_user_enabled = isGpsTelemetryUserEnabled(); + _location->setGPSPowerSaving(enabled && gps_user_enabled); + if (!gps_user_enabled) return; + + if (enabled) { + if (gps_active) { + armGpsPowerSavingCycle(); + } else { + start_gps(); + } + } else if (!gps_active) { + // Manual GPS-on means continuous operation when node power saving is off. + start_gps(); + } + #endif +} + #if ENV_INCLUDE_GPS void EnvironmentSensorManager::initBasicGPS() { @@ -879,26 +920,42 @@ bool EnvironmentSensorManager::gpsIsAwake(uint8_t ioPin){ } #endif +void EnvironmentSensorManager::armGpsPowerSavingCycle() { + if (!powersaving_enabled || !_location->getGPSPowerSaving()) return; + _location->syncTime(); + _location->setNextGPSOn(0); + _location->setNextGPSOff(millis() + GPS_POWERSAVING_ON_DURATION_SECS * 1000UL); +} + void EnvironmentSensorManager::start_gps() { if (gps_active) return; gps_active = true; #ifdef RAK_WISBLOCK_GPS - pinMode(gpsResetPin, OUTPUT); - digitalWrite(gpsResetPin, HIGH); - return; - #endif - + pinMode(gpsResetPin, OUTPUT); + digitalWrite(gpsResetPin, HIGH); + #else _location->begin(); _location->reset(); #ifndef PIN_GPS_EN MESH_DEBUG_PRINTLN("Start GPS is N/A on this board. Actual GPS state unchanged"); #endif + #endif + + armGpsPowerSavingCycle(); } void EnvironmentSensorManager::stop_gps() { + if (!gps_active) return; gps_active = false; - #ifdef RAK_WISBLOCK_GPS + + if (powersaving_enabled && _location->getGPSPowerSaving()) { + _location->stopTimeSync(); + _location->setNextGPSOff(0); + _location->setNextGPSOn(millis() + GPS_POWERSAVING_OFF_DURATION_SECS * 1000UL); + } + +#ifdef RAK_WISBLOCK_GPS pinMode(gpsResetPin, OUTPUT); digitalWrite(gpsResetPin, LOW); return; @@ -920,11 +977,22 @@ void EnvironmentSensorManager::loop() { unsigned long now = millis(); loopGpsTelemetry(now); + if (powersaving_enabled && gps_detected && _location->getGPSPowerSaving()) { + unsigned long next_off = _location->getNextGPSOff(); + unsigned long next_on = _location->getNextGPSOn(); + if (gps_active && ((next_off != 0 && (long)(now - next_off) >= 0) + || !_location->waitingTimeSync())) { + stop_gps(); + } else if (!gps_active && ((next_on != 0 && (long)(now - next_on) >= 0) + || _location->waitingTimeSync())) { + start_gps(); + } + } + if (gps_active) { _location->loop(); } if ((long)(now - next_gps_update) >= 0) { - if(gps_active){ #ifdef RAK_WISBLOCK_GPS if ((i2cGPSFlag || serialGPSFlag) && _location->isValid()) { @@ -951,6 +1019,7 @@ void EnvironmentSensorManager::loop() { processGpsTelemetryFix(gps_lat, gps_lon, gps_altitude, now); } #endif + } next_gps_update = now + (gps_update_interval_sec * 1000); } diff --git a/src/helpers/sensors/EnvironmentSensorManager.h b/src/helpers/sensors/EnvironmentSensorManager.h index 9b7eedad..e2855c0f 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.h +++ b/src/helpers/sensors/EnvironmentSensorManager.h @@ -25,6 +25,7 @@ protected: #if ENV_INCLUDE_GPS LocationProvider* _location; + void armGpsPowerSavingCycle(); void start_gps(); void stop_gps(); bool telemetryGpsDetected() const override { return gps_detected; } @@ -55,4 +56,5 @@ public: const char* getSettingName(int i) const override; const char* getSettingValue(int i) const override; bool setSettingValue(const char* name, const char* value) override; + void setPowerSavingEnabled(bool enabled) override; }; diff --git a/src/helpers/sensors/LocationProvider.h b/src/helpers/sensors/LocationProvider.h index 8d79b9db..d87c263a 100644 --- a/src/helpers/sensors/LocationProvider.h +++ b/src/helpers/sensors/LocationProvider.h @@ -7,6 +7,10 @@ class LocationProvider { protected: bool _time_sync_needed = true; bool _time_sync_applied = false; + bool _gps_powersaving_enabled = false; + unsigned long _next_gps_off = 0; + unsigned long _next_gps_on = 0; + unsigned long _last_valid_time_sync = 0; void markTimeSyncApplied() { _time_sync_applied = true; } @@ -20,6 +24,14 @@ public: _time_sync_applied = false; return applied; } + virtual void stopTimeSync() { _time_sync_needed = false; } + virtual void setGPSPowerSaving(bool enabled) { _gps_powersaving_enabled = enabled; _next_gps_off = 0; _next_gps_on = 0; } + virtual bool getGPSPowerSaving() { return _gps_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 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 cefe4d11..37aeaadc 100644 --- a/src/helpers/sensors/MicroNMEALocationProvider.h +++ b/src/helpers/sensors/MicroNMEALocationProvider.h @@ -153,9 +153,10 @@ public : markTimeSyncApplied(); _time_sync_needed = false; _last_time_sync = millis(); + _last_valid_time_sync = _clock->getCurrentTime(); } } - if (isValid()) { + if (isValid() && satellitesCount() >= 5) { time_valid ++; } } diff --git a/src/helpers/ui/ST7735Display.cpp b/src/helpers/ui/ST7735Display.cpp index 65b0fe7f..b4851346 100644 --- a/src/helpers/ui/ST7735Display.cpp +++ b/src/helpers/ui/ST7735Display.cpp @@ -1,4 +1,5 @@ #include "ST7735Display.h" +#include //#include diff --git a/src/helpers/ui/ST7735Display.h b/src/helpers/ui/ST7735Display.h index 68c83db2..850cb9c9 100644 --- a/src/helpers/ui/ST7735Display.h +++ b/src/helpers/ui/ST7735Display.h @@ -3,7 +3,6 @@ #include "DisplayDriver.h" #include #include -#include "TFT_eSPI.h" #include class ST7735Display : public DisplayDriver { diff --git a/variants/wio-tracker-l1/platformio.ini b/variants/wio-tracker-l1/platformio.ini index 4bd41363..95bd5bf2 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} @@ -68,6 +72,8 @@ build_flags = ${WioTrackerL1.build_flags} -D OFFLINE_QUEUE_SIZE=256 -D PIN_BUZZER=12 -D QSPIFLASH=1 + -D ENV_PIN_SDA=PIN_WIRE1_SDA + -D ENV_PIN_SCL=PIN_WIRE1_SCL ; NOTE: DO NOT ENABLE --> -D MESH_PACKET_LOGGING=1 ; NOTE: DO NOT ENABLE --> -D MESH_DEBUG=1 build_src_filter = ${WioTrackerL1.build_src_filter}