Watchdog Timer Interrupt and External Interrupt

That code does not define an ISR for INT1_vect.

This also needs changing:

    EICRA = _BV(ISC01);            //configure INT0 to trigger on falling edge
    EIFR = _BV(INTF0);             //ensure interrupt flag cleared
    EIMSK = _BV(INT0);             //enable INT0

Why not use attachInterrupt? That's simpler.

Nick, I'm punching above my weight here!!
I've just had a look at attachInterrupt and that does not seem straightforward either...
From your tone, I suspect that this would not be easy to implement, so maybe I need to take a step back and try and find a solution using just one pin with interrupt.

Paul

Not at all, it's simpler using that. Here:

//Using the watchdog timer, sleep for the given number of intervals,
//then print a message. Prints a dot at each WDT wakeup.
//A button is also connected to provide an external interrupt, print
//a message when the button is pressed.
//Finally, blink an LED after each wake-up regardless of source.
//Using an ATmega328P at 16MHz and 5V, draws ~6.3µA while sleeping, which
//is consistent with only the WDT running.
//
//Jack Christensen 19Nov2013
//CC BY-SA, see http://creativecommons.org/licenses/by-sa/3.0/

#include <avr/wdt.h>
#include <avr/sleep.h>
#include <util/atomic.h>
#include <Streaming.h>            //http://arduiniana.org/libraries/streaming/

const int BTN_PIN = 2;            //wire a button from pin 2 to ground
const int BTN_PIN2 = 3;            //wire a button from pin 3 to ground
const int LED_PIN = 13;           //wire an LED from pin 13 to ground through a proper current-limiting resistor
const int WDT_INTERVALS = 50;     //number of WDT timeouts before printing message
const unsigned long LED_ON_TIME = 200;  //turn on LED for this many ms after each wake-up
const long BAUD_RATE = 115200;

volatile boolean extInterrupt;    //external interrupt flag (button)
volatile boolean wdtInterrupt;    //watchdog timer interrupt flag

void wakeOnInterrupt ()
  {
  extInterrupt = true;
  // don't need the external interrupts any more
  detachInterrupt (0);  
  detachInterrupt (1);  
  }
  
void setup(void)
{
    pinMode(BTN_PIN, INPUT_PULLUP);
    pinMode(BTN_PIN2, INPUT_PULLUP);
    pinMode(LED_PIN, OUTPUT);
    Serial.begin(BAUD_RATE);

    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
        wdt_reset();
        MCUSR &= ~bit(WDRF);                            //clear WDRF
        WDTCSR |= bit(WDCE) | bit(WDE);                 //enable WDTCSR change
        WDTCSR =  bit(WDIE) | bit(WDP3) | bit(WDP0);    //~8 sec
    }
    Serial << endl << F("Setup complete");
}

void loop(void)
{
    static int wdtCount;
    static int btnCount;

    if (wdtCount == 0 || extInterrupt) Serial << endl << F("Sleep");
    Serial.flush();
    Serial.end();
    gotoSleep();

    digitalWrite(LED_PIN, HIGH);    //blink the LED on each wakeup
    delay(LED_ON_TIME);
    digitalWrite(LED_PIN, LOW);

    Serial.begin(BAUD_RATE);
    if (wdtInterrupt) {
        Serial << '.';
        if (++wdtCount >= WDT_INTERVALS) {
            Serial << endl << F("WDT: ") << wdtCount;
            wdtCount = 0;
        }
    }
    if (extInterrupt) {
        Serial << endl << F("Button: ") << ++btnCount;
    }
}

void gotoSleep(void)
{
    byte adcsra = ADCSRA;          //save the ADC Control and Status Register A
    ADCSRA = 0;                    //disable the ADC
    noInterrupts ();     // timed sequences follow
    EIFR = bit (INTF0);  // clear flag for interrupt 0
    EIFR = bit (INTF1);  // clear flag for interrupt 1
    attachInterrupt (0, wakeOnInterrupt, FALLING);
    attachInterrupt (1, wakeOnInterrupt, FALLING);
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);
    wdtInterrupt = false;
    extInterrupt = false;
    sleep_enable();
    byte mcucr1 = MCUCR | bit(BODS) | bit(BODSE); //turn off the brown-out detector while sleeping
    byte mcucr2 = mcucr1 & ~bit(BODSE);
    MCUCR = mcucr1; //timed sequence
    MCUCR = mcucr2; //BODS stays active for 3 cycles, sleep instruction must be executed while it's active
    interrupts ();      // need interrupts now
    sleep_cpu();                   //go to sleep
    sleep_disable();               //wake up here
    ADCSRA = adcsra;               //restore ADCSRA
}

