diff --git a/src/helpers/NRF52Board.cpp b/src/helpers/NRF52Board.cpp index 2c8753d464..c83f40bf0d 100644 --- a/src/helpers/NRF52Board.cpp +++ b/src/helpers/NRF52Board.cpp @@ -1,6 +1,10 @@ #if defined(NRF52_PLATFORM) #include "NRF52Board.h" +// Single definitions for noinit backup variables (declared extern in NRF52Board.h) +uint32_t _noinit_backup_time __attribute__((section(".noinit"))); +uint32_t _noinit_backup_magic __attribute__((section(".noinit"))); + #include #include diff --git a/src/helpers/NRF52Board.h b/src/helpers/NRF52Board.h index 96f67dc950..604aea14b1 100644 --- a/src/helpers/NRF52Board.h +++ b/src/helpers/NRF52Board.h @@ -5,6 +5,47 @@ #if defined(NRF52_PLATFORM) +// noinit variables survive watchdog, soft, pin, and lockup resets (RAM retained). +// Lost on power-on and System OFF (magic check handles this). +extern uint32_t _noinit_backup_time __attribute__((section(".noinit"))); +extern uint32_t _noinit_backup_magic __attribute__((section(".noinit"))); +#define NRF52_BACKUP_MAGIC 0xAA55CC33 +#define NRF52_TIME_MIN 1772323200 // 1 Mar 2026 + +class NRF52RTCClock : public mesh::RTCClock { + uint32_t base_time; + uint64_t accumulator; + unsigned long prev_millis; +public: + NRF52RTCClock() { + if (_noinit_backup_magic == NRF52_BACKUP_MAGIC && _noinit_backup_time > NRF52_TIME_MIN) { + base_time = _noinit_backup_time; + } else { + base_time = NRF52_TIME_MIN; + } + accumulator = 0; + prev_millis = millis(); + } + uint32_t getCurrentTime() override { return base_time + accumulator / 1000; } + void setCurrentTime(uint32_t time) override { + base_time = time; + accumulator = 0; + prev_millis = millis(); + _noinit_backup_time = time; + _noinit_backup_magic = NRF52_BACKUP_MAGIC; + } + void tick() override { + unsigned long now = millis(); + accumulator += (now - prev_millis); + prev_millis = now; + uint32_t current = base_time + accumulator / 1000; + if (current > NRF52_TIME_MIN && current != _noinit_backup_time) { + _noinit_backup_time = current; + _noinit_backup_magic = NRF52_BACKUP_MAGIC; + } + } +}; + #ifdef NRF52_POWER_MANAGEMENT // Shutdown Reason Codes (stored in GPREGRET before SYSTEMOFF) #define SHUTDOWN_REASON_NONE 0x00 diff --git a/variants/heltec_mesh_solar/target.cpp b/variants/heltec_mesh_solar/target.cpp index 1ea33e1f2e..c2e828ccab 100644 --- a/variants/heltec_mesh_solar/target.cpp +++ b/variants/heltec_mesh_solar/target.cpp @@ -9,7 +9,7 @@ RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BU WRAPPER_CLASS radio_driver(radio, board); -VolatileRTCClock fallback_clock; +NRF52RTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); SolarSensorManager sensors = SolarSensorManager(nmea); diff --git a/variants/heltec_t114/target.cpp b/variants/heltec_t114/target.cpp index 6a30a4d18e..2ac89f652b 100644 --- a/variants/heltec_t114/target.cpp +++ b/variants/heltec_t114/target.cpp @@ -17,7 +17,7 @@ RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BU WRAPPER_CLASS radio_driver(radio, board); -VolatileRTCClock fallback_clock; +NRF52RTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); #if ENV_INCLUDE_GPS diff --git a/variants/ikoka_handheld_nrf/target.cpp b/variants/ikoka_handheld_nrf/target.cpp index 48244e1722..994c301e4c 100644 --- a/variants/ikoka_handheld_nrf/target.cpp +++ b/variants/ikoka_handheld_nrf/target.cpp @@ -8,7 +8,7 @@ RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BU WRAPPER_CLASS radio_driver(radio, board); -VolatileRTCClock fallback_clock; +NRF52RTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); EnvironmentSensorManager sensors; diff --git a/variants/ikoka_nano_nrf/target.cpp b/variants/ikoka_nano_nrf/target.cpp index be20cfb436..13585499a0 100644 --- a/variants/ikoka_nano_nrf/target.cpp +++ b/variants/ikoka_nano_nrf/target.cpp @@ -13,7 +13,7 @@ RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BU WRAPPER_CLASS radio_driver(radio, board); -VolatileRTCClock fallback_clock; +NRF52RTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); EnvironmentSensorManager sensors; diff --git a/variants/ikoka_stick_nrf/target.cpp b/variants/ikoka_stick_nrf/target.cpp index 4f6befc609..03f7a1b5e3 100644 --- a/variants/ikoka_stick_nrf/target.cpp +++ b/variants/ikoka_stick_nrf/target.cpp @@ -13,7 +13,7 @@ RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BU WRAPPER_CLASS radio_driver(radio, board); -VolatileRTCClock fallback_clock; +NRF52RTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); EnvironmentSensorManager sensors; diff --git a/variants/keepteen_lt1/target.cpp b/variants/keepteen_lt1/target.cpp index 85f11232ad..2053cdf06b 100644 --- a/variants/keepteen_lt1/target.cpp +++ b/variants/keepteen_lt1/target.cpp @@ -8,7 +8,7 @@ RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BU WRAPPER_CLASS radio_driver(radio, board); -VolatileRTCClock fallback_clock; +NRF52RTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); #if ENV_INCLUDE_GPS #include diff --git a/variants/lilygo_techo/target.cpp b/variants/lilygo_techo/target.cpp index 12d222ff78..238fa83650 100644 --- a/variants/lilygo_techo/target.cpp +++ b/variants/lilygo_techo/target.cpp @@ -9,7 +9,7 @@ RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BU WRAPPER_CLASS radio_driver(radio, board); -VolatileRTCClock fallback_clock; +NRF52RTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); #ifdef ENV_INCLUDE_GPS diff --git a/variants/lilygo_techo_lite/target.cpp b/variants/lilygo_techo_lite/target.cpp index 40a94526ed..c926ea3093 100644 --- a/variants/lilygo_techo_lite/target.cpp +++ b/variants/lilygo_techo_lite/target.cpp @@ -9,7 +9,7 @@ RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BU WRAPPER_CLASS radio_driver(radio, board); -VolatileRTCClock fallback_clock; +NRF52RTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); #ifdef ENV_INCLUDE_GPS diff --git a/variants/mesh_pocket/target.cpp b/variants/mesh_pocket/target.cpp index 6fabb31742..ff3e8f8bbd 100644 --- a/variants/mesh_pocket/target.cpp +++ b/variants/mesh_pocket/target.cpp @@ -11,7 +11,7 @@ WRAPPER_CLASS radio_driver(radio, board); SensorManager sensors = SensorManager(); -VolatileRTCClock fallback_clock; +NRF52RTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); #ifdef DISPLAY_CLASS diff --git a/variants/meshtiny/target.cpp b/variants/meshtiny/target.cpp index 9188db1741..6965ed75de 100644 --- a/variants/meshtiny/target.cpp +++ b/variants/meshtiny/target.cpp @@ -9,7 +9,7 @@ RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BU WRAPPER_CLASS radio_driver(radio, board); -VolatileRTCClock fallback_clock; +NRF52RTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); EnvironmentSensorManager sensors = EnvironmentSensorManager(); diff --git a/variants/minewsemi_me25ls01/target.cpp b/variants/minewsemi_me25ls01/target.cpp index fcec194190..554dfbf839 100644 --- a/variants/minewsemi_me25ls01/target.cpp +++ b/variants/minewsemi_me25ls01/target.cpp @@ -7,7 +7,7 @@ RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BU WRAPPER_CLASS radio_driver(radio, board); -VolatileRTCClock rtc_clock; +NRF52RTCClock rtc_clock; extern EnvironmentSensorManager sensors; #if ENV_INCLUDE_GPS #include diff --git a/variants/minewsemi_me25ls01/target.h b/variants/minewsemi_me25ls01/target.h index ea7383e254..271777a581 100644 --- a/variants/minewsemi_me25ls01/target.h +++ b/variants/minewsemi_me25ls01/target.h @@ -19,7 +19,7 @@ extern MinewsemiME25LS01Board board; extern WRAPPER_CLASS radio_driver; -extern VolatileRTCClock rtc_clock; +extern NRF52RTCClock rtc_clock; extern EnvironmentSensorManager sensors; bool radio_init(); diff --git a/variants/nano_g2_ultra/target.cpp b/variants/nano_g2_ultra/target.cpp index bd4e9b4807..8fd1f57c3e 100644 --- a/variants/nano_g2_ultra/target.cpp +++ b/variants/nano_g2_ultra/target.cpp @@ -10,7 +10,7 @@ RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BU WRAPPER_CLASS radio_driver(radio, board); -VolatileRTCClock fallback_clock; +NRF52RTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); NanoG2UltraSensorManager sensors = NanoG2UltraSensorManager(nmea); diff --git a/variants/promicro/target.cpp b/variants/promicro/target.cpp index e4a4442ab1..7a5609f859 100644 --- a/variants/promicro/target.cpp +++ b/variants/promicro/target.cpp @@ -8,7 +8,7 @@ RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BU WRAPPER_CLASS radio_driver(radio, board); -VolatileRTCClock fallback_clock; +NRF52RTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); #if ENV_INCLUDE_GPS #include diff --git a/variants/rak3401/target.cpp b/variants/rak3401/target.cpp index 77fb0e5f2d..fa22f8b084 100644 --- a/variants/rak3401/target.cpp +++ b/variants/rak3401/target.cpp @@ -21,7 +21,7 @@ RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BU WRAPPER_CLASS radio_driver(radio, board); -VolatileRTCClock fallback_clock; +NRF52RTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); #if ENV_INCLUDE_GPS diff --git a/variants/rak4631/target.cpp b/variants/rak4631/target.cpp index ac1ac7cacb..a775e6000a 100644 --- a/variants/rak4631/target.cpp +++ b/variants/rak4631/target.cpp @@ -21,7 +21,7 @@ RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BU WRAPPER_CLASS radio_driver(radio, board); -VolatileRTCClock fallback_clock; +NRF52RTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); #if ENV_INCLUDE_GPS diff --git a/variants/rak_wismesh_tag/target.cpp b/variants/rak_wismesh_tag/target.cpp index d42c0d58eb..7bbc351e26 100644 --- a/variants/rak_wismesh_tag/target.cpp +++ b/variants/rak_wismesh_tag/target.cpp @@ -17,7 +17,7 @@ RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BU WRAPPER_CLASS radio_driver(radio, board); -VolatileRTCClock fallback_clock; +NRF52RTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); #if ENV_INCLUDE_GPS diff --git a/variants/sensecap_solar/target.cpp b/variants/sensecap_solar/target.cpp index 9f5495155f..82dae512ee 100644 --- a/variants/sensecap_solar/target.cpp +++ b/variants/sensecap_solar/target.cpp @@ -10,7 +10,7 @@ RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BU WRAPPER_CLASS radio_driver(radio, board); -VolatileRTCClock fallback_clock; +NRF52RTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); #ifdef ENV_INCLUDE_GPS MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); diff --git a/variants/t1000-e/target.cpp b/variants/t1000-e/target.cpp index da8fa48bb3..9f9d88a5b0 100644 --- a/variants/t1000-e/target.cpp +++ b/variants/t1000-e/target.cpp @@ -9,7 +9,7 @@ RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BU WRAPPER_CLASS radio_driver(radio, board); -VolatileRTCClock rtc_clock; +NRF52RTCClock rtc_clock; MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); T1000SensorManager sensors = T1000SensorManager(nmea); diff --git a/variants/t1000-e/target.h b/variants/t1000-e/target.h index d4e3c02c52..2bd86993e9 100644 --- a/variants/t1000-e/target.h +++ b/variants/t1000-e/target.h @@ -37,7 +37,7 @@ class T1000SensorManager: public SensorManager { extern T1000eBoard board; extern WRAPPER_CLASS radio_driver; -extern VolatileRTCClock rtc_clock; +extern NRF52RTCClock rtc_clock; extern T1000SensorManager sensors; bool radio_init(); diff --git a/variants/thinknode_m1/target.cpp b/variants/thinknode_m1/target.cpp index ec2438d404..24f29b9cc8 100644 --- a/variants/thinknode_m1/target.cpp +++ b/variants/thinknode_m1/target.cpp @@ -9,7 +9,7 @@ RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BU WRAPPER_CLASS radio_driver(radio, board); -VolatileRTCClock fallback_clock; +NRF52RTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); ThinkNodeM1SensorManager sensors = ThinkNodeM1SensorManager(nmea); diff --git a/variants/thinknode_m3/target.cpp b/variants/thinknode_m3/target.cpp index 7303eb4ca9..a2b2f02ebb 100644 --- a/variants/thinknode_m3/target.cpp +++ b/variants/thinknode_m3/target.cpp @@ -8,7 +8,7 @@ RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BU WRAPPER_CLASS radio_driver(radio, board); -VolatileRTCClock fallback_clock; +NRF52RTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); #ifdef ENV_INCLUDE_GPS MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); diff --git a/variants/thinknode_m6/target.cpp b/variants/thinknode_m6/target.cpp index 36ca861805..798d8f31bb 100644 --- a/variants/thinknode_m6/target.cpp +++ b/variants/thinknode_m6/target.cpp @@ -9,7 +9,7 @@ RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BU WRAPPER_CLASS radio_driver(radio, board); -VolatileRTCClock fallback_clock; +NRF52RTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); #ifdef ENV_INCLUDE_GPS MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Serial1, &rtc_clock); diff --git a/variants/wio-tracker-l1/target.cpp b/variants/wio-tracker-l1/target.cpp index 4575a76c85..a23df3532e 100644 --- a/variants/wio-tracker-l1/target.cpp +++ b/variants/wio-tracker-l1/target.cpp @@ -9,7 +9,7 @@ RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BU WRAPPER_CLASS radio_driver(radio, board); -VolatileRTCClock fallback_clock; +NRF52RTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); #ifdef ENV_INCLUDE_GPS diff --git a/variants/wio_wm1110/target.cpp b/variants/wio_wm1110/target.cpp index 457d5bda2c..7312657285 100644 --- a/variants/wio_wm1110/target.cpp +++ b/variants/wio_wm1110/target.cpp @@ -8,7 +8,7 @@ RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BU WRAPPER_CLASS radio_driver(radio, board); -VolatileRTCClock rtc_clock; +NRF52RTCClock rtc_clock; EnvironmentSensorManager sensors; #ifndef LORA_CR diff --git a/variants/wio_wm1110/target.h b/variants/wio_wm1110/target.h index 8712a0ef0b..d6bc0e4a1a 100644 --- a/variants/wio_wm1110/target.h +++ b/variants/wio_wm1110/target.h @@ -10,7 +10,7 @@ extern WioWM1110Board board; extern WRAPPER_CLASS radio_driver; -extern VolatileRTCClock rtc_clock; +extern NRF52RTCClock rtc_clock; extern EnvironmentSensorManager sensors; bool radio_init(); diff --git a/variants/xiao_nrf52/target.cpp b/variants/xiao_nrf52/target.cpp index a8f4162eab..61430acdb3 100644 --- a/variants/xiao_nrf52/target.cpp +++ b/variants/xiao_nrf52/target.cpp @@ -12,7 +12,7 @@ RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BU WRAPPER_CLASS radio_driver(radio, board); -VolatileRTCClock fallback_clock; +NRF52RTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); EnvironmentSensorManager sensors;