From cd6ed3978fae40b67876adcaac3b6e5564f4aff0 Mon Sep 17 00:00:00 2001 From: DatanoiseTV <6614616+DatanoiseTV@users.noreply.github.com> Date: Fri, 8 May 2026 21:41:00 +0200 Subject: [PATCH] gps: soft-sleep the receiver during CPU light-sleep MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GPS only subscribed to notifyDeepSleep, so during light-sleep the receiver kept drawing ~25 mA on a non-router device — the dominant drain on devices that spend most of their time in LIGHT_SLEEP. Subscribe to notifyLightSleep/notifyLightSleepEnd as well, but use GPS_SOFTSLEEP instead of disable()+enable() since the state machine survives across the short light-sleep window and full power-cycle re-acquisition would cost more than it saves. --- src/gps/GPS.cpp | 31 +++++++++++++++++++++++++++++++ src/gps/GPS.h | 13 +++++++++++++ 2 files changed, 44 insertions(+) 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);