//handles the Watchdog Time-out Interrupt
ISR(WDT_vect)
{
    wdtInterrupt = true;
}

The wakeOnInterrupt function handles waking on both interrupts.

Compiled and tested OK. Some style changes made (sorry, Jack!).

That's great Nick, thank you very much!
This project has proved a steep learning curve, so I do appreciate your support.

Paul

No worries, to each his own! Thanks for getting my back there.

I suppose attachInterrupt() is simpler, but not much. Guess I just like diddling with the registers -- that way if anything goes South, I only have myself to blame. :smiley:

How long, precisely, is each sleep cycle?
My best guess is about 8.7 seconds, but is there a precise figure which could be used to determine reasonably accurate, longer time periods by repeating the cycle, such as a re-occurring task exactly every 8hrs.

I suggest you add a real-time clock chip (for around $1) if you want more accurate time-keeping.

pauldreed:
How long, precisely, is each sleep cycle?
My best guess is about 8.7 seconds, but is there a precise figure which could be used to determine reasonably accurate, longer time periods by repeating the cycle, such as a re-occurring task exactly every 8hrs.

Some comments about the accuracy of the WDT:
http://forum.arduino.cc/index.php?topic=201460.msg1484665#msg1484665
http://forum.arduino.cc/index.php?topic=201460.msg1484794#msg1484794

Nick & Jack -

NG reply #38 & JC reply #24

I read this thread with great interest because i have been trying to figure out how to do exactly this for weeks. I have posted threads (questions) on this subject in this forum, and gotten no responses.

And in looking at your codes, I would not have figured it out in this lifetime. But when i run either code, I get the same error message "in function 'void setup()' end1 was not declared in this scope".

I am using UNO 1.0.1 I have all of the includes.h

Would either of you take a minute and help me to get your code running . it runs for you, but not for me.

thanks
pat

I

pratto:
But when i run either code, I get the same error message "in function 'void setup()' end1 was not declared in this scope".

"endl" is defined in the library:

#include <Streaming.h>            //http://arduiniana.org/libraries/streaming/

Perhaps it's not installed in the proper place.

Jack -

that did it. it's amazing how much more smoothly things run when the library is where the code can find it.

thanks
pat

pratto:
Jack -

that did it. it's amazing how much more smoothly things run when the library is where the code can find it.

thanks
pat

Excellent! Of course that library is not absolutely necessary, the "Serial <<" type statements can be replaced with regular Serial.print() and Serial.println() but I like the syntax and it reduces the number of lines of code.

Jack -

at the risk of trying your patience, i'll ask one more dumb question.

when i run your code, the serial output is just as your comment says it will be, and so is the led blink, but when i touch a jumper from pin 2 to ground, nothing happens (or at least i don't see a blink). this is why i say it's a dumb question. i am using an UNO. isn't pin 2 two over from rx 0, and marked 2 (and in the digital section) ?

you're probably thinking, what next ? help with the on/off button ?
by the way, i'm from michigan too.

pat

Please ignore my last post. it works fine. out of laziness i had left another wire in pin 2. after i sent my reply to you i thought, maybe i better try it with that wire out, just to make sure. and it blinked.

thanks again

pratto:
Please ignore my last post. it works fine. out of laziness i had left another wire in pin 2. after i sent my reply to you i thought, maybe i better try it with that wire out, just to make sure. and it blinked.

thanks again

No worries. What part of Michigan?

Nick, Jack -Thanks so much for your thorough explanations on the watchdog timers. They've been a huge help for my project!
Scott

Nick & Jack,
Many thanks for taking the time of contributing to this thread and putting the code together.
You guys have certainly helped me a lot.
George

Hi! I've a question, can i use Watchdog Timer Interrupt and External Interrupt with an Attiny85?

I turn on a LED every 24hours or when button is pressed.

Thank you so much for any reply and sorry for my bad english.

Bye!

JChristensen:
Sorry about that, I'm using a later toolchain than the one that comes with the IDE and I didn't realize that function wasn't defined. This code is equivalent:

//Using the watchdog timer, sleep for the given number of intervals,

//then print a message. Prints a dot at each WDT wakeup.
//A button is also connected to provide an external interrupt, print
//a message when the button is pressed.
//Finally, blink an LED after each wake-up regardless of source.
//Using an ATmega328P at 16MHz and 5V, draws ~6.3µA while sleeping, which
//is consistent with only the WDT running.
//
//Jack Christensen 19Nov2013
//CC BY-SA, see Creative Commons — Attribution-ShareAlike 3.0 Unported — CC BY-SA 3.0

#include <avr/wdt.h>
#include <avr/sleep.h>
#include <util/atomic.h>
#include <Streaming.h>            //Streaming | Arduiniana

const int BTN_PIN = 2;            //wire a button from pin 2 to ground
const int LED_PIN = 13;           //wire an LED from pin 13 to ground through a proper current-limiting resistor
const int WDT_INTERVALS = 50;     //number of WDT timeouts before printing message
const unsigned long LED_ON_TIME = 200;  //turn on LED for this many ms after each wake-up
const long BAUD_RATE = 9600;

volatile boolean extInterrupt;    //external interrupt flag (button)
volatile boolean wdtInterrupt;    //watchdog timer interrupt flag

void setup(void)
{
   pinMode(BTN_PIN, INPUT_PULLUP);
   pinMode(LED_PIN, OUTPUT);
   Serial.begin(BAUD_RATE);

ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
       wdt_reset();
       MCUSR &= ~_BV(WDRF);                            //clear WDRF
       WDTCSR |= _BV(WDCE) | _BV(WDE);                 //enable WDTCSR change
       WDTCSR =  _BV(WDIE) | _BV(WDP3) | _BV(WDP0);    //~8 sec
   }
   Serial << endl << F("Setup complete");
}

void loop(void)
{
   static int wdtCount;
   static int btnCount;

if (wdtCount == 0 || extInterrupt) Serial << endl << F("Sleep");
   Serial.flush();
   Serial.end();
   gotoSleep();

digitalWrite(LED_PIN, HIGH);    //blink the LED on each wakeup
   delay(LED_ON_TIME);
   digitalWrite(LED_PIN, LOW);

Serial.begin(BAUD_RATE);
   if (wdtInterrupt) {
       Serial << '.';
       if (++wdtCount >= WDT_INTERVALS) {
           Serial << endl << F("WDT: ") << wdtCount;
           wdtCount = 0;
       }
   }
   if (extInterrupt) {
       Serial << endl << F("Button: ") << ++btnCount;
   }
}

void gotoSleep(void)
{
   byte adcsra = ADCSRA;          //save the ADC Control and Status Register A
   ADCSRA = 0;                    //disable the ADC
   EICRA = _BV(ISC01);            //configure INT0 to trigger on falling edge
   EIFR = _BV(INTF0);             //ensure interrupt flag cleared
   EIMSK = _BV(INT0);             //enable INT0
   set_sleep_mode(SLEEP_MODE_PWR_DOWN);
   ATOMIC_BLOCK(ATOMIC_FORCEON) {
       wdtInterrupt = false;
       extInterrupt = false;
       sleep_enable();
       byte mcucr1 = MCUCR | _BV(BODS) | _BV(BODSE); //turn off the brown-out detector while sleeping
       byte mcucr2 = mcucr1 & ~_BV(BODSE);
       MCUCR = mcucr1; //timed sequence
       MCUCR = mcucr2; //BODS stays active for 3 cycles, sleep instruction must be executed while it's active
   }
   sleep_cpu();                   //go to sleep
   sleep_disable();               //wake up here
   ADCSRA = adcsra;               //restore ADCSRA
}

//external interrupt 0 wakes the MCU
ISR(INT0_vect)
{
   EIMSK = 0;                     //disable external interrupts (only need one to wake up)
   extInterrupt = true;
}

//handles the Watchdog Time-out Interrupt
ISR(WDT_vect)
{
   wdtInterrupt = true;
}

Would it be possible to wait to execute the goToSleep() function so as to be able to execute a number N of times the loop (), in which there could be interaction with the user?