From c045df1da53d625e394d8f73a810e896e03e63c0 Mon Sep 17 00:00:00 2001 From: MarekZegare4 Date: Wed, 12 Aug 2026 22:08:13 +0200 Subject: [PATCH] fix(power): stop GPS duty-cycling from starving !gps fix and fighting manual toggles Two races between the new GPS duty-cycle scheduler and code that changes GPS state independently of it: - gpsDutyCycleLoop() capped every "GPS on" phase at a fixed 60s and would stop_gps() as soon as a fix went valid, with no awareness of an in-flight "!gps fix" bot request -- so a fix's own up-to-300s acquire window (and its 10s averaging phase) could get cut short by the scheduler shutting GPS off mid-request. MyMesh::isGpsFixPending() now feeds into UITask's existing "is anything live using GPS right now" hold, alongside trail/live-share/locator/nearby. - setSettingValue("gps", ...) (Settings toggle, bot !gps on/off, CLI) starts/stops GPS directly without resetting the scheduler's own phase timer, so a manual toggle could land on a stale, already-expired deadline left over from before -- immediately re-stopping GPS a tick after turning it on. The phase timer now resets on every external change, so the next duty-cycle tick re-arms fresh. Co-Authored-By: Claude Sonnet 5 --- examples/companion_radio/MyMesh.h | 5 +++++ examples/companion_radio/ui-new/UITask.cpp | 3 ++- src/helpers/sensors/EnvironmentSensorManager.cpp | 7 +++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/examples/companion_radio/MyMesh.h b/examples/companion_radio/MyMesh.h index 8d5058e0..bea006c5 100644 --- a/examples/companion_radio/MyMesh.h +++ b/examples/companion_radio/MyMesh.h @@ -323,6 +323,11 @@ public: // Number of auto-replies sent since boot (DM + channel + room). Shown on BotScreen. uint16_t botReplyCount() const { return _bot_reply_count; } + // Whether a "!gps fix" request is acquiring/averaging right now -- used by + // UITask's GPS duty-cycle "is anything live using GPS right now" check, so + // the scheduler doesn't nap GPS out from under an in-flight bot request. + bool isGpsFixPending() const { return _loc_fix.active; } + private: void tryBotReplyDM(const ContactInfo& from, const char* text, uint8_t hops); void tryBotReplyChannel(uint8_t channel_idx, const char* text, uint8_t hops); diff --git a/examples/companion_radio/ui-new/UITask.cpp b/examples/companion_radio/ui-new/UITask.cpp index 8c597af4..ca05d734 100644 --- a/examples/companion_radio/ui-new/UITask.cpp +++ b/examples/companion_radio/ui-new/UITask.cpp @@ -2589,7 +2589,8 @@ void UITask::loop() { || (_node_prefs && _node_prefs->loc_share_enabled) || (_node_prefs && _node_prefs->locator_enabled && _node_prefs->locator_has_target) || curr == compass_screen - || (curr == nearby_screen && ((NearbyScreen*)nearby_screen)->isNavigating()); + || (curr == nearby_screen && ((NearbyScreen*)nearby_screen)->isNavigating()) + || the_mesh.isGpsFixPending(); _sensors->setGpsKeepAwake(gps_needed_live); // A fresh wake (either a duty-cycle wake, or GPS forced continuously back // on) may deliver a still-settling first fix — re-seed the locator's diff --git a/src/helpers/sensors/EnvironmentSensorManager.cpp b/src/helpers/sensors/EnvironmentSensorManager.cpp index 63bb8b80..36301f51 100644 --- a/src/helpers/sensors/EnvironmentSensorManager.cpp +++ b/src/helpers/sensors/EnvironmentSensorManager.cpp @@ -718,6 +718,13 @@ bool EnvironmentSensorManager::setSettingValue(const char* name, const char* val } else { stop_gps(); } + // This bypasses gpsDutyCycleLoop()'s own start_gps()/stop_gps() calls, so + // its phase timer would otherwise still be counting down (or already + // expired) from before this external change -- reset it so the next + // duty-cycle tick re-arms a fresh phase instead of judging gps_active + // against a stale deadline (e.g. stopping GPS again a tick after this + // just started it). + _gps_duty_phase_until = 0; return true; } if (strcmp(name, "gps_interval") == 0) {