fix(wifi): sync time on each IP lease

This commit is contained in:
liu weikai
2026-08-13 21:36:59 +08:00
parent 1e5d44a2d6
commit 8487ff6d2c
3 changed files with 66 additions and 29 deletions
@@ -28,7 +28,6 @@ static bool s_netif_ready;
static bool s_wifi_initialized;
static bool s_wifi_started;
static bool s_time_sync_in_progress;
static bool s_time_sync_attempted;
static time_t s_time_sync_epoch;
static esp_timer_handle_t s_time_sync_timeout_timer;
static TaskHandle_t s_time_sync_emit_task;
@@ -82,21 +81,28 @@ static void stop_sntp_once(void)
esp_sntp_set_time_sync_notification_cb(NULL);
}
static void cancel_network_time_sync(void)
// Keep the timer as Wi-Fi runtime state to avoid repeated heap allocation on
// every DHCP lease, but always return its active resources on each terminal
// path. A subsequent GOT_IP can then start a fresh one-shot sync.
static void finish_network_time_sync(void)
{
s_time_sync_in_progress = false;
if (s_time_sync_timeout_timer != NULL)
{
(void)esp_timer_stop(s_time_sync_timeout_timer);
}
stop_sntp_once();
s_time_sync_in_progress = false;
}
static void cancel_network_time_sync(void)
{
finish_network_time_sync();
}
static void time_sync_emit_task(void* arg)
{
(void)arg;
const time_t epoch_seconds = s_time_sync_epoch;
stop_sntp_once();
if (valid_network_epoch(epoch_seconds))
{
@@ -109,7 +115,7 @@ static void time_sync_emit_task(void* arg)
emit_time_sync_event(TM_C6_ERROR_INTERNAL, 0, "c6_wifi_sntp");
}
s_time_sync_in_progress = false;
finish_network_time_sync();
s_time_sync_emit_task = NULL;
vTaskDelete(NULL);
}
@@ -139,8 +145,7 @@ static void time_sync_notification_cb(struct timeval* tv)
5,
&s_time_sync_emit_task) != pdPASS)
{
stop_sntp_once();
s_time_sync_in_progress = false;
finish_network_time_sync();
s_time_sync_emit_task = NULL;
ESP_LOGW(TAG, "Failed to start SNTP emit task");
}
@@ -155,8 +160,7 @@ static void time_sync_timeout_cb(void* arg)
}
ESP_LOGW(TAG, "SNTP sync timed out server=%s", TM_WIFI_SNTP_SERVER);
stop_sntp_once();
s_time_sync_in_progress = false;
finish_network_time_sync();
}
static bool ensure_time_sync_timer(void)
@@ -204,12 +208,12 @@ static void start_or_restart_sntp(void)
static void request_network_time_sync(void)
{
if (s_time_sync_attempted || s_time_sync_in_progress)
if (s_time_sync_in_progress)
{
ESP_LOGI(TAG, "Ignoring duplicate GOT_IP while SNTP is in progress");
return;
}
s_time_sync_attempted = true;
if (!ensure_time_sync_timer())
{
return;
@@ -220,7 +224,7 @@ static void request_network_time_sync(void)
esp_timer_start_once(s_time_sync_timeout_timer, (uint64_t)TM_WIFI_SNTP_TIMEOUT_MS * 1000ULL);
if (timer_err != ESP_OK)
{
s_time_sync_in_progress = false;
finish_network_time_sync();
ESP_LOGW(TAG, "Failed to start SNTP timeout timer: %s", esp_err_to_name(timer_err));
return;
}
@@ -290,7 +294,6 @@ static void wifi_event_handler(void* arg, esp_event_base_t event_base, int32_t e
}
case WIFI_EVENT_STA_DISCONNECTED:
cancel_network_time_sync();
s_time_sync_attempted = false;
emit_simple_event(TM_C6_WIFI_EVENT_STA_DISCONNECTED, TM_C6_OK, NULL, 0);
break;
case WIFI_EVENT_AP_START:
@@ -314,6 +317,7 @@ static void wifi_event_handler(void* arg, esp_event_base_t event_base, int32_t e
ipv4 = event->ip_info.ip.addr;
}
emit_simple_event(TM_C6_WIFI_EVENT_STA_GOT_IP, TM_C6_OK, s_config.sta_ssid, ipv4);
ESP_LOGI(TAG, "GOT_IP triggers one-shot SNTP");
request_network_time_sync();
}
}
@@ -381,7 +385,6 @@ esp_err_t tm_wifi_apply_config(const tm_c6_wifi_config_t* config)
if (!config->wifi_enabled)
{
cancel_network_time_sync();
s_time_sync_attempted = false;
if (s_wifi_started)
{
(void)esp_wifi_disconnect();
@@ -470,7 +473,6 @@ esp_err_t tm_wifi_handle_control(const tm_c6_wifi_control_t* control)
case TM_C6_WIFI_CMD_CONNECT:
{
cancel_network_time_sync();
s_time_sync_attempted = false;
s_config.wifi_enabled = 1;
s_config.sta_enabled = 1;
copy_text(s_config.sta_ssid, sizeof(s_config.sta_ssid), control->ssid);
@@ -480,7 +482,6 @@ esp_err_t tm_wifi_handle_control(const tm_c6_wifi_control_t* control)
}
case TM_C6_WIFI_CMD_DISCONNECT:
cancel_network_time_sync();
s_time_sync_attempted = false;
return esp_wifi_disconnect();
case TM_C6_WIFI_CMD_GET_IP:
{
@@ -21,6 +21,24 @@ bool contains(const std::string& haystack, const char* needle)
return haystack.find(needle) != std::string::npos;
}
std::size_t occurrences(const std::string& haystack, const char* needle)
{
const std::string target = needle ? needle : "";
if (target.empty())
{
return 0;
}
std::size_t count = 0;
std::size_t offset = 0;
while ((offset = haystack.find(target, offset)) != std::string::npos)
{
++count;
offset += target.size();
}
return count;
}
} // namespace
int main(int argc, char** argv)
@@ -36,6 +54,8 @@ int main(int argc, char** argv)
const std::string wifi_runtime = readFile(
repo_root /
"platform/esp/common/include/platform/esp/common/wifi_runtime_impl.h");
const std::string c6_wifi_runtime =
readFile(repo_root / "firmware/c6_companion/components/tm_wifi/tm_wifi.c");
const std::string team_pairing_transport = readFile(
repo_root /
"platform/esp/arduino_common/src/team/pairing/team_pairing_transport_espnow.cpp");
@@ -58,6 +78,17 @@ int main(int argc, char** argv)
assert(contains(wifi_runtime, "void profile_retry_timer_cb(void*)"));
assert(contains(wifi_runtime, "schedule_profile_retry();"));
assert(contains(wifi_runtime, "(void)connect(nullptr);"));
assert(!contains(wifi_runtime, "network_time_sync_attempted"));
assert(contains(wifi_runtime, "void finish_network_time_sync()"));
assert(contains(wifi_runtime, "GOT_IP triggers one-shot SNTP"));
assert(contains(wifi_runtime, "duplicate GOT_IP ignored while SNTP is in progress"));
assert(occurrences(wifi_runtime, "finish_network_time_sync();") >= 5);
assert(!contains(c6_wifi_runtime, "s_time_sync_attempted"));
assert(contains(c6_wifi_runtime, "static void finish_network_time_sync(void)"));
assert(contains(c6_wifi_runtime, "GOT_IP triggers one-shot SNTP"));
assert(contains(c6_wifi_runtime, "Ignoring duplicate GOT_IP while SNTP is in progress"));
assert(occurrences(c6_wifi_runtime, "finish_network_time_sync();") >= 5);
assert(contains(team_pairing_transport,
"set_non_preemptible_activity(true, \"team_pairing\")"));
assert(contains(team_pairing_transport, "set_non_preemptible_activity(false)"));
@@ -87,7 +87,6 @@ struct RuntimeState
bool wifi_initialized = false;
bool ble_paused_for_wifi = false;
bool network_time_sync_in_progress = false;
bool network_time_sync_attempted = false;
std::time_t network_time_sync_epoch = 0;
bool config_cached = false;
bool profiles_cached = false;
@@ -758,7 +757,6 @@ void clear_connection_details()
cancel_network_time_sync();
s_runtime.connected = false;
s_runtime.connecting = false;
s_runtime.network_time_sync_attempted = false;
s_runtime.rssi = -127;
s_runtime.ssid[0] = '\0';
s_runtime.ip[0] = '\0';
@@ -848,20 +846,28 @@ void stop_sntp_once()
esp_sntp_set_time_sync_notification_cb(nullptr);
}
void cancel_network_time_sync()
// A time sync is strictly bound to the current IP lease. Keep the timer
// allocated with the Wi-Fi runtime so repeated DHCP events do not fragment
// scarce internal memory, but make every terminal path stop it and unregister
// the SNTP callback before another lease can start a new sync.
void finish_network_time_sync()
{
s_runtime.network_time_sync_in_progress = false;
if (s_runtime.network_time_sync_timeout_timer != nullptr)
{
(void)esp_timer_stop(s_runtime.network_time_sync_timeout_timer);
}
stop_sntp_once();
s_runtime.network_time_sync_in_progress = false;
}
void cancel_network_time_sync()
{
finish_network_time_sync();
}
void network_time_apply_task(void*)
{
const std::time_t epoch_seconds = s_runtime.network_time_sync_epoch;
stop_sntp_once();
if (is_valid_network_epoch(epoch_seconds))
{
@@ -877,7 +883,7 @@ void network_time_apply_task(void*)
static_cast<long long>(epoch_seconds));
}
s_runtime.network_time_sync_in_progress = false;
finish_network_time_sync();
s_runtime.network_time_apply_task = nullptr;
vTaskDelete(nullptr);
}
@@ -908,8 +914,7 @@ void network_time_sync_notification_cb(timeval* tv)
&s_runtime.network_time_apply_task);
if (created != pdPASS)
{
stop_sntp_once();
s_runtime.network_time_sync_in_progress = false;
finish_network_time_sync();
s_runtime.network_time_apply_task = nullptr;
std::printf("[WiFi][Time] failed to start SNTP apply task\n");
}
@@ -923,8 +928,7 @@ void network_time_sync_timeout_cb(void*)
}
std::printf("[WiFi][Time] SNTP sync timed out server=%s\n", kNetworkTimeSyncServer);
stop_sntp_once();
s_runtime.network_time_sync_in_progress = false;
finish_network_time_sync();
}
bool ensure_network_time_sync_timer()
@@ -971,12 +975,12 @@ void start_or_restart_sntp()
void request_network_time_sync()
{
if (s_runtime.network_time_sync_attempted || s_runtime.network_time_sync_in_progress)
if (s_runtime.network_time_sync_in_progress)
{
std::printf("[WiFi][Time] duplicate GOT_IP ignored while SNTP is in progress\n");
return;
}
s_runtime.network_time_sync_attempted = true;
if (!ensure_network_time_sync_timer())
{
return;
@@ -988,7 +992,7 @@ void request_network_time_sync()
static_cast<uint64_t>(kNetworkTimeSyncTimeoutMs) * 1000ULL);
if (timer_err != ESP_OK)
{
s_runtime.network_time_sync_in_progress = false;
finish_network_time_sync();
std::printf("[WiFi][Time] failed to start SNTP timeout timer err=0x%x\n",
static_cast<unsigned>(timer_err));
return;
@@ -1225,6 +1229,7 @@ void wifi_event_handler(void*,
s_runtime.ip[0] ? s_runtime.ip : "<none>",
s_runtime.ssid[0] ? s_runtime.ssid : current_config().ssid,
s_runtime.rssi);
std::printf("[WiFi][Time] GOT_IP triggers one-shot SNTP\n");
request_network_time_sync();
ensure_reconnect_memory_reserve();
}