From 7f9456e8c6a025b03daa2a563b31611c8717c44f Mon Sep 17 00:00:00 2001 From: jpirnay Date: Thu, 26 Mar 2026 21:41:39 +0100 Subject: [PATCH] Add calibration --- lib/hal/HalClock.cpp | 50 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/lib/hal/HalClock.cpp b/lib/hal/HalClock.cpp index 4d32dcbb..83042731 100644 --- a/lib/hal/HalClock.cpp +++ b/lib/hal/HalClock.cpp @@ -19,6 +19,7 @@ RTC_NOINIT_ATTR static uint32_t rtcClockMagic; RTC_NOINIT_ATTR static uint32_t rtcClockFlags; RTC_NOINIT_ATTR static time_t rtcEpoch; // last-known unix epoch RTC_NOINIT_ATTR static uint64_t rtcLpTimeUs; // esp_clk_rtc_time() at capture +RTC_NOINIT_ATTR static uint32_t rtcSlowCal; // esp_clk_slowclk_cal_get() at capture static bool clockApproximate = true; @@ -47,8 +48,13 @@ static constexpr TimeZoneEntry TIMEZONES[] = { // ---- NVS helpers ---------------------------------------------------------- +// If the last NTP sync is older than this, treat a cold-boot restore as +// unsynced rather than showing a potentially very wrong time. +static constexpr int64_t STALE_THRESHOLD_S = 72 * 3600; // 72 hours + static constexpr char NVS_NAMESPACE[] = "halclock"; static constexpr char NVS_KEY[] = "epoch"; +static constexpr char NVS_SYNC_KEY[] = "lastsync"; static void nvsWrite(time_t epoch) { Preferences prefs; @@ -58,6 +64,14 @@ static void nvsWrite(time_t epoch) { } } +static void nvsWriteSyncTime(time_t syncEpoch) { + Preferences prefs; + if (prefs.begin(NVS_NAMESPACE, false)) { + prefs.putLong64(NVS_SYNC_KEY, (int64_t)syncEpoch); + prefs.end(); + } +} + static time_t nvsRead() { Preferences prefs; time_t epoch = 0; @@ -68,6 +82,16 @@ static time_t nvsRead() { return epoch; } +static time_t nvsReadSyncTime() { + Preferences prefs; + time_t syncEpoch = 0; + if (prefs.begin(NVS_NAMESPACE, true)) { + syncEpoch = (time_t)prefs.getLong64(NVS_SYNC_KEY, 0); + prefs.end(); + } + return syncEpoch; +} + // ---- internal helpers ----------------------------------------------------- static void setSystemClock(time_t epoch) { @@ -82,6 +106,7 @@ static bool rtcValid() { return rtcClockMagic == CLOCK_RTC_MAGIC && rtcEpoch > 0 static void capture(bool lpValid) { rtcEpoch = time(nullptr); rtcLpTimeUs = esp_clk_rtc_time(); + rtcSlowCal = esp_clk_slowclk_cal_get(); rtcClockMagic = CLOCK_RTC_MAGIC; rtcClockFlags = lpValid ? CLOCK_RTC_FLAG_LP_VALID : 0; nvsWrite(rtcEpoch); @@ -120,6 +145,7 @@ bool syncNtp() { } capture(false); + nvsWriteSyncTime(rtcEpoch); clockApproximate = false; LOG_INF("CLK", "NTP synced, epoch %lld", (long long)rtcEpoch); return true; @@ -138,15 +164,30 @@ void restore() { if (rtcValid() && lpValid) { // RTC memory survived — we woke from deep sleep. // Use the LP timer to compute how much time elapsed during sleep. + // Apply calibration correction: the slow-clock frequency may have + // drifted (temperature) between when we captured and now. The fresh + // boot-time calibration (calNow) is our best estimate of the actual + // frequency during sleep. uint64_t lpNow = esp_clk_rtc_time(); time_t estimated = rtcEpoch; if (lpNow > rtcLpTimeUs) { - estimated += (time_t)((lpNow - rtcLpTimeUs) / 1000000LL); + uint32_t calNow = esp_clk_slowclk_cal_get(); + uint64_t elapsedUs; + if (rtcSlowCal != 0 && calNow != 0) { + // rtcLpTimeUs was computed with rtcSlowCal; convert it to the + // current calibration basis so the subtraction is consistent. + uint64_t lpThenCorrected = (uint64_t)((double)rtcLpTimeUs * calNow / rtcSlowCal); + elapsedUs = lpNow - lpThenCorrected; + } else { + elapsedUs = lpNow - rtcLpTimeUs; + } + estimated += (time_t)(elapsedUs / 1000000LL); } setSystemClock(estimated); // Re-capture with current LP baseline rtcEpoch = estimated; rtcLpTimeUs = lpNow; + rtcSlowCal = esp_clk_slowclk_cal_get(); clockApproximate = true; LOG_INF("CLK", "Restored from RTC + LP timer, epoch %lld", (long long)estimated); return; @@ -155,9 +196,16 @@ void restore() { // Cold boot — try NVS. No elapsed correction possible. time_t epoch = nvsRead(); if (epoch > 0) { + time_t lastSync = nvsReadSyncTime(); + if (lastSync > 0 && (epoch - lastSync) > STALE_THRESHOLD_S) { + LOG_ERR("CLK", "NVS epoch %lld is stale (last NTP sync %lld, %lld h ago), discarding", (long long)epoch, + (long long)lastSync, (long long)((epoch - lastSync) / 3600)); + return; + } setSystemClock(epoch); rtcEpoch = epoch; rtcLpTimeUs = esp_clk_rtc_time(); + rtcSlowCal = esp_clk_slowclk_cal_get(); rtcClockMagic = CLOCK_RTC_MAGIC; rtcClockFlags = 0; clockApproximate = true;