From 0ec2475bcf813a3cdf8dae54d4d4489c692acc7e Mon Sep 17 00:00:00 2001 From: Alexander Hoffer Date: Mon, 20 Jul 2026 15:00:19 +0100 Subject: [PATCH] fix: initialize RTC after power loss --- src/helpers/AutoDiscoverRTCClock.cpp | 9 +++++++ src/helpers/RTCValidity.h | 14 ++++++++++ test/test_rtc_validity/test_rtc_validity.cpp | 28 ++++++++++++++++++++ 3 files changed, 51 insertions(+) create mode 100644 src/helpers/RTCValidity.h create mode 100644 test/test_rtc_validity/test_rtc_validity.cpp diff --git a/src/helpers/AutoDiscoverRTCClock.cpp b/src/helpers/AutoDiscoverRTCClock.cpp index af08fddb03..fa260faf0b 100644 --- a/src/helpers/AutoDiscoverRTCClock.cpp +++ b/src/helpers/AutoDiscoverRTCClock.cpp @@ -1,4 +1,5 @@ #include "AutoDiscoverRTCClock.h" +#include "RTCValidity.h" #include "RTClib.h" #include #include "RTC_RX8130CE.h" @@ -30,6 +31,10 @@ void AutoDiscoverRTCClock::begin(TwoWire& wire) { #if !defined(DISABLE_DS3231_PROBE) if (i2c_probe(wire, DS3231_ADDRESS)) { ds3231_success = rtc_3231.begin(&wire); + if (ds3231_success && mesh::seedRTCIfLostPower(rtc_3231.lostPower(), _fallback->getCurrentTime(), + [](uint32_t time) { rtc_3231.adjust(DateTime(time)); })) { + MESH_DEBUG_PRINTLN("DS3231: Lost power, initialized from fallback clock"); + } } #endif @@ -44,6 +49,10 @@ void AutoDiscoverRTCClock::begin(TwoWire& wire) { if (i2c_probe(wire, PCF8563_ADDRESS)) { MESH_DEBUG_PRINTLN("PCF8563: Found"); rtc_8563_success = rtc_8563.begin(&wire); + if (rtc_8563_success && mesh::seedRTCIfLostPower(rtc_8563.lostPower(), _fallback->getCurrentTime(), + [](uint32_t time) { rtc_8563.adjust(DateTime(time)); })) { + MESH_DEBUG_PRINTLN("PCF8563: Lost power, initialized from fallback clock"); + } } if (i2c_probe(wire, RX8130CE_ADDRESS)) { diff --git a/src/helpers/RTCValidity.h b/src/helpers/RTCValidity.h new file mode 100644 index 0000000000..8410fefcbe --- /dev/null +++ b/src/helpers/RTCValidity.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace mesh { + +template +bool seedRTCIfLostPower(bool lost_power, uint32_t fallback_time, AdjustClock adjust_clock) { + if (!lost_power) return false; + adjust_clock(fallback_time); + return true; +} + +} // namespace mesh diff --git a/test/test_rtc_validity/test_rtc_validity.cpp b/test/test_rtc_validity/test_rtc_validity.cpp new file mode 100644 index 0000000000..9fac3e24d7 --- /dev/null +++ b/test/test_rtc_validity/test_rtc_validity.cpp @@ -0,0 +1,28 @@ +#include + +#include + +TEST(RTCValidity, SeedsLostPowerClockFromFallback) { + uint32_t adjusted_to = 0; + + bool seeded = mesh::seedRTCIfLostPower(true, 1715770351, + [&](uint32_t time) { adjusted_to = time; }); + + EXPECT_TRUE(seeded); + EXPECT_EQ(1715770351u, adjusted_to); +} + +TEST(RTCValidity, PreservesValidBatteryBackedClock) { + bool adjusted = false; + + bool seeded = mesh::seedRTCIfLostPower(false, 1715770351, + [&](uint32_t) { adjusted = true; }); + + EXPECT_FALSE(seeded); + EXPECT_FALSE(adjusted); +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}