Combining sleep/wake cycle and watchdog timer

I am currently trying to develop code for a Mayfly Datalogger. I’d like the unit to (1) Wake up every X minutes, check the time and record data if the time is a particular multiple; and (2) have a ‘watchdog’ that where the unit will reset if no reading has been taken in Y minutes, for example.

I’ve gotten both of these pieces of code to work individually, but they don’t seem to work when I combine them. If I comment out the Watchdogsetup() (line 40), the timer works fine. But I can’t get them to work together. I suspect it has something to do with conflicted settings.

This is Frankenstein code, so I don’t know 100% of what is going on for each. Any advice on getting this to work. A second issue I’m having is that the code will only awake every 1 minute, not every X minutes as defined by RTC_INT_PERIOD (line 12), but this is less important.

#include <avr/sleep.h> //
#include <avr/wdt.h>
#include <RTCTimer.h>
#include <Sodaq_DS3231.h>
#include <Sodaq_PcInt.h>

#define DEBUG // comment out after debugging is complete to disable DEBUG SERIAL

//RTC Interrupt pin
#define RTC_INT_PIN A7
#define RTC_INT_UNITS EveryMinute // wake up every X seconds
#define RTC_INT_PERIOD 2 // wake up every 20 seconds

//Initalize RTC
RTCTimer timer;                   // Real-time clock

int frequency = 2; // Take measurements every 2 minutes, upon waking
unsigned long TIMEOUT_PERIOD = 90000;           // in milliseconds, must be <49 days

int currentminute;
long currentepochtime = 0;
unsigned long resetTime = 0;

#define doggieTickle() resetTime = millis();  // This macro will reset the timer

// Setup debug-specific Serial printing, these functions only compiles if DEBUG is defined, above
#ifdef DEBUG
#define DEBUG_PRINTLN(x)  Serial.println (x)
#define DEBUG_PRINT(x)  Serial.print (x)
#else
#define DEBUG_PRINTLN(x)
#define DEBUG_PRINT(x)
#endif

void setup()
{
  //Initialize real time clock and I2C connection
  Serial.begin(9600);
  rtc.begin();        // real time clock
  watchdogSetup();
  setupTimer();        //Setup timer events
  setupSleep();        //Setup sleep mode
}

void loop()
{
  //Update the timer
  timer.update();

  DEBUG_PRINTLN("Waking up, checking time.");
  String wakeup_time = getDateTime();
  DEBUG_PRINTLN(wakeup_time);

  if (currentminute % frequency == 0)  // wake up at intervals of frequency minutes
  {
    //Script to capture and record data will happen here.
     DEBUG_PRINTLN("* Data recording will occur here: ");
    
    //If readings are good, then reset watchdog timer
    //doggieTickle();  // keeping commented away so force reset.
  }

  //Sleep
  DEBUG_PRINTLN("Going to sleep minute.");
  systemSleep();
}

void showTime(uint32_t ts)
{
  //Retrieve and display the current date/time
  String dateTime = getDateTime();
}

//Timer wakes up every period to check time
void setupTimer()
{
  //Schedule the wakeup every minute
  timer.every(RTC_INT_PERIOD, showTime);
  //Instruct the RTCTimer how to get the current time reading
  timer.setNowCallback(getNow);
}

void wakeISR()
{
  //Leave this blank
}

void setupSleep()
{
  pinMode(RTC_INT_PIN, INPUT_PULLUP);
  PcInt::attachInterrupt(A7, wakeISR);
  //Setup the RTC in interrupt mode
  rtc.enableInterrupts(RTC_INT_UNITS);
  //Set the sleep mode
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
}

void systemSleep()
{
  //Wait until the serial ports have finished transmitting
  Serial.flush();
  //The next timed interrupt will not be sent until this is cleared
  rtc.clearINTStatus();

  //Disable ADC
  ADCSRA &= ~_BV(ADEN);

  //Sleep time
  noInterrupts();
  sleep_enable();
  interrupts();
  sleep_cpu();
  sleep_disable();

  //Enbale ADC
  ADCSRA |= _BV(ADEN);
}

String getDateTime()
{
  String dateTimeStr;
  //Create a DateTime object from the current time
  DateTime dt(rtc.makeDateTime(rtc.now().getEpoch()));
  currentepochtime = (dt.get());    //Unix time in seconds
  currentminute = (dt.minute());
  //Convert it to a String
  dt.addToString(dateTimeStr);
  return dateTimeStr;
}

uint32_t getNow()
{
  currentepochtime = rtc.now().getEpoch();
  return currentepochtime;
}

void(* resetFunc) (void) = 0; //declare reset function @ address 0

void watchdogSetup()
{
  cli();  // disable all interrupts
  wdt_reset(); // reset the WDT timer
  MCUSR &= ~(1 << WDRF); // because the data sheet said to
  /*
    WDTCSR configuration:
    WDIE = 1 :Interrupt Enable
    WDE = 1  :Reset Enable - I won't be using this on the 2560
    WDP3 = 0 :For 1000ms Time-out
    WDP2 = 1 :bit pattern is
    WDP1 = 1 :0110  change this for a different
    WDP0 = 0 :timeout period.
  */
  // Enter Watchdog Configuration mode:
  WDTCSR = (1 << WDCE) | (1 << WDE);
  // Set Watchdog settings: interrupte enable, 0110 for timer
  WDTCSR = (1 << WDIE) | (0 << WDP3) | (1 << WDP2) | (1 << WDP1) | (0 << WDP0);
  sei();
}

ISR(WDT_vect) // Watchdog timer interrupt.
{
  if (millis() - resetTime > TIMEOUT_PERIOD) {
    DEBUG_PRINTLN("REBOOTING");
    resetFunc();     // This will call location zero and cause a reboot.
  }
}

Sorry but Frankenstein has me worried - does this mean you’ve nailed together two sketches and hope they will work ?
I’ve not looked at your code , but have played in this area

  • you need to study watchdog , how to set it up , the registers and settings involved.
    You have multiple interrupts and need to know how they will be serviced and whether or not to disable one or other at certain times.
    There may also be problems using timers , sleeping and interrupts - may well be some conflicts or timers being reset .

This might be better if you didn’t use a watchdog to time out if a reading is not available , use some form of timer and check that , it’s a bit crude in my opinion to so use the WDT in this fashion.

Need to have a look at your RTCTimer library.

Thanks DK: RTCTimer library here: