#include "HalClock.h" #include #include #include #include HalClock halClock; // Singleton instance void HalClock::begin() { _available = _sdkRtc.begin(); LOG_INF("CLK", _available ? "SDK RTC found" : "RTC not found"); } bool HalClock::getTime(uint8_t& hour, uint8_t& minute) const { if (!_available) return false; const unsigned long now = millis(); if (_lastPollMs != 0 && (now - _lastPollMs) < CLOCK_POLL_MS) { hour = _cachedHour; minute = _cachedMinute; return true; } Rtc::DateTime dt; if (!_sdkRtc.now(dt)) { if (!_hasCachedTime) return false; _lastPollMs = now; hour = _cachedHour; minute = _cachedMinute; return true; } _cachedHour = dt.hour; _cachedMinute = dt.minute; _lastPollMs = now; _hasCachedTime = true; hour = _cachedHour; minute = _cachedMinute; return true; } bool HalClock::formatTime(char* buf, size_t bufSize, uint8_t utcOffsetQuarterHoursBiased, bool use12Hour) const { if (bufSize < (use12Hour ? 9u : 6u)) return false; uint8_t h, m; if (!getTime(h, m)) return false; // Apply UTC offset: convert biased value to signed quarter-hours. // Clamp against corrupted persisted values so display time can't drift outside [-12:00, +14:00]. if (utcOffsetQuarterHoursBiased > 104) utcOffsetQuarterHoursBiased = 104; int offsetQuarterHours = static_cast(utcOffsetQuarterHoursBiased) - 48; int totalMinutes = static_cast(h) * 60 + static_cast(m) + offsetQuarterHours * 15; // Wrap around 24 hours totalMinutes = ((totalMinutes % 1440) + 1440) % 1440; const int hour24 = totalMinutes / 60; const int min = totalMinutes % 60; if (use12Hour) { const bool pm = hour24 >= 12; int hour12 = hour24 % 12; if (hour12 == 0) hour12 = 12; snprintf(buf, bufSize, "%d:%02d %s", hour12, min, pm ? "PM" : "AM"); } else { snprintf(buf, bufSize, "%02d:%02d", hour24, min); } return true; } bool HalClock::syncFromNTP() { if (!_available) return false; if (WiFi.status() != WL_CONNECTED) { LOG_ERR("CLK", "WiFi not connected, cannot sync NTP"); return false; } LOG_INF("CLK", "Starting NTP sync..."); configTzTime("UTC0", "pool.ntp.org", "time.nist.gov"); // Wait for SNTP sync to complete (up to 5 seconds) constexpr int maxAttempts = 50; for (int i = 0; i < maxAttempts; i++) { if (sntp_get_sync_status() == SNTP_SYNC_STATUS_COMPLETED) { time_t now = time(nullptr); struct tm timeinfo; gmtime_r(&now, &timeinfo); Rtc::DateTime dt; dt.year = static_cast(timeinfo.tm_year + 1900); dt.month = static_cast(timeinfo.tm_mon + 1); dt.day = static_cast(timeinfo.tm_mday); dt.hour = static_cast(timeinfo.tm_hour); dt.minute = static_cast(timeinfo.tm_min); dt.second = static_cast(timeinfo.tm_sec); dt.weekday = static_cast(timeinfo.tm_wday); if (_sdkRtc.set(dt)) { _lastPollMs = 0; _cachedHour = dt.hour; _cachedMinute = dt.minute; _hasCachedTime = true; LOG_INF("CLK", "RTC set to %04u-%02u-%02u %02u:%02u:%02u UTC", dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second); return true; } return false; } delay(100); } LOG_ERR("CLK", "NTP sync timed out"); return false; }