1

I have been struggling to get my Adafruit 5v pro mini to loop in deep sleep mode several times, then awake to perform something then return to the deep sleep mode loop. While it is out of deep sleep mode I want the watchdog timer to be active to protect against system hangups. Although I can get in and out of deep sleep mode easy enough, and I can also use a loop that performs one deep sleep/awake cycle. I want to maximize the power savings by having a prolonged deep sleep period of several minutes. I have read many posts related to this subject, but have not yet found a response works for me. Attached is my code, which loops through the sleep cycle twice, but not if ISR(WDT_vect) on row 85 calls my hardwareReset() function. If my call to hardwareReset is used, my test loop on rows 25-30 times out as expected, but my waitSleep function no longer loops twice. Can anyone please offer guidance? Many thanks

// thanks to KeyChainino for basic code. I added ADC disable to further reduce power from 0.67mA to 0.54mA
//
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/wdt.h>
const int ledPin = 13;
const int resetPin = 9;

// the setup function runs once when you press reset or power the board
void setup() {
  pinMode(ledPin, OUTPUT);
  pinMode(resetPin, OUTPUT);
  digitalWrite(resetPin,HIGH);
  Serial.begin(9600);
  Serial.println("Resetting..");
  delay(100);
}

// the loop function runs over and over again forever
void loop() {
  digitalWrite(13, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1000);              // wait for a second
  digitalWrite(13, LOW);    // turn the LED off by making the voltage LOW
  for(int i=0; i<9; i++){; //set up loop to force timeout of WDT
  delay(1000);
  Serial.print("Loop ");
  Serial.println(i);
  delay(100);
} 
  waitSleep(2); //go sleep for x loops of WDT settings (8 sec max per loop)
}

void waitSleep(int sleep_cycles) {
  while (sleep_cycles) {
    goSleep();
    sleep_cycles--;
  }
}

void goSleep() {
  watchdogSetup(); //enable watchDog
  // disable ADC (analog to digital conversion)
  byte old_ADCSRA = ADCSRA;
  ADCSRA = 0;
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  sleep_mode();
  //disable watchdog after sleep
  wdt_disable();
  ADCSRA = old_ADCSRA;
}

void watchdogSetup() {

  //WDP3 - WDP2 - WPD1 - WDP0 - time
  // 0      0      0      0      16 ms
  // 0      0      0      1      32 ms
  // 0      0      1      0      64 ms
  // 0      0      1      1      0.125 s
  // 0      1      0      0      0.25 s
  // 0      1      0      1      0.5 s
  // 0      1      1      0      1.0 s
  // 0      1      1      1      2.0 s
  // 1      0      0      0      4.0 s
  // 1      0      0      1      8.0 s


  // Reset the watchdog reset flag
  bitClear(MCUSR, WDRF);
 
 
 //The following bit have to be set at the same time otherwise the system doesn't set the right value

 /* Start timed equence */
 //Watchdog Change Enable to clear WD (by setting the WDCE bit) and Enable WD (by setting the WDE bit)
 WDTCSR |= (1 << WDCE) | (1 << WDE);
 
 //Set new watchdog timeout value to 8 seconds (WDP3 and WDP0 to 1) and enable interrupts instead of reset (WDIE to 1)
 WDTCSR = (1 << WDIE) | (1 << WDP3) | (1 << WDP0) ;
}
void hardwareReset(){;
  digitalWrite(resetPin,LOW);
}

ISR(WDT_vect) {
  //hardwareReset();
}
5
  • I'm not exactly sure what the problem is. I feel like your not fully understanding how the wachdog timer works, in conjunction with the ISR, but I may be wrong. Your loop to force timeout is called before the watchdog is setup. So this only works the second time around. Maybe you do this for testing, but I always put this in my setup function (minus the WDIE bit, that is). Remember that later on you need to call wdt_reset(); if you expect the loop the take more than 8 seconds.
    – Gerben
    Jan 13 at 14:56
  • If I remember correctly the WDIE bit is cleared every time the watchdog times out. So if you code hangs or sleeps for 8 seconds the ISR is called, and the WDIE bit is cleared. Then if another 8 seconds pass by, the chip is reset. Try changing ` i<9` to ` i<17` and see if the chip now resets itself.
    – Gerben
    Jan 13 at 14:56
  • I only wish I could fully understand how the watchdog timer works. I can no longer remember why I moved watchdog out of the setup. I tried so many different things. I didn't want the interrupt to fire only once, then on the next timeout perform a reset. It was my way of waking up the device with the built-in reset button, while still allowing a reset if the software hangs. Maybe I should try controlling the WDIE bit differently. I changed "i<9" to "i<17". There was no change. Thanks for the input Gerben.
    – J.B.
    Jan 14 at 16:59
  • 1
    I didn't want the interrupt to fire only once; you can. You just have to set the WDIE bit after each time the interrupt is called. In you case that would be, setting the WDIE bit each time the MCU goes to sleep. Maybe you could tell what it is you are trying to achieve in the end, instead of what isn't working as you expect in your piece of test-code. As I'm still not fully getting what it is you are asking/expecting.
    – Gerben
    Jan 14 at 17:15
  • Resetting the WDIE was the answer I was looking for. Simple. Thanks. This is to control a peristaltic bilge pump on a sailboat wintering in the water. The pump keeps the bilge bone dry, but operates continuously. Ice is a problem, so the Arduino senses temperature and will shut down the pump via a relay when it's near freezing. To conserve battery the arduino will check temp every 15 mins. I can't risk software hanging hence watchdog reset. External interrupt needed to fire up OLED if I want to read high and low saved readings. Next I will test for water flow via barometric pressure sensor.
    – J.B.
    Jan 16 at 3:25
1

I don't know if this is the best solution, but I found a workaround. In the ISR(WDT_vect) function I added a while statement where I added a boolean variable set to true when running the basic program, and false when I enter looping deep sleep mode. I'd appreciate if someone out there can provide a better solution, but for now this one works for me. Below is the revised code in case anyone's interested.

// thanks to KeyChainino for basic code. I added ADC disable to further reduce power from 0.67mA to 0.54mA
//revised by John Bedford 01/12/2020
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/wdt.h>
const int ledPin = 13;
const int resetPin = 9;
volatile boolean wdtInterrupt;    //watchdog timer interrupt flag

// the setup function runs once when you press reset or power the board
void setup() {
  pinMode(ledPin, OUTPUT);
  pinMode(resetPin, OUTPUT);
  digitalWrite(resetPin,HIGH);
  Serial.begin(9600);
  Serial.println("Resetting..");
  delay(100);
  watchdogSetup();
}

// the loop function runs over and over again forever
void loop() {
  wdtInterrupt= true;
  digitalWrite(13, HIGH);   // turn the LED on (HIGH is the voltage level)
  delay(1000);              // wait for a second
  digitalWrite(13, LOW);    // turn the LED off by making the voltage LOW
  for(int i=0; i<9; i++){; //set up loop to force timeout of WDT
    delay(1500);
    Serial.print("Loop ");
    Serial.println(i);
    delay(100);
  }
  wdt_reset();
  
 
  waitSleep(2); //go sleep for x loops of WDT settings (8 sec max per loop)
}

void waitSleep(int sleep_cycles) {
  wdtInterrupt=false;
  while (sleep_cycles) {
    goSleep();
    sleep_cycles--;
  }
}

void goSleep() {
  watchdogSetup(); //enable watchDog
  // disable ADC (analog to digital conversion)
  byte old_ADCSRA = ADCSRA;
  ADCSRA = 0;
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  sleep_mode();
  //disable watchdog after sleep
  wdt_disable();
  ADCSRA = old_ADCSRA;
}

void watchdogSetup() {

  //WDP3 - WDP2 - WPD1 - WDP0 - time
  // 0      0      0      0      16 ms
  // 0      0      0      1      32 ms
  // 0      0      1      0      64 ms
  // 0      0      1      1      0.125 s
  // 0      1      0      0      0.25 s
  // 0      1      0      1      0.5 s
  // 0      1      1      0      1.0 s
  // 0      1      1      1      2.0 s
  // 1      0      0      0      4.0 s
  // 1      0      0      1      8.0 s


  // Reset the watchdog reset flag
  bitClear(MCUSR, WDRF);
 
 
 //The following bit have to be set at the same time otherwise the system doesn't set the right value

 /* Start timed equence */
 //Watchdog Change Enable to clear WD (by setting the WDCE bit) and Enable WD (by setting the WDE bit)
 WDTCSR |= (1 << WDCE) | (1 << WDE);
 
 //Set new watchdog timeout value to 8 seconds (WDP3 and WDP0 to 1) and enable interrupts instead of reset (WDIE to 1)
 WDTCSR = (1 << WDIE) | (1 << WDP3) | (1 << WDP0) ;
}
void hardwareReset(){;
  digitalWrite(resetPin,LOW);
}

ISR(WDT_vect) {
  while (wdtInterrupt){
   hardwareReset();
  }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.