AB1805 Watchdog - Ignoring stopWDT() when in deepPowerDown()

I am using @rickkas7’s library AB1805_RK and have been testing new firmware with the AB1805.

So far so good except for one pesky problem. When the device is napping (STOP mode sleep), I follow these steps:


    ab1805.stopWDT();                                                 // If we are sleeping, we will miss petting the watchdog
    int wakeInSeconds = constrain(wakeBoundary - Time.now() % wakeBoundary, 1, wakeBoundary);
    config.mode(SystemSleepMode::STOP)
      .gpio(userSwitch,CHANGE)
      .gpio(intPin,RISING)
      .duration(wakeInSeconds * 1000);
    SystemSleepResult result = System.sleep(config);                    // Put the device to sleep

This works perfectly, the device sleeps uninterrupted by the watchdog.

However, when I want the device to “sleep” (Enable pin sleep) I use the following

    ab1805.stopWDT();                                                 // No watchdogs interrupting our slumber
    int wakeInSeconds = constrain(wakeBoundary - Time.now() % wakeBoundary, 1, wakeBoundary);
    delay(100);                                                       // This is a test to see if I needed to give the i2c command some time before turning off the device
    ab1805.deepPowerDown(wakeInSeconds);                              // The Real Time Clock will turn the Enable pin back on to wake the device         

I tried with and without the “delay” but the device is reset every 6 minutes by the watchdog timer.

What am I doing wrong here?

Thanks,

Chip

Interesting… I probably don’t have much to add as I’m still learning the ab1805 myself. I’m mostly commenting to follow the thread as I also am using the ab1805. Do you know if it’s truly pulling the EN pin low for the total duration of 6 minutes or is it keeping the EN pin high and waking up after 6 minutes using the interrupt pin? What I’m not understanding is the maximum duration of the watchdog is 124 seconds. I.e. just over 2 minutes. So if it truly was the watchdog not being stopped (and not being pet by the loop due to sleeping) then it should reset after 124 seconds, not 6 minutes.

Yes, I’d double check the AB1805 wakeup reason. The BMB register has a maximum value of 31 but the clock speed is set at 1/4 Hz (WRB=0b11), so the maximum WDT period is 124 seconds. Not sure where 6 minutes is coming from.

@rickkas7,

I found two issues:

  1. I was setting a repeating alarm set startup which, when removed, got the device to function normally throughout the day (when using STOP mode sleep).
  2. In the “Sleep” code snipped above, I was passing up to 3600 seconds as an argument in the deepPowerDown call. This value puts the RTC in a weird state where it passes an invalid timestamp (4294967295000 - some time in 2106) and does not set itself even when the device wakes and reconnects to the Particle cloud.

I am going to change my code to ensure I don’t send a deepPowerDown value of greater than 255 but perhaps there should be some bounds checking in the library? The library accepts an INT for the value despite the documentation saying that values can only be from 0 to 255 seconds.

Any advice on how to ensure that the clock gets set on reconnect? Perhaps something like:

waitUntil(ab1805.isRTCset);

I believe that, with your application not on watchdog timers, this chip will get a lot more use so any help you can give here will yield big rewards for the community.

Thanks,

Chip

3 Likes

OK, well my process of discovery continues with this new clock.

One thing that thew me - in addition to the bounds checking issue above is that in all the example code there is this line in setup:

    // Reset the AB1805 configuration to default values
    ab1805.resetConfig();

In the API documentation we get this:

and on the Github page, the following:
Screen Shot 2020-12-23 at 11.34.45 AM

But, what I did not infer - and in hindsight shame on me. Is that this line resets the RTC clock to Unix Time = 0. If you are in a connected state, this is not that big a deal as the RTC example code has a test for whether the RTC is set or not. However, in my use case, the device was not connecting to the Particle cloud overnight. So, the clock would get reset and the device would never realize it was time to wake in the morning as the clock was being reset over and over again.

Here is some example code that cycles through the three different sleep states. You can see the impact of this line by commenting out the ab1805.resetConfig() line.

/*
 * Project AB1805-Sleep-Test
 * Description: Test the sleep modes and the interaction with the clock
 * Author: Chip McClelland
 * Date: 12-3-20
 */

#include "AB1805_RK.h"

SYSTEM_THREAD(ENABLED);
SYSTEM_MODE(MANUAL);

SerialLogHandler logHandler;

