185 lines
5.2 KiB
C
185 lines
5.2 KiB
C
#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;
|
|
}
|