diff --git a/firmware/main/CMakeLists.txt b/firmware/main/CMakeLists.txt index 416919e..da8275f 100644 --- a/firmware/main/CMakeLists.txt +++ b/firmware/main/CMakeLists.txt @@ -6,6 +6,7 @@ idf_component_register( "ble.c" "provision.c" "nvs_migration.c" + "ntp.c" INCLUDE_DIRS "." "${CMAKE_BINARY_DIR}/spaxel-firmware/main" REQUIRES esp_wifi esp_netif nvs_flash mdns esp_http_client esp_timer bt driver log esp_http_server mbedtls esp_ota ) diff --git a/firmware/main/main.c b/firmware/main/main.c index 72386e0..28b58e9 100644 --- a/firmware/main/main.c +++ b/firmware/main/main.c @@ -131,6 +131,12 @@ static esp_err_t load_nvs_config(void) { g_state.debug = (debug == 1); } + // Load NTP server + len = sizeof(g_state.ntp_server); + if (nvs_get_str(nvs, NVS_KEY_NTP_SERVER, g_state.ntp_server, &len) != ESP_OK) { + strncpy(g_state.ntp_server, "pool.ntp.org", sizeof(g_state.ntp_server)); + } + nvs_close(nvs); ESP_LOGI(TAG, "NVS config loaded: provisioned=%d, role=%s, rate=%d Hz", @@ -200,8 +206,18 @@ static void state_machine_task(void *arg) { if (bits & SPAXEL_EVENT_WIFI_CONNECTED) { ESP_LOGI(TAG, "WiFi connected"); wifi_fail_count = 0; - g_state.state = NODE_STATE_MOTHERSHIP_DISCOVERY; discovery_fail_count = 0; + + // Initialize NTP after WiFi is up + ESP_LOGI(TAG, "Starting NTP sync with server: %s", g_state.ntp_server); + ntp_init(); + ntp_start_sync(g_state.ntp_server); + if (!ntp_wait_sync(10000)) { + ESP_LOGW(TAG, "NTP sync failed, proceeding without stagger"); + } + ntp_start_periodic_resync(); + + g_state.state = NODE_STATE_MOTHERSHIP_DISCOVERY; } else if (bits & SPAXEL_EVENT_WIFI_FAILED) { wifi_fail_count++; ESP_LOGW(TAG, "WiFi failed (attempt %d)", wifi_fail_count); @@ -315,6 +331,15 @@ static void state_machine_task(void *arg) { if (bits & SPAXEL_EVENT_WIFI_CONNECTED) { ESP_LOGI(TAG, "WiFi reconnected"); + + // Re-sync NTP after reconnect + ntp_init(); + ntp_start_sync(g_state.ntp_server); + if (!ntp_wait_sync(10000)) { + ESP_LOGW(TAG, "NTP resync failed after WiFi reconnect"); + } + ntp_start_periodic_resync(); + g_state.state = NODE_STATE_MOTHERSHIP_DISCOVERY; } else { wifi_fail_count++; diff --git a/firmware/main/ntp.c b/firmware/main/ntp.c index cb595d3..a8ad4c6 100644 --- a/firmware/main/ntp.c +++ b/firmware/main/ntp.c @@ -35,9 +35,15 @@ static void sntp_sync_time_callback(struct timeval *tv) { // Periodic resync timer callback static void periodic_resync_callback(void *arg) { ESP_LOGI(TAG, "Periodic NTP resync triggered"); + esp_sntp_stop(); + s_is_synced = false; + if (s_ntp_events) { + xEventGroupClearBits(s_ntp_events, NTP_SYNC_BIT); + } + esp_sntp_setoperatingmode(SNTP_OPMODE_POLL); esp_sntp_setservername(0, s_ntp_server); + sntp_set_time_sync_notification_cb(sntp_sync_time_callback); esp_sntp_init(); - // No need to wait here - the callback will handle completion } esp_err_t ntp_init(void) { diff --git a/firmware/main/spaxel.h b/firmware/main/spaxel.h index 1d3a91a..c3464b5 100644 --- a/firmware/main/spaxel.h +++ b/firmware/main/spaxel.h @@ -83,6 +83,7 @@ typedef struct { bool provisioned; bool debug; uint8_t mac[6]; + char ntp_server[64]; EventGroupHandle_t events; } spaxel_state_t; diff --git a/firmware/main/websocket.c b/firmware/main/websocket.c index c82e810..b90d49a 100644 --- a/firmware/main/websocket.c +++ b/firmware/main/websocket.c @@ -2,6 +2,7 @@ #include "spaxel.h" #include "csi.h" #include "wifi.h" +#include "ntp.h" #include "esp_log.h" #include "esp_timer.h" #include "esp_system.h" @@ -348,6 +349,9 @@ esp_err_t websocket_send_health(void) { } } + // NTP sync status + cJSON_AddBoolToObject(root, "ntp_synced", ntp_is_synced()); + char *json = cJSON_PrintUnformatted(root); cJSON_Delete(root); @@ -584,6 +588,15 @@ static void handle_config_msg(cJSON *root) { g_variance_threshold = (float)var_thresh->valuedouble; } + // NTP server (runtime reconfiguration) + cJSON *ntp = cJSON_GetObjectItem(root, "ntp_server"); + if (ntp && cJSON_IsString(ntp) && strlen(ntp->valuestring) > 0) { + strncpy(g_state.ntp_server, ntp->valuestring, sizeof(g_state.ntp_server) - 1); + g_state.ntp_server[sizeof(g_state.ntp_server) - 1] = '\0'; + ESP_LOGI(TAG, "NTP server changed to: %s", g_state.ntp_server); + ntp_start_sync(g_state.ntp_server); + } + if (changed) { csi_set_rate(g_state.packet_rate); }