Help preserving RTC over System or Watchdog resets

I have an Argon / Gen3 application running deviceOS@6.1.0 where most of the time I cannot connect to the network, so it is important that the RTC (counter) time is preserved over System or Watchdog resets from when it last successfully synced to the Cloud.

According to the docs, this should not be a problem:

BUT

  • A simple test shows that retained memory works fine across reboots, but RTC time does not! (see my code + results below).
  • [I did also try to save 'Time.now()' to retained memory using the Watchdog.onExpired handler, but a) this shouldn’t be necessary and b) it didn’t work anyway]

There was a similar issue raised back in 2021, but it looks like the problem still persists: [Boron RTC not retaining time after System.reset]

This is a really major problem for me since Cloud sync is not readily available.

Any thoughts / advice gratefully received!

SYSTEM_MODE(SEMI_AUTOMATIC);
SYSTEM_THREAD(ENABLED);

SerialLogHandler logHandler(LOG_LEVEL_WARN, { { "app", LOG_LEVEL_INFO } });

retained int savedValue = 0;

void setup() {
  Serial.begin();
  waitFor(Serial.isConnected, 10000);
  if (!Time.isValid()) {
    Log.info("Invalid time: " + Time.timeStr());
    Particle.connect();
    waitUntil(Time.isValid);
  }
  Watchdog.init(WatchdogConfiguration().timeout(40s));
  Watchdog.start();
}

void loop() {
    Log.info("Saved value= " + String(savedValue++) + "; Current time= " + Time.timeStr());
    delay(10000);
    // Should reset every 40s since we don't call Watchdog.refresh()
}

Results in...

0000001036 [app] INFO: Invalid time: Sat Jan  1 00:00:01 2000
*** As expected, the system will need to connect / sync on initial power-up
0000010206 [system] WARN: Failed to load session data from persistent storage
0000011947 [app] INFO: Saved value= 0; Current time= Thu Jun  6 20:22:44 2024
0000021948 [app] INFO: Saved value= 1; Current time= Thu Jun  6 20:22:54 2024
0000031949 [app] INFO: Saved value= 2; Current time= Thu Jun  6 20:23:04 2024
0000041950 [app] INFO: Saved value= 3; Current time= Thu Jun  6 20:23:14 2024
0000000901 [app] INFO: Invalid time: Sat Jan  1 00:00:00 2000
*** The system had to re-connect at this point (which it should not need to do)
*** If the time were valid then since SEMI_AUTOMATIC, we would not connect  at all on reboot
0000030376 [app] INFO: Saved value= 4; Current time= Thu Jun  6 20:23:55 2024
0000040378 [app] INFO: Saved value= 5; Current time= Thu Jun  6 20:24:05 2024
0000050379 [app] INFO: Saved value= 6; Current time= Thu Jun  6 20:24:15 2024

nRF52 devices don't have a true real-time clock. It's just a counter in the MCU. However I don't think that counter is in the backup domain and is not preserved on reset.

I would save the Time.now() value in retained memory from loop, maybe once per second, instead of using an onExpired handler. The onExpired handler runs its callback as a true ISR and I'm not completely convinced Time.now is interrupt safe. Also doing it from loop should work when the reset button is pressed.

This technique would only apply to nRF52 (Gen 3). It won't work on RTL872x (Gen 4) because retained memory won't be preserved on Gen 4 in this scenario.

However the best solution if you need time preserved across reset and hibernate sleep is to use an external RTC. The Tracker and the Muon use the AM1805/AB1805 which is both an RTC and a hardware watchdog, and is connected by I2C. It can be powered from your LiPo battery, or from a coin cell or supercap.

Thanks @rickkas7 ! It's a pity that the Gen3 RTC (Counter) does not appear to be in the backup domain (maybe this could be suggested for the next deviceOS update?) but your suggestion is a very helpful workaround :slight_smile: !