feat: implement system time synchronization, web server handlers, and clock application components
This commit is contained in:
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user