feat: implement system time synchronization, web server handlers, and clock application components

This commit is contained in:
dark98
2026-09-25 20:57:19 +01:00
parent 4bf31f908a
commit 59b0318579
11 changed files with 442 additions and 33 deletions
+184
View File
@@ -0,0 +1,184 @@
#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 <sys/time.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
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;
}