AB1805 ab1805(Wire);
unsigned long lastTestTimeStamp;
unsigned long timeBetweenTests = 10000;
bool doSleep = false;
bool doWaitForTime = false;
int whichTest = 0;
time_t currentTime;

void setup() {    
    // Optional: Enable to make it easier to see debug USB serial messages at startup
    waitFor(Serial.isConnected, 15000);
    delay(1000);

    // The sample board has D8 connected to FOUT for wake interrupts
    ab1805.withFOUT(D8).setup();

    // Note whether the RTC is set before calling resetConfig() as this will make
    // isRTCSet return false.
    bool rtcSet = ab1805.isRTCSet();

    if (!rtcSet) {
        Log.info("RTC not set - will connect to the Particle cloud");
        Particle.connect();
        doWaitForTime = true;
        doSleep = false;
    }

    // Reset the AB1805 configuration to default values - comment this out to keep time between reboots.
    ab1805.resetConfig();                      // Note!  This line also resets the clock so you loose your time sync.

    // Enable watchdog
    ab1805.setWDT(AB1805::WATCHDOG_MAX_SECONDS);

    // The wakeReason is set during setup() so it's safe to call it after resetConfig.
    AB1805::WakeReason wakeReason = ab1805.getWakeReason();
    if (wakeReason == AB1805::WakeReason::DEEP_POWER_DOWN) {
        Log.info("woke from DEEP_POWER_DOWN");
    }
    else
    if (wakeReason == AB1805::WakeReason::ALARM) {
        // We were wakened by the alarm
        Log.info("woke by alarm (periodic interrupt)");
    }

    if (EEPROM.read(0) == 5) whichTest = 3;     // Coming back from HIBERNATE test


    Log.info("Startup complete - beginning tests");
    ab1805.getRtcAsTime(currentTime);
    Log.info(Time.timeStr(currentTime));
}


void loop() {
    // Be sure to call ab1805.loop() on every call to loop()
    ab1805.loop();

    if ((millis() - lastTestTimeStamp) > timeBetweenTests && !doWaitForTime) doSleep = true;

    if (doSleep) {

        SystemSleepConfiguration config;

        switch (whichTest) {    
            case 0:
                // Be sure to stop the watchdog timer before going to sleep!
                ab1805.stopWDT();

                // The delay is only so the Log.info will go out by serial. It's not
                // necessary for the functioning of sleep itself.
                Log.info("Going into Stop Mode Sleep");
                delay(100);

                ab1805.interruptCountdownTimer(10,false);

                config.mode(SystemSleepMode::STOP)
                    .gpio(D8, FALLING);
                System.sleep(config);
            break;

            case 1:
                // Be sure to stop the watchdog timer before going to sleep!
                ab1805.stopWDT();

                // The delay is only so the Log.info will go out by serial. It's not
                // necessary for the functioning of sleep itself.
                Log.info("Going into Ultra Low Power Sleep");
                delay(100);

                ab1805.interruptCountdownTimer(10,false);

                config.mode(SystemSleepMode::ULTRA_LOW_POWER)
                    .gpio(D8, FALLING);
                System.sleep(config);

            break;

            case 2:
                // Be sure to stop the watchdog timer before going to sleep!
                ab1805.stopWDT(); 

                EEPROM.write(0,5);                // Since we will reboot - putting in a flag

                // The delay is only so the Log.info will go out by serial. It's not
                // necessary for the functioning of sleep itself.
                Log.info("Going into Hibernation Sleep");
                delay(100);

                ab1805.interruptCountdownTimer(10,false);

                config.mode(SystemSleepMode::HIBERNATE)
                    .gpio(D8, FALLING);
                System.sleep(config);

            break;

            case 3:
                Log.info("Sleep Tests Complete");
                EEPROM.write(0,0);
            break;
        }

        doSleep = false;
        lastTestTimeStamp = millis();
        if (whichTest < 3) whichTest++;
    }

    if (doWaitForTime && ab1805.isRTCSet()) {                   // We got the time we can disconnect
        doWaitForTime = false;
        doSleep = true;
        lastTestTimeStamp = millis();
        Log.info("Clock is set - disconnecting");
        ab1805.getRtcAsTime(currentTime);
        Log.info(Time.timeStr(currentTime));
        Particle.disconnect();
    }
}

I hope this is helpful and I will keep sharing code as I get more comfortable with the AB1805.

Thanks, Chip

1 Like