diff --git a/zephcore/adapters/datastore/ZephyrDataStore.cpp b/zephcore/adapters/datastore/ZephyrDataStore.cpp index c04bbb3..fc31ee6 100644 --- a/zephcore/adapters/datastore/ZephyrDataStore.cpp +++ b/zephcore/adapters/datastore/ZephyrDataStore.cpp @@ -506,8 +506,11 @@ void ZephyrDataStore::loadPrefs(NodePrefs &prefs) LOG_ERR("loadPrefs: read failed"); return; } - if (len < 88) { - LOG_ERR("loadPrefs: file too small (%d bytes, need 88)", (int)len); + if (len < 90) { + /* gps_interval occupies bytes 86-89, so a shorter blob would read + * its top bytes from uninitialized stack — reject rather than load a + * garbage interval. */ + LOG_ERR("loadPrefs: file too small (%d bytes, need 90)", (int)len); return; } LOG_DBG("loadPrefs: loaded %d bytes from %s", (int)len, PREFS_FILE); diff --git a/zephcore/helpers/ui/ui_mesh_actions.cpp b/zephcore/helpers/ui/ui_mesh_actions.cpp index 66ce6e2..6870a71 100644 --- a/zephcore/helpers/ui/ui_mesh_actions.cpp +++ b/zephcore/helpers/ui/ui_mesh_actions.cpp @@ -370,7 +370,10 @@ extern "C" void mesh_housekeeping_ui_refresh(void) s_lora_radio->getPacketsSent(), s_lora_radio->getPacketsRecvErrors()); - /* Update GPS satellite count even without fix */ + /* Update GPS satellite count even without fix. When GPS is disabled, push + * a zeroed count — gps_enable(false) already zeros the internal count, but + * this refresh is otherwise gated on gps_is_enabled() and would leave the + * UI showing a stale value from before the toggle-off. */ if (gps_is_enabled()) { struct gps_position gpos; @@ -379,6 +382,8 @@ extern "C" void mesh_housekeeping_ui_refresh(void) ui_set_gps_data(false, (uint8_t)gpos.satellites, 0, 0, 0); } + } else { + ui_set_gps_data(false, 0, 0, 0, 0); } /* Update GPS state machine info (state, last fix age, next search) */ diff --git a/zephcore/src/main_companion.cpp b/zephcore/src/main_companion.cpp index 75a8b80..580b69a 100644 --- a/zephcore/src/main_companion.cpp +++ b/zephcore/src/main_companion.cpp @@ -740,6 +740,44 @@ public: MeshTimeSync* getMeshTimeSync() override { return companion_mesh.getMeshTimeSync(); } + + /* GPS control — companion delegates to the GPS manager (same as repeater). + * Without these, the base CommonCLICallbacks stubs make the CLI report + * "off" / "gps toggle not found" regardless of real GPS state. */ + bool setGpsEnabled(bool enabled) override { + if (!gps_is_available()) return false; + gps_enable(enabled); + return true; + } + bool isGpsEnabled() const override { + return gps_is_enabled(); + } + void formatGpsStatsReply(char* reply) override { + if (!gps_is_enabled()) { + strcpy(reply, "off"); + return; + } + struct gps_state_info gsi; + gps_get_state_info(&gsi); + static const char* const state_str[] = { "off", "standby", "acquiring" }; + const char* state = gsi.state < 3 ? state_str[gsi.state] : "unknown"; + struct gps_position pos; + bool has_pos = gps_get_last_known_position(&pos); + if (has_pos) { + snprintf(reply, CLI_REPLY_SIZE, + "on state=%s sats=%u fix=%us ago lat=%.6f lon=%.6f", + state, gsi.satellites, gsi.last_fix_age_s, + pos.latitude_ndeg / 1e9, pos.longitude_ndeg / 1e9); + } else if (gsi.next_search_s > 0) { + snprintf(reply, CLI_REPLY_SIZE, + "on state=%s sats=%u no fix next=%us", + state, gsi.satellites, gsi.next_search_s); + } else { + snprintf(reply, CLI_REPLY_SIZE, + "on state=%s sats=%u no fix", + state, gsi.satellites); + } + } }; static CompanionCLICallbacks companion_cli_cbs;