From e14eb304b53aaba95b1fbff299b27637a083b9d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Martin?= Date: Tue, 6 Oct 2026 08:52:54 +0200 Subject: [PATCH] Wi-Fi: SNTP could be started twice at a join, and ESP-IDF asserts on that At every join the DNS and NTP setup ran twice in the same pass: the "check now and then" timer compared this pass's time with a stamp taken from millis() a moment later, and the unsigned difference underflowed. Starting SNTP is only queued for the network task, so when the second run looked before the first had been carried out, it queued a second start: "Operating mode must not be set while SNTP client is running", a panic nine seconds after boot. Rare (once in the dozen or so boots of this branch's testing), there since v0.7.0. Rollback caught it: the update was on Probation and the device went back to the build before. The service now remembers that it started SNTP instead of asking esp_sntp_enabled(), and the timer compares signed. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT --- src/services/wifi_service.cpp | 10 ++++++++-- src/services/wifi_service.h | 1 + 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/services/wifi_service.cpp b/src/services/wifi_service.cpp index 25b76a8..5c14496 100644 --- a/src/services/wifi_service.cpp +++ b/src/services/wifi_service.cpp @@ -87,7 +87,10 @@ void WifiService::applyServers(Why why) { touched = true; } } - if (!esp_sntp_enabled()) { + // Our own flag, not esp_sntp_enabled(): starting is only queued for the network task, and a + // second call that looked before it ran would start SNTP twice, which ESP-IDF asserts on. + if (!sntpStarted_) { + sntpStarted_ = true; esp_sntp_setoperatingmode(ESP_SNTP_OPMODE_POLL); esp_sntp_init(); } else if (touched || changed) { @@ -249,7 +252,10 @@ void WifiService::tick(uint32_t nowMs) { apply(controller_.update(nowMs, settings_.getBool(Setting::WifiEnabled) && !paused_)); - if (controller_.state() == State::Connected && nowMs - serversCheckedMs_ >= kServersEveryMs) applyServers(Why::Check); + // Signed: serversCheckedMs_ can be newer than this pass's nowMs (set from millis() just above), + // and the unsigned difference then ran the check again at once, at every join. + if (controller_.state() == State::Connected && static_cast(nowMs - serversCheckedMs_) >= static_cast(kServersEveryMs)) + applyServers(Why::Check); if (ntpWaiting_ && sntp_get_sync_status() == SNTP_SYNC_STATUS_COMPLETED) { ntpWaiting_ = false; diff --git a/src/services/wifi_service.h b/src/services/wifi_service.h index 5d77a84..c39864e 100644 --- a/src/services/wifi_service.h +++ b/src/services/wifi_service.h @@ -86,6 +86,7 @@ class WifiService : public Service { std::vector listScan_; uint32_t listScanSeq_ = 0; bool ntpWaiting_ = false; + bool sntpStarted_ = false; bool radioInitialised_ = false; bool paused_ = false; // Debug Builds: off for a moment, whatever the setting says bool fixed_ = false; // the network in use has a Fixed address