Boron 404 - Watchdog timer not shutting down before sleep?

Hello, I am going to post my full code below so you can see how I am trying to implement the watchdog:

#include <SdFat.h>
#include <ThingSpeak.h>
#include <Adafruit_MLX90614.h>

#define SPI_CONFIGURATION 0 //for uSD card
#define MOSFET_GATE_CONTROL D3
#define D_DATA D2 

//STARTUP(System.enableFeature(FEATURE_RETAINED_MEMORY));
SYSTEM_THREAD(ENABLED);
SystemSleepConfiguration config;

//int SleepInterval = (43200000 - 30000);
//int SleepInterval = (7200000 - 30000); 
int SleepInterval = (3600000 - 30000); //30s seems decent at migtigating drift, but only in the lab??
int failCounter = 0; 
int loopCounter = 0;

TCPClient client; 
unsigned long myChannelNumber = 123456789; 
const char* myWriteAPIKey = "watchdogneverrests";
String diststr, tempstr, ambystr, bvoltstr;

double MB_7368_OUTPUT = 0; 
double wDistance = 0;
//double GWelevation = 0; //to be used later

Adafruit_MLX90614 mlx = Adafruit_MLX90614();
double WaterTemp = 0; 
double AmbyTemp = 0;

FuelGauge fuel; 
float bVoltage1; 
double bVoltage2; 

SdFat sd;
const uint8_t chipSelect = SS;
File myFile;

void setup() 
{
    pinMode(D_DATA, INPUT);
    pinMode(MOSFET_GATE_CONTROL, OUTPUT);
    digitalWrite(MOSFET_GATE_CONTROL, LOW);

    Particle.variable("Distance", &wDistance, DOUBLE);
    Particle.variable("Temperature", &WaterTemp, DOUBLE);
    Particle.variable("AmbientTemp", &AmbyTemp, DOUBLE);
    Particle.variable("BatteryVoltage", &bVoltage2, DOUBLE);
    
    Time.zone(-4); 
    ThingSpeak.begin(client);
    mlx.begin();
    //Serial.begin(9600);
    
    //Watchdog.init(WatchdogConfiguration().timeout(180s)); //The watchdog is automatically stopped before sleep mode and when reset??
    //Watchdog.start();
    config.mode(SystemSleepMode::ULTRA_LOW_POWER);
    //config.duration(SleepInterval);
}

void loop() 
{
    BeginAgain: //this stuff is not necessary now after rewrite?
    //Watchdog.refresh();
    LoopCheck();
    Measurements();
    WriteSD(); 
    
    //waitFor() certainly blocks, so must set watchdog timer to be > 120s??
    if(waitFor(Particle.connected, 120000)) //try to connect for two minutes
    {
        if(wDistance <= 0.5 || wDistance >= 9.99 || WaterTemp <= -50 || WaterTemp >= 50 || AmbyTemp <= -50 || AmbyTemp >= 50) //if there are bad values which will ruin the graphs, do this
        {
            ParticleCloudPost(); 
            //config.duration(sleep_time());
            config.duration(SleepInterval);
            System.sleep(config);
            goto BeginAgain;
        }
        else //else we're good, do this
        {
            ParticleCloudPost();
            ThingSpeakPost();
            //config.duration(sleep_time());
            config.duration(SleepInterval);
            System.sleep(config);
            goto BeginAgain;
        }
    }
    else //connection attempt failed, do this
    {
        FailFunction();
        //config.duration(sleep_time());
        config.duration(SleepInterval);
        System.sleep(config);
        goto BeginAgain;
    }
}

void LoopCheck() //reset the boron once per week
{
    loopCounter = loopCounter + 1;
    if(loopCounter == 14)
    {
        //loopCounter = 0;
        //Serial.printlnf("loopCounter = %d, system reset imminent", loopCounter);
        System.reset();
    }
    //Watchdog.refresh();
}
void FailFunction() //reset the boron after three connection attempts have failed
{
    failCounter = failCounter + 1;
    if(failCounter == 3)
    {
        //failCounter = 0;
        //Serial.printlnf("failCounter = %d, system reset imminent", failCounter);
        System.reset();
    }
    //Watchdog.refresh();
}
int sleep_time()
{
    int sleep_time;
    int mins, secs;
    mins = Time.minute();
    secs = Time.second();
    mins %= 55;
    sleep_time = (55 - mins) * 60000 - secs * 1000;
    //Watchdog.refresh();
    return sleep_time;
}
void Measurements()
{
    digitalWrite(MOSFET_GATE_CONTROL, HIGH);
    delay(500);                                 
    MB_7368_OUTPUT = pulseIn(D_DATA, HIGH);   
    wDistance = (MB_7368_OUTPUT / 1000); //converts from mm to meters
    //Depth = Depth + HangDown;   // *****UNCOMMENT IN FIELD*****
    delay(500);
	for(int i = 0; i < 5; i++) 
	{
		WaterTemp = mlx.readObjectTempC();
		delay(500);
	}
	for(int i = 0; i < 5; i++)
	{
	    AmbyTemp = mlx.readAmbientTempC();
	    delay(500);
	}
	digitalWrite(MOSFET_GATE_CONTROL, LOW);
    bVoltage1 = fuel.getSoC(); //measure remaining battery life
    bVoltage2 = bVoltage1;    //float to double 
    
    // wDistance = random(10);
    // WaterTemp = random(25);
    // AmbyTemp = random(25);
    // bVoltage1 = fuel.getSoC(); //measure remaining battery
    // bVoltage2 = bVoltage1;    //float to double 
    
    diststr = String(wDistance);
    tempstr = String(WaterTemp);
    ambystr = String (AmbyTemp);
    bvoltstr = String(bVoltage2);
    //Watchdog.refresh();
}
void WriteSD() //writing 1999 as the date now??
{
    digitalWrite(MOSFET_GATE_CONTROL, HIGH); //concern about pin switching speed? merge measurement and writesd functions?
    if (!sd.begin(chipSelect, SPI_FULL_SPEED)) 
	{
		//Serial.println("failed to open card");
		//return;
	}
	if (!myFile.open("WaterSecurityData.txt", O_RDWR | O_CREAT | O_AT_END)) 
	{
		//Serial.println("opening WaterSecurityData.txt for write failed");
		//return;
	}
    myFile.printf(Time.format(Time.now(), TIME_FORMAT_ISO8601_FULL)); 
    myFile.printf("  %.3f m  %.3f C  %.3f C  %.3f %%\n", wDistance, WaterTemp, AmbyTemp, bVoltage2);
	myFile.close();
	digitalWrite(MOSFET_GATE_CONTROL, LOW);
	//Watchdog.refresh();
}
void ParticleCloudPost()
{
    Particle.publish("Distance", diststr, PRIVATE);         
    delay(1000);                                            
    Particle.publish("Temperature", tempstr, PRIVATE);      
    delay(1000);    
    Particle.publish("AmbientTemp", ambystr, PRIVATE);
    delay(1000);
    Particle.publish("BatteryVoltage", bvoltstr, PRIVATE);  
    delay(1000);
    //Watchdog.refresh();
}
void ThingSpeakPost()
{
    ThingSpeak.setField(1, diststr);
    delay(500);
    ThingSpeak.setField(2, tempstr);
    delay(500);
    ThingSpeak.setField(3, ambystr);
    delay(500);
    ThingSpeak.setField(4, bvoltstr);
    delay(500);
    ThingSpeak.writeFields(myChannelNumber, myWriteAPIKey);
    //Watchdog.refresh();
}

As you can see, I currently have the watchdog stuff commented out, and my code works fine/does what I expect it to do. If I uncomment the watchdog stuff, my code will execute, post what I want, and then go to sleep. However, the Boron wakes up shortly after going to sleep with the watchdog stuff in the code, and I am not sure what to do.

The watchdog documentation explicitly states: “The watchdog is automatically stopped before sleep mode and when reset” however this does not appear to be the case for me? Is my implementation incorrect? I initialize the timer in the setup() and then start it. Then I proceed to refresh the watchdog plenty of times before it would ever reach three minutes. Then my Boron goes to sleep and wakes up shortly after? Not sure what is wrong here. I’ve tried the device OS 5.3.0 default and prerelease, as well as the 5.3.1 prerelease and it’s the same in all cases. Unless of course this only applies to the old version of the Boron? I have since switched to the 404 model and have not tried this on the older versions.

I was able to reproduce waking up too early on the BRN404 running Device OS 5.3.2 using this code:

#include "Particle.h"

SerialLogHandler logHandler;
SYSTEM_THREAD(ENABLED);
SYSTEM_MODE(SEMI_AUTOMATIC);

enum {
    STATE_WAITING_TO_CONNECT,
    STATE_CONNECTED,
    STATE_PREPARE_TO_SLEEP,
};
int state = STATE_WAITING_TO_CONNECT;
unsigned long stateTime = 0;

const std::chrono::milliseconds connectedTime = 45s;
const std::chrono::milliseconds sleepTime = 5min;


void setup() {
    waitFor(Serial.isConnected, 10000);
    delay(2000);

    Log.info("resetReason %d", (int)System.resetReason());

    Watchdog.init(WatchdogConfiguration().timeout(30s));
    Watchdog.start();

    Particle.connect();
}

void loop() {
    switch(state) {
        case STATE_WAITING_TO_CONNECT:
            if (Particle.connected()) {
                Log.info("connected to the cloud");
                stateTime = millis();
                state = STATE_CONNECTED;
            }
            break;

        case STATE_CONNECTED:
            if (millis() - stateTime >= connectedTime.count()) {
                Log.info("preparing to sleep");
                state = STATE_PREPARE_TO_SLEEP;
            }
            break;

        case STATE_PREPARE_TO_SLEEP:
            {
                SystemSleepConfiguration config;
                config.mode(SystemSleepMode::ULTRA_LOW_POWER)
                    .duration(sleepTime);
                SystemSleepResult sleepResult = System.sleep(config);

                Log.info("wakeupReason %d", (int)sleepResult.wakeupReason());
            }
            state = STATE_WAITING_TO_CONNECT;
            break;
    }

    // Call on every loop, or when blocking
    Watchdog.refresh(); 
}

The serial log shows resetReason 60 (RESET_REASON_WATCHDOG).

Serial connection closed.  Attempting to reconnect...
Serial monitor opened successfully:
0000002682 [app] INFO: resetReason 60

I haven’t looked into why it’s happening, but it does seem to wake up within 30 seconds.

30 seconds is roughly what I was seeing too, and it’s good to know that it’s not just me. Not sure how to proceed now though. Could it be an issue with the Boron’s underlying firmware?

I updated the docs because I completely misunderstood how WatchdogCap::SLEEP_RUNNING works. On all platforms, the default is for the watchdog to continue running in sleep mode, which matches the behavior you are seeing.

On the RTL872x platform (P2 and Photon 2), it's not possible to automatically pause the watchdog during sleep.

On the nRF52 (Boron, B Series SoM, Tracker, Argon) the default includes WatchdogCap::SLEEP_RUNNING so to turn it off, which is to say pause the watchdog on sleep, you need to set the capabilities to not include the flag.

Watchdog.init(WatchdogConfiguration()
  .capabilities(WatchdogCap::NOTIFY | WatchdogCap::DEBUG_RUNNING)
  .timeout(5min));
Watchdog.start();

For best compatibility across devices, we recommend that you set a watchdog timeout longer than your sleep period, plus an allowance for the time it takes to go to sleep, and wake up from sleep, which could add 30 seconds to a minute. This also provides extra safety in case the device does not wake up at the expected time, as the watchdog will reset the system in this case. This could happen if you have a bug in your logic for how long to sleep, for example.

1 Like

Just seeing this now, thanks for the update. Looking forward to testing this out and having an extra layer of protection for our deployments :saluting_face:

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.