Finally got ATTiny85 Watchdog Interrupt to work during Sleep Mode

I needed as near to maximum use out of a CR2032 / CR2035 button cell as I could for a project I’ve been working on, using a Digispark ( ATTiny85 ) system. The easiest route I could see was to use a Sleep configuration with a Watchdog interrupt to bring the system out of sleep, check a condition and then go back to sleep again, else perform an action then go back to sleep.

I’ve not had the pleasure of working with the Watchdog timer before, and so after days, ( literally ), combing through various so called Tutorials and examples, and studying the Datasheet for hours, I finally got the thing to work. In personal time, it took me several days of studying to arrive at a very basic sketch to illustrate both concepts that took me 20 mins to code. It’s like being back in the military, 98 per cent of the time planning and 2 per cent actually doing.

Anyway, to check the difference in battery consumption, I built 2 systems, both running on CR2032 button cells. One system is the bog standard Blink Without Delay sketch, adapted for a 10 second delay between 100 milisecond flashes. The other system does the same thing, but I use a Sleep and Watchdog configuration.

Starting both systems at exactly the same time, the Blink Without Delay shut down through battery discharge after just under 10 hours continuous use. However, the second system is still doing it’s thing after 11 DAYS of continuous use. Point proved I think, and I’ll be using a Sleep and Watchdog combination wherever possible in future projects. Thinking about hooking up the Watchdog to check an IR pulse in an anti burglar alarm, but i’m still in the planning and learning stage of that.

Many thanks to hneve for breaking it down so that even I could understand the concept
on , and to Nick Gammon for a practical example with further explanation at Gammon Forum : Electronics : Microprocessors : Power saving techniques for microprocessors , scroll down almost to the end of the link for the bit on the ATTiny85. Also to the ATTiny85 Datasheet for breaking down the Registers. The following sketch is adapted from all 3 sources. The only thing I’m looking at adding to it is power down the BrownOut detection to save even more power, together with switching the led to INPUT then back to OUTPUT between flashes.

NOTE: I’ve declared counterWD as a byte. If you need to count higher than 255, then change the declaration to word, unsigned long etc to suit your needs.

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

const byte ledPin = 0;                  // declare and initialise led on ATTiny digital pin zero
byte saveADCSRA;                      // variable to save the content of the ADC for later. if needed.
volatile byte counterWD = 0; // Count how many times WDog has fired. Used in the timing of the 
                                           // loop to increase the delay before the LED is illuminated. For example,
                                        // if WDog is set to 1 second TimeOut, and the counterWD loop to 10, the delay
                                        // between LED illuminations is increased to 1 x 10 = 10 seconds
void setup ()
  resetWatchDog ();                     // do this first in case WDog fires

  pinMode ( ledPin, OUTPUT );           // I could put to INPUT between sleep_enable() and interrupts()
                                        // to save more power, then to OUTPUT in the ISR after wdt_disable()
} // end of setup

void loop ()
  if ( counterWD == 9 )                 // if the WDog has fired 10 times......
  digitalWrite ( ledPin, HIGH );        // flash the led on for 100ms
  delay ( 100 );                        // hate using delay(), but hey! it's only a demo
  digitalWrite ( ledPin, LOW );
  counterWD = 0;                        // reset the counterWD for another 10 WDog firings
  } // end of if counterWD
  sleepNow ();                          // then set up and enter sleep mode
} // end of loop ()

void sleepNow ()
  set_sleep_mode ( SLEEP_MODE_PWR_DOWN ); // set sleep mode Power Down
  saveADCSRA = ADCSRA;                    // save the state of the ADC. We can either restore it or leave it turned off.
  ADCSRA = 0;                             // turn off the ADC
  power_all_disable ();                   // turn power off to ADC, TIMER 1 and 2, Serial Interface
  noInterrupts ();                        // turn off interrupts as a precaution
  resetWatchDog ();                       // reset the WatchDog before beddy bies
  sleep_enable ();                        // allows the system to be commanded to sleep
  interrupts ();                          // turn on interrupts
  sleep_cpu ();                           // send the system to sleep, night night!

  sleep_disable ();                       // after ISR fires, return to here and disable sleep
  power_all_enable ();                    // turn on power to ADC, TIMER1 and 2, Serial Interface
  // ADCSRA = saveADCSRA;                 // turn on and restore the ADC if needed. Commented out, not needed.
} // end of sleepNow ()

void resetWatchDog ()
  MCUSR = 0;
  WDTCR = bit ( WDCE ) | bit ( WDE ) | bit ( WDIF ); // allow changes, disable reset, clear existing interrupt
  WDTCR = bit ( WDIE ) | bit ( WDP2 )| bit ( WDP1 ); // set WDIE ( Interrupt only, no Reset ) and 1 second TimeOut
  wdt_reset ();                            // reset WDog to parameters
} // end of resetWatchDog ()

ISR ( WDT_vect )
  wdt_disable ();                           // until next time....
  counterWD ++;                             // increase the WDog firing counter. Used in the loop to time the flash
                                            // interval of the LED. If you only want the WDog to fire within the normal 
                                            // presets, say 2 seconds, then comment out this command and also the associated
                                            // commands in the if ( counterWD..... ) loop, except the 2 digitalWrites and the
                                            // delay () commands.
} // end of ISR

That’s great , just starting to play with sleeping myself. !

Awesome, I have been looking for something like this for a long time, great work. /SM6UAF Lars

I just found this post today, because I want to do similar sleep-with-timed-wakeup for ATtiny85. However, I think the new forum port has messed up your code (particularly the #include statements: maybe it did not accept angle-bracket delimiters for the file names?). Can you please re-edit to fix it up?

Yeah, the new system seems to eat anything between angle brackets.

Try these from an example, but there are other watch dog libraries, you’ll have to do this after you find each one unless the OP pipes up.

#include "watchdogHandler.h"
#include <avr/power.h>
#include <avr/wdt.h>
#include <avr/sleep.h>

Dunno why angle brackets work sometimes… here is some ← “stuff in angle brackets” got eaten.


volatile bool wdtFlag = false;

// this gets executed after watchdog wake up
ISR(WDT_vect) {
  wdtFlag = true;

void setup() {
  // set WDT interrupt mode, 4 seconds timeout
  WDTCR = 1<<WDCE | 1<<WDE;
  WDTCR = 0<<WDE | 1<<WDIE | 1<<WDP3 | 0<<WDP2 | 0<<WDP1 | 0<<WDP0;
  wdtFlag = false;

void loop() {
      wdtFlag = false;

      // do your thing here, it will execute once every 4 seconds after each wake-up, and immediately go back to sleep

    // go back to sleep
    MCUCR = 1<<SE | 1<<SM1 | 0<<SM0;

This is used for my projects on the Attiny85

You can get even more battery life by sleeping during the 100 ms delay too. SLEEP_MODE_IDLE only turns off the CPU and leaves all the other peripherals running, so you can

Also, leave off everything you aren’t using instead of turning them all on at wakeup.

If battery usage is a huge concern, it’s better to specially make the sleeping functions instead of relying on a general library so you can fine tune the sleep modes and the peripheral states to your program’s precise needs.