So I discovered some unexpected yet explainable behavior and looking for some guidance on best way to account for it. It seems the "gotcha" is here:
nRF52 platform (Boron, B-Series SoM, Argon, Tracker SoM):
stop()
is not available due to hardware limitations. Watchdog.start - Watchdog - Hardware | Reference | Particle
I recently added an infinite Ultra_Low_Power sleep mode any time the battery SoC is less than 5%. This way, the device enters an infinite sleep state until the user applies power. This should be better on the battery and also prevent many "brown outs" and other rare unattended side affects as the battery is fully depleted.
In my case, I'm using the Low_Bat_UC as the interrupt to immediately wake the device up if the user happened to apply power. This is my Low Battery Sleep State:
case st_LowBatterySlp:
{
// Turn off the software and hardware watchdog. We might sleep a very long time.
ab1805.stopWDT();
Watchdog.stop(); // This actually doesn't do anything on the nRF52 as once started it can't be stopped
//Configure for sleeping. The only way we can wake back up is from the interrupt that power was applied.
SystemSleepConfiguration config;
config.mode(SystemSleepMode::ULTRA_LOW_POWER)
.gpio(LOW_BAT_UC, FALLING);
SystemSleepResult result = System.sleep(config);
//When we do wake back up finally... start the watchdog and then publish the status - this will also cause us to connect to the cloud.
Watchdog.start(); //Start the Watchdog
ab1805.resumeWDT();
ab1805.updateWakeReason();
newState = st_PubStat; // Go connect to the cloud and publish something.
}
break;
This is the Watchdog implementation. I choose 250 min as it seemed acceptable in my scenario. Note, I also use an AB1805 external RTC as a hardware watchdog to protect against application code issues. I use the MCU hardware watchdog as more of a cloud side watchdog.
// Watchdog is always active and only gets pet when the device publishes a successful event. ACK.
Watchdog.init(WatchdogConfiguration()
.capabilities(WatchdogCap::SLEEP_RUNNING)
.timeout(250min));
Watchdog.start();
I only pet the watchdog with a successful publish ack using the PublishQueuePosix library callback. This is my "cloud side" watchdog. Basically if the device never gets an acknowledge from a publish event in 250 minutes, something is wrong and should also reset itself.
PublishQueuePosix::instance().withPublishCompleteUserCallback([](bool succeeded, const char *eventName, const char *eventData) {
if (succeeded){
Watchdog.refresh();
// Log.info("Refresh the watchdog... an event was successfully published with ACK: %s", eventName);
}
else{
// Log.error("Publish failed: %s", eventName);
}
});
So now the dilemma... if I want to sleep for an infinite period of time due to low battery, but once the watchdog is started, it can't be stopped, what do I do?
Option 1: Don't sleep for an infinite period of time, rather, sleep for say 240 minutes (some duration less than the hardware watchdog. Simply wake up, pet the watchdog manually here as well, check the battery SoC again and if it is still below 5% then just stay in the same state and immediately go back to sleep. This should be minimal impact to battery SoC every 240 minutes.
Option 2: Before sleeping, update the hardware watchdog timeout to the maximum value 131071 sec or ~1.5 days, likely still check.
Option 3: Somehow some way stop the watchdog? Seems strange to not be able to stop it once it's started but that's what the docs say and I believe the docs.
Other ideas?