diff --git a/src/gps/GPS.cpp b/src/gps/GPS.cpp index e581eb0ef98..72fd752efc4 100644 --- a/src/gps/GPS.cpp +++ b/src/gps/GPS.cpp @@ -819,6 +819,10 @@ bool GPS::setup() } notifyDeepSleepObserver.observe(¬ifyDeepSleep); +#ifdef ARCH_ESP32 + notifyLightSleepObserver.observe(¬ifyLightSleep); + notifyLightSleepEndObserver.observe(¬ifyLightSleepEnd); +#endif return true; } @@ -827,6 +831,10 @@ GPS::~GPS() { // we really should unregister our sleep observer notifyDeepSleepObserver.unobserve(¬ifyDeepSleep); +#ifdef ARCH_ESP32 + notifyLightSleepObserver.unobserve(¬ifyLightSleep); + notifyLightSleepEndObserver.unobserve(¬ifyLightSleepEnd); +#endif } // Put the GPS hardware into a specified state @@ -1246,6 +1254,29 @@ int GPS::prepareDeepSleep(void *unused) return 0; } +#ifdef ARCH_ESP32 +// Light-sleep is short and the GPS state machine survives across it; soft-sleep +// the receiver instead of disabling so we don't pay re-init / re-acquire cost +// on every cycle. Without this the GPS keeps drawing ~25 mA the whole time the +// MCU is asleep — the dominant drain on most non-router devices. +int GPS::prepareLightSleep(void *unused) +{ + preLightSleepState = powerState; + if (powerState == GPS_ACTIVE) { + setPowerState(GPS_SOFTSLEEP); + } + return 0; +} + +int GPS::endLightSleep(esp_sleep_wakeup_cause_t cause) +{ + if (preLightSleepState == GPS_ACTIVE && powerState != GPS_ACTIVE) { + setPowerState(GPS_ACTIVE); + } + return 0; +} +#endif + static const char *PROBE_MESSAGE = "Trying %s (%s)..."; static const char *DETECTED_MESSAGE = "%s detected"; diff --git a/src/gps/GPS.h b/src/gps/GPS.h index 8d63ce82fee..2778ae79d4c 100644 --- a/src/gps/GPS.h +++ b/src/gps/GPS.h @@ -198,6 +198,13 @@ class GPS : private concurrency::OSThread CallbackObserver notifyDeepSleepObserver = CallbackObserver(this, &GPS::prepareDeepSleep); +#ifdef ARCH_ESP32 + GPSPowerState preLightSleepState = GPS_OFF; + CallbackObserver notifyLightSleepObserver = CallbackObserver(this, &GPS::prepareLightSleep); + CallbackObserver notifyLightSleepEndObserver = + CallbackObserver(this, &GPS::endLightSleep); +#endif + /** If !NULL we will use this serial port to construct our GPS */ #if defined(ARCH_RP2040) static SerialUART *_serial_gps; @@ -226,6 +233,12 @@ class GPS : private concurrency::OSThread /// always returns 0 to indicate okay to sleep int prepareDeepSleep(void *unused); +#ifdef ARCH_ESP32 + /// Drop the GPS into a low-power state for the duration of CPU light-sleep, and restore on wake. + int prepareLightSleep(void *unused); + int endLightSleep(esp_sleep_wakeup_cause_t cause); +#endif + /** Set power with EN pin, if relevant */ void writePinEN(bool on);