Watchdog timer and ISR

Hello everybody,

First of all, a few words about my project to give you the context : I want to make a “fridge alarm”, that would go off when the temperature is above a certain threshold. I am using an ATtiny85, the temperature is measured thanks to a thermistor and the alarm is a buzzer. In order to reduce power consumption, I decided to use the watchdog timer and interrupts : the ATtiny is powered down for 8 seconds, then wakes up to take a measure (thanks to the watchdog timer), and goes back to sleep.

It is the first time I am using an ATtiny85, I program it using the Arduino IDE. It is also the first time I am using the watchdog timer, and interrupts (with Arduino at least - I’ve already done that programming in C).

Now my question :

I got inspired from a project found on the Internet. In this project, an ISR is used. It sets a flag to 1, and the value of this flag is used in the main function, to start a treatment if the flag is set to 1 (blinking a LED). But in my opinion, the flag isn’t needed : the main() loop is executed, and this execution forces the ATtiny to sleep. When it wakes up after 8 seconds, I believe it resumes where it stoped : no need for extra variables, at least it’s my (possibly wrong) opinion. I stay within the main() loop and at each iteration I sleep.

So I removed the unused flag. I ended up with an empty ISR. So I tried to remove the ISR routine as well : It seems to work, at least I get no error and the LED keeps blinking.
So my question is : Do I need to have an ISR defined in my program ? Can I use interrupts without defining an ISR ? Is there a kind of “if no ISR, resume program execution” behavior ?

Hereafter is my code :

#include <avr/sleep.h>  // Sleep
#include <avr/wdt.h>    // Watchdog timer

#ifndef cbi
  #define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
  #define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

int pinLed = 0;

void setup(){
  pinMode(pinLed,OUTPUT);
  setup_watchdog(9); // approximately 8 seconds sleep
}

void loop(){
  discardSleepPowerSavings();   // Peripherals go back to normal power consumption
  digitalWrite(pinLed,HIGH);    // LED is shining
  delay(1000);                  // Delay
  digitalWrite(pinLed,LOW);     // LED is switched off
  initiateSleepPowerSavings();  // Reduces power consumption
  system_sleep();               // System goes to sleep   
}
void initiateSleepPowerSavings(){
  cbi(ADCSRA,ADEN); // Swith off ADC
}

void discardSleepPowerSavings(){
  sbi(ADCSRA,ADEN);  // Switch on ADC
}


// set system into the sleep state 
// system wakes up when wtchdog is timed out
void system_sleep() {
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Sleep mode is set (power down)
  sleep_mode();                        // System sleeps
}

// 0 = 16 ms; 1 = 32 ms; 2 = 64 ms; 3 = 128 ms; 4 = 250 ms; 5 = 500 ms; 6 = 1 s; 7 = 2 s; 8 = 4 s; 9 = 8 s
void setup_watchdog(int ii) {

  byte bb;
  int ww;
  if (ii > 9 ) ii = 9;
  bb = ii & 7;
  if (ii > 7) bb |= (1<<5);
  bb |= (1<<WDCE);
  ww = bb;

  MCUSR &= ~(1<<WDRF);
  // start timed sequence
  WDTCR |= (1<<WDCE) | (1<<WDE);
  // set new watchdog timeout value
  WDTCR = bb;
  WDTCR |= _BV(WDIE);
}
/* 
// Watchdog Interrupt Service / is executed when watchdog timed out
ISR(WDT_vect) {
  // f_wdt = 1;    // Set global flag to 1
}
*/

And here is the original code :

/*
 * Watchdog Sleep Example 
 * Demonstrate the Watchdog and Sleep Functions
 * LED on digital pin 0
 * 
 * KHM 2008 / Lab3/  Martin Nawrath nawrath@khm.de
 * Kunsthochschule fuer Medien Koeln
 * Academy of Media Arts Cologne
 *
 * Modified on 5 Feb 2011 by InsideGadgets (www.insidegadgets.com)
 * to suit the ATtiny85 and removed the cbi( MCUCR,SE ) section 
 * in setup() to match the Atmel datasheet recommendations
 */

#include <avr/sleep.h>
#include <avr/wdt.h>

#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

int pinLed = 0;
volatile boolean f_wdt = 1;

void setup(){
  pinMode(pinLed,OUTPUT);
  setup_watchdog(8); // approximately 4 seconds sleep
}

void loop(){
  if (f_wdt==1) {  // wait for timed out watchdog / flag is set when a watchdog timeout occurs
    f_wdt=0;       // reset flag

    digitalWrite(pinLed,HIGH);  // let led blink
    delay(1000);
    digitalWrite(pinLed,LOW);

    pinMode(pinLed,INPUT); // set all used port to intput to save power
    system_sleep();
    pinMode(pinLed,OUTPUT); // set all ports into state before sleep
  }
}

// set system into the sleep state 
// system wakes up when wtchdog is timed out
void system_sleep() {
  cbi(ADCSRA,ADEN);                    // switch Analog to Digitalconverter OFF

  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here
  sleep_enable();

  sleep_mode();                        // System sleeps here

  sleep_disable();                     // System continues execution here when watchdog timed out 
  sbi(ADCSRA,ADEN);                    // switch Analog to Digitalconverter ON
}

// 0=16ms, 1=32ms,2=64ms,3=128ms,4=250ms,5=500ms
// 6=1 sec,7=2 sec, 8=4 sec, 9= 8sec
void setup_watchdog(int ii) {

  byte bb;
  int ww;
  if (ii > 9 ) ii=9;
  bb=ii & 7;
  if (ii > 7) bb|= (1<<5);
  bb|= (1<<WDCE);
  ww=bb;

  MCUSR &= ~(1<<WDRF);
  // start timed sequence
  WDTCR |= (1<<WDCE) | (1<<WDE);
  // set new watchdog timeout value
  WDTCR = bb;
  WDTCR |= _BV(WDIE);
}
  
// Watchdog Interrupt Service / is executed when watchdog timed out
ISR(WDT_vect) {
  f_wdt=1;  // set global flag
}

Thank you !

greg765: then wakes up to take a measure (thanks to the watchdog timer), and goes back to sleep.

Just a quick tip: although it might work, you are not using the watchdog timer correctly.

Watchdog timers are used to determine if the code hangs for a predescribed amount of time (in error) and then resets the controller. Its like having Windows freeze for an unknown reason and then restarts itself to clear the error.

This concept is very useful in embedded systems that should be highly reliable and/or difficult to service, and not to "wake up" the MCU.

Power_Broker: Just a quick tip: although it might work, you are not using the watchdog timer correctly.

Watchdog timers are used to determine if the code hangs for a predescribed amount of time (in error) and then resets the controller. Its like having Windows freeze for an unknown reason and then restarts itself to clear the error.

This concept is very useful in embedded systems that should be highly reliable and/or difficult to service, and not to "wake up" the MCU.

On page 42, ATtiny85 datasheet states :

"The Wathdog Timer can also be configured to generate an interrupt instead of a reset. This can be very helpful when using the Watchdog to wake-up from Power-down."

So I presume even if the watchdog timer can be used the way you describe, it can also be used to generate interrupts to wake up the MCU.

This is indeed a very common use of the WDT in this context.

I wonder what the compiler generates when you don't define the ISR? I would be uncomfortable with this, personally, until I'd examined the vector table in the hex file and satisfied myself that it wasn't jumping off somewhere crazy when the ISR (tried to) run; my default behavior would be to use an empty ISR.

greg765: On page 42, ATtiny85 datasheet states :

"The Wathdog Timer can also be configured to generate an interrupt instead of a reset. This can be very helpful when using the Watchdog to wake-up from Power-down."

So I presume even if the watchdog timer can be used the way you describe, it can also be used to generate interrupts to wake up the MCU.

Your interpretation is correct. Waking the micro is a perfectly valid use of the WDT, and in fact a very common one.

DrAzzy: I wonder what the compiler generates when you don't define the ISR? I would be uncomfortable with this, personally, until I'd examined the vector table in the hex file and satisfied myself that it wasn't jumping off somewhere crazy when the ISR (tried to) run; my default behavior would be to use an empty ISR.

Your interpretation is correct. Waking the micro is a perfectly valid use of the WDT, and in fact a very common one.

Well, it's true that an empty ISR won't take too much space in my code, so I will keep it !

Thank you for the advice ! It's not the first time I am using interrupts but it is the first time I encounter one where nothing special has to be done ! So I'll keep it, but empty ! :)