#include "time_manager.h" #include "config.h" #include #ifdef ARDUINO_ARCH_ESP32 #include #endif static bool g_time_synced = false; static bool g_clock_plausible = false; static bool g_tz_set = false; static uint32_t g_last_sync_utc = 0; static constexpr uint32_t MIN_PLAUSIBLE_EPOCH_UTC = 1672531200UL; // 2023-01-01 00:00:00 UTC static void note_last_sync(uint32_t epoch) { if (epoch == 0) { return; } g_last_sync_utc = epoch; } static bool epoch_is_plausible(time_t epoch) { return epoch >= static_cast(MIN_PLAUSIBLE_EPOCH_UTC); } static void mark_synced(uint32_t epoch) { if (epoch == 0) { return; } g_time_synced = true; g_clock_plausible = true; note_last_sync(epoch); } #ifdef ARDUINO_ARCH_ESP32 static void ntp_sync_notification_cb(struct timeval *tv) { time_t epoch = tv ? tv->tv_sec : time(nullptr); if (!epoch_is_plausible(epoch)) { return; } if (epoch > static_cast(UINT32_MAX)) { return; } mark_synced(static_cast(epoch)); } #endif static void ensure_timezone_set() { if (g_tz_set) { return; } setenv("TZ", TIMEZONE_TZ, 1); tzset(); g_tz_set = true; } void time_receiver_init(const char *ntp_server_1, const char *ntp_server_2) { const char *server1 = (ntp_server_1 && ntp_server_1[0] != '\0') ? ntp_server_1 : "pool.ntp.org"; const char *server2 = (ntp_server_2 && ntp_server_2[0] != '\0') ? ntp_server_2 : "time.nist.gov"; #ifdef ARDUINO_ARCH_ESP32 sntp_set_time_sync_notification_cb(ntp_sync_notification_cb); #endif configTime(0, 0, server1, server2); ensure_timezone_set(); } uint32_t time_get_utc() { time_t now = time(nullptr); if (!epoch_is_plausible(now)) { g_clock_plausible = false; return 0; } g_clock_plausible = true; #ifdef ARDUINO_ARCH_ESP32 if (!g_time_synced && sntp_get_sync_status() == SNTP_SYNC_STATUS_COMPLETED) { mark_synced(static_cast(now)); } #endif return static_cast(now); } bool time_is_synced() { (void)time_get_utc(); return g_time_synced && g_clock_plausible; } void time_set_utc(uint32_t epoch) { ensure_timezone_set(); struct timeval tv; tv.tv_sec = epoch; tv.tv_usec = 0; settimeofday(&tv, nullptr); if (epoch_is_plausible(static_cast(epoch))) { mark_synced(epoch); } else { g_clock_plausible = false; g_time_synced = false; } } void time_get_local_hhmm(char *out, size_t out_len) { if (!time_is_synced()) { snprintf(out, out_len, "--:--"); return; } time_t now = time(nullptr); struct tm timeinfo; localtime_r(&now, &timeinfo); snprintf(out, out_len, "%02d:%02d", timeinfo.tm_hour, timeinfo.tm_min); } uint32_t time_get_last_sync_utc() { return g_last_sync_utc; } uint32_t time_get_last_sync_age_sec() { if (!time_is_synced()) { return 0; } if (g_last_sync_utc == 0) { return 0; } uint32_t now = time_get_utc(); return now > g_last_sync_utc ? now - g_last_sync_utc : 0; }