#include "sys_time.h" #include "os_event.h" #include "os_status.h" #include "esp_log.h" #include "esp_netif_sntp.h" #include "esp_sntp.h" #include "nvs.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include #include #include #include static const char *TAG = "SYS_TIME"; static char s_tz_str[64] = SYS_TIME_DEFAULT_TZ; static TaskHandle_t s_sntp_task_handle = NULL; static const char *const s_ntp_servers[] = { "time.google.com", "time.cloudflare.com", "pool.ntp.org", "time.windows.com", "216.239.35.0" /* Fallback direct IP for time.google.com */ }; static void time_sync_notification_cb(struct timeval *tv) { time_t now = tv ? tv->tv_sec : time(NULL); struct tm timeinfo; localtime_r(&now, &timeinfo); char strftime_buf[64]; strftime(strftime_buf, sizeof(strftime_buf), "%Y-%m-%d %H:%M:%S %Z", &timeinfo); ESP_LOGI(TAG, "SNTP packet received & verified: %s", strftime_buf); sys_event_post(SYS_EVENT_TICK_1S, 0, NULL); } esp_err_t sys_time_init(void) { nvs_handle_t nvs; size_t tz_len = sizeof(s_tz_str); if (nvs_open("system", NVS_READONLY, &nvs) == ESP_OK) { if (nvs_get_str(nvs, "tz", s_tz_str, &tz_len) != ESP_OK) { strncpy(s_tz_str, SYS_TIME_DEFAULT_TZ, sizeof(s_tz_str) - 1); } nvs_close(nvs); } else { strncpy(s_tz_str, SYS_TIME_DEFAULT_TZ, sizeof(s_tz_str) - 1); } s_tz_str[sizeof(s_tz_str) - 1] = '\0'; setenv("TZ", s_tz_str, 1); tzset(); ESP_LOGI(TAG, "System time service initialized (Timezone: %s)", s_tz_str); return ESP_OK; } static void sntp_sync_task(void *pvParameters) { const size_t num_servers = sizeof(s_ntp_servers) / sizeof(s_ntp_servers[0]); for (size_t i = 0; i < num_servers && !sys_time_is_synced(); i++) { const char *server = s_ntp_servers[i]; ESP_LOGI(TAG, "Attempting SNTP sync with server [%u/%u]: '%s'...", (unsigned int)(i + 1), (unsigned int)num_servers, server); esp_netif_sntp_deinit(); esp_sntp_config_t config = ESP_NETIF_SNTP_DEFAULT_CONFIG(server); config.sync_cb = time_sync_notification_cb; config.smooth_sync = false; config.server_from_dhcp = false; config.wait_for_sync = true; config.start = true; esp_err_t err = esp_netif_sntp_init(&config); if (err != ESP_OK) { ESP_LOGE(TAG, "Failed to initialize SNTP for '%s': %s", server, esp_err_to_name(err)); continue; } // Poll for sync completion up to 5 seconds per server for (int r = 0; r < 5; r++) { vTaskDelay(pdMS_TO_TICKS(1000)); if (sys_time_is_synced() || sntp_get_sync_status() == SNTP_SYNC_STATUS_COMPLETED) { break; } } if (sys_time_is_synced()) { break; } } if (sys_time_is_synced()) { time_t now = time(NULL); struct tm timeinfo; localtime_r(&now, &timeinfo); char strftime_buf[64]; strftime(strftime_buf, sizeof(strftime_buf), "%Y-%m-%d %H:%M:%S %Z", &timeinfo); ESP_LOGI(TAG, "SNTP Sync Complete! Current Local Time: %s", strftime_buf); } else { ESP_LOGW(TAG, "SNTP sync could not reach any configured NTP server. Device will retry on reconnect."); } s_sntp_task_handle = NULL; vTaskDelete(NULL); } void sys_time_start_sntp(void) { if (s_sntp_task_handle != NULL) { ESP_LOGI(TAG, "SNTP sync already running in background"); return; } xTaskCreate(sntp_sync_task, "sntp_task", 4096, NULL, 5, &s_sntp_task_handle); } bool sys_time_is_synced(void) { time_t now = time(NULL); // Synced if timestamp is after Jan 1, 2024 (1704067200) return (now >= 1704067200ULL); } time_t sys_time_get_epoch(void) { return time(NULL); } bool sys_time_get_local(struct tm *out_tm) { if (!out_tm) return false; time_t now = time(NULL); localtime_r(&now, out_tm); return sys_time_is_synced(); } esp_err_t sys_time_set_epoch(time_t epoch_sec) { if (epoch_sec < 1704067200ULL) { ESP_LOGE(TAG, "Invalid epoch timestamp: %lld", (long long)epoch_sec); return ESP_ERR_INVALID_ARG; } struct timeval tv = { .tv_sec = epoch_sec, .tv_usec = 0 }; int ret = settimeofday(&tv, NULL); if (ret != 0) { ESP_LOGE(TAG, "Failed to set time of day"); return ESP_FAIL; } struct tm timeinfo; localtime_r(&epoch_sec, &timeinfo); char strftime_buf[64]; strftime(strftime_buf, sizeof(strftime_buf), "%Y-%m-%d %H:%M:%S %Z", &timeinfo); ESP_LOGI(TAG, "Manual time set: %s", strftime_buf); sys_event_post(SYS_EVENT_TICK_1S, 0, NULL); return ESP_OK; } esp_err_t sys_time_set_timezone(const char *tz) { if (!tz || strlen(tz) == 0) return ESP_ERR_INVALID_ARG; strncpy(s_tz_str, tz, sizeof(s_tz_str) - 1); s_tz_str[sizeof(s_tz_str) - 1] = '\0'; setenv("TZ", s_tz_str, 1); tzset(); nvs_handle_t nvs; if (nvs_open("system", NVS_READWRITE, &nvs) == ESP_OK) { nvs_set_str(nvs, "tz", s_tz_str); nvs_commit(nvs); nvs_close(nvs); } ESP_LOGI(TAG, "Timezone updated to: %s", s_tz_str); return ESP_OK; } const char *sys_time_get_timezone(void) { return s_tz_str; }