fix(mqtt): require real SNTP completion before crediting a fallback server

The fallback configured a server, waited 500 ms, and accepted any plausible
system clock as proof that server had answered. It usually has not answered
that fast — and the device usually already holds valid time, from an earlier
sync or the RTC — so the first server in the list was credited unconditionally,
the walk stopped there, _last_ntp_sync was refreshed, and an unreachable host
was logged as the source. On the `set mqtt.ntp` validation path, where the
single-server walk exists so a typo fails fast, that reported a bad server as OK.

Poll sntp_get_sync_status() for SNTP_SYNC_STATUS_COMPLETED instead, which is the
layer's own statement that a packet arrived. The status is one-shot — reading
COMPLETED clears it — so a result left by an earlier sync would latch on the
first poll; clear it before the loop.

An implausible epoch after a completed sync now moves to the next server rather
than spinning out the remaining attempts against a server that has answered.
This commit is contained in:
agessaman
2026-08-14 19:40:48 -07:00
parent 74a3df3206
commit b1ceaf01a8
+13 -1
View File
@@ -21,6 +21,7 @@
#ifdef ESP_PLATFORM
#include <esp_wifi.h>
#include <esp_sntp.h>
#include <esp_heap_caps.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
@@ -3986,15 +3987,26 @@ bool MQTTBridge::syncTimeWithNTP(bool force, bool primary_only) {
const char* server = servers[s];
MQTT_DEBUG_PRINTLN("SNTP fallback trying %s...", server);
configTime(0, 0, server);
// A plausible clock is not evidence this server answered. The device usually
// already holds valid time here — from an earlier sync, or the RTC — so polling
// time(nullptr) declared the very first server successful without a packet ever
// arriving, stopped the fallback walk there, and refreshed _last_ntp_sync. Worse
// on the `set mqtt.ntp` validation path, where a typo is supposed to fail fast.
// Wait for SNTP itself to report completion. The status is one-shot — reading
// COMPLETED clears it — so drop any result an earlier sync left behind.
sntp_set_sync_status(SNTP_SYNC_STATUS_RESET);
for (int i = 0; i < 20; i++) {
delay(500);
if (sntp_get_sync_status() != SNTP_SYNC_STATUS_COMPLETED) continue;
epochTime = (unsigned long)time(nullptr);
if (epochTime >= kMinValidEpoch) {
ntp_ok = true;
ntp_server_used = server;
MQTT_DEBUG_PRINTLN("SNTP fallback succeeded on %s: %lu", server, epochTime);
break;
} else {
MQTT_DEBUG_PRINTLN("SNTP fallback: %s synced an implausible epoch %lu", server, epochTime);
}
break;
}
}
}