Go Down

Topic: "Extend" Watchdog Timer Beyond 8 Seconds? (Read 32111 times) previous topic - next topic

bloogoo

Jun 24, 2013, 09:38 pm Last Edit: Jun 25, 2013, 02:46 am by bloogoo Reason: 1
I'm trying to create a system that will be in SLEEP_MODE_PWR_DOWN state for 4 minutes at a time, and then wake up and take measurements. I understand that the watchdog timer works well to wake up a system. Is there a way to have the timer not wake up the system for 240 seconds (4 minutes) at a time? I've tried WHILE statements but that didn't work (i.e. WHILE x < 30 stay asleep [as 30 * 8s = 240 seconds]). Is there a better option that I should be considering?

Here's my (adapted) code so far:
Code: [Select]
/*
* Sketch for testing sleep mode with wake up on WDT.
* Donal Morrissey - 2011.
*
*/
#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/wdt.h>

#define Set_prescaler(x)   (CLKPR = (1 << CLKPCE), CLKPR = x)
#define LED_PIN (13)

volatile int f_wdt=1;
int test_mode_clock = 4;
int test_mode_pin = 3;


/***************************************************
*  Name:        ISR(WDT_vect)
*
*  Returns:     Nothing.
*
*  Parameters:  None.
*
*  Description: Watchdog Interrupt Service. This
*               is executed when watchdog timed out.
*
***************************************************/
ISR(WDT_vect)
{
  if(f_wdt == 0)
  {
    f_wdt=1;
  }
}


/***************************************************
*  Name:        enterSleep
*
*  Returns:     Nothing.
*
*  Parameters:  None.
*
*  Description: Enters the Arduino into sleep mode.
*
***************************************************/
void enterSleep(void)
{
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);   /* EDIT: could also use SLEEP_MODE_PWR_DOWN for lowest power consumption. */
  sleep_enable();

  /* Now enter sleep mode. */
  sleep_mode();

  /* The program will continue from here after the WDT timeout*/
  sleep_disable(); /* First thing to do is disable sleep. */

  /* Re-enable the peripherals. */
  power_all_enable();
}


/***************************************************
*  Name:        setup
*
*  Returns:     Nothing.
*
*  Parameters:  None.
*
*  Description: Setup for the serial comms and the
*                Watch dog timeout.
*
***************************************************/
void setup()
{
  Set_prescaler(test_mode_clock);

  int i;
  for(i = 0; i <= 19; i++)
  {
    switch(test_mode_pin)
    {
    case 0:
      pinMode(i, INPUT);
      break;
    case 1:
      pinMode(i, INPUT);
      digitalWrite(i, HIGH);
      break;
    case 2:
      pinMode(i, OUTPUT);
      digitalWrite(i, LOW);
      break;
    case 3:
      pinMode(i, OUTPUT);
      digitalWrite(i, HIGH);
      break;
    }
  }

  pinMode(LED_PIN, OUTPUT);

  /*** Setup the WDT ***/

  /* Clear the reset flag. */
  MCUSR &= ~(1 << WDRF);

  /* In order to change WDE or the prescaler, we need to
   * set WDCE (This will allow updates for 4 clock cycles).
   */
  WDTCSR |= (1 << WDCE) | (1 << WDE);

  /* set new watchdog timeout prescaler value */
  WDTCSR = 1 << WDP0 | 1 << WDP3; /* 8.0 seconds */

  /* Enable the WD interrupt (note no reset). */
  WDTCSR |= _BV(WDIE);
}


/***************************************************
*  Name:        enterSleep
*
*  Returns:     Nothing.
*
*  Parameters:  None.
*
*  Description: Main application loop.
*
***************************************************/
void loop()
{
  if(f_wdt == 1)
  {
    /* Toggle the LED */
    digitalWrite(LED_PIN, !digitalRead(LED_PIN));

    /* Don't forget to clear the flag. */
    f_wdt = 0;

    /* Re-enter sleep mode. */
    enterSleep();
  }
  else
  {
    /* Do nothing. */
  }
}

PeterH

You could use a FOR loop to sleep the required number of times to achieve your desired overall sleep duration.

bloogoo


You could use a FOR loop to sleep the required number of times to achieve your desired overall sleep duration.

I'll be honest, I'm not sure how to do that (code wise). I'm still trying to figure out all of the intricacies of the watchdog code, specifically this section:
Code: [Select]
/* Clear the reset flag. */
  MCUSR &= ~(1 << WDRF);

  /* In order to change WDE or the prescaler, we need to
   * set WDCE (This will allow updates for 4 clock cycles).
   */
  WDTCSR |= (1 << WDCE) | (1 << WDE);

  /* set new watchdog timeout prescaler value */
  WDTCSR = 1 << WDP0 | 1 << WDP3; /* 8.0 seconds */

  /* Enable the WD interrupt (note no reset). */
  WDTCSR |= _BV(WDIE);


Is there an easier way to include this in my sketch? Something similar to using "wdt_enable(WDTO_8S);", or do I need all of the above as well?

nickgammon

It's not that complicated. Here is an example of using a watchdog to reset you:

Code: [Select]

#include <avr/wdt.h>

void setup ()
  {
  Serial.begin (115200);
  wdt_enable(WDTO_1S);  // reset after one second, if no "pat the dog" received
   }  // end of setup

void loop ()
  {
  Serial.println ("Entered loop ...");
  Serial.println ("Point A");
  delay (900);
 
  wdt_reset();  // give me another second to do stuff (pat the dog)
  Serial.println ("Point B");
  delay (900);
 
  while (true) ;   // oops, went into a loop
  }  // end of loop





And here is an example of using a watchdog to wake after sleep. Yes, you have to do it multiple times to get more than 8 seconds, but it isn't that bad:

Code: [Select]

// Example of sleeping and saving power
//
// Author: Nick Gammon
// Date:   25 May 2011

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

#define LED 13

// watchdog interrupt
ISR(WDT_vect)
  {
  wdt_disable();  // disable watchdog
  }

void myWatchdogEnable(const byte interval)
  { 
  MCUSR = 0;                          // reset various flags
  WDTCSR |= 0b00011000;               // see docs, set WDCE, WDE
  WDTCSR =  0b01000000 | interval;    // set WDIE, and appropriate delay

  wdt_reset();
  set_sleep_mode (SLEEP_MODE_PWR_DOWN); 
  sleep_mode();            // now goes to Sleep and waits for the interrupt
  }

void setup()
{
pinMode (LED, OUTPUT);
}  // end of setup

void loop()
{
  digitalWrite (LED, HIGH);  // awake
  delay (2000);    // ie. do stuff here
  digitalWrite (LED, LOW);  // asleep

  // sleep for a total of 20 seconds
  myWatchdogEnable (0b100001);  // 8 seconds
  myWatchdogEnable (0b100001);  // 8 seconds
  myWatchdogEnable (0b100000);  // 4 seconds

}  // end ofloop

// sleep bit patterns:
//  1 second:  0b000110
//  2 seconds: 0b000111
//  4 seconds: 0b100000
//  8 seconds: 0b100001

Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

bloogoo

Quote
And here is an example of using a watchdog to wake after sleep. Yes, you have to do it multiple times to get more than 8 seconds, but it isn't that bad

That's fantastic! Thanks. Then I can use something like this for 4 minutes (240 seconds, or 30x 8 second intervals), correct?

Code: [Select]
  // sleep for a total of 240 seconds
  int i;
  for (i = 0; i <=30; i++)
  { 
    myWatchdogEnable (0b100001);  // 8 seconds
  }

nickgammon

Code: [Select]

  for (i = 0; i <=30; i++)


Yes, but < 30, not <= 30, or that would be 31 times around the loop.
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

bloogoo


Yes, but < 30, not <= 30, or that would be 31 times around the loop.

Right! Thank you.

Now when I want to take measurements, I put that code here?
Code: [Select]
digitalWrite (LED, HIGH);  // awake
  delay (2000);    // ie. do stuff here
  digitalWrite (LED, LOW);  // asleep

nickgammon

If that's where you want to sleep, I suppose so.
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

bloogoo

#8
Jun 26, 2013, 03:57 am Last Edit: Jun 26, 2013, 04:10 am by bloogoo Reason: 1

If that's where you want to sleep, I suppose so.

I'm not sure I understand. Shouldn't I have my measurements taken during the awake phase (ie before the sleep phase starts)? Is that not between the digitalWrite HIGH and LOW in the code you gave (where it says "do stuff here")? Should those measurements be somewhere else in the code?

Here's what I'm using so far:
Code: [Select]
// Example of sleeping and saving power
//
// Author: Nick Gammon
// Date:   25 May 2011

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

/*********************
* PRESCALER - modifies the clock times. For delay(1000) with Mode 4, delay would be 16x longer.
* Mode 0 - Do nothing - normal 16Mhz clock
* Mode 1 - Clock divide by 2 - 8MHz
* Mode 2 - Clock divide by 4 - 4MHz
* Mode 3 - Clock divide by 8 - 2MHz
* Mode 4 - Clock divide by 16 - 1MHz
* Mode 5 - Clock divide by 32 - 500kHz
* Mode 6 - Clock divide by 64 - 250kHz
* Mode 7 - Clock divide by 128 - 125kHz
* Mode 8 - Clock divide by 256 - 62.5kHz
*********************/
#define Set_prescaler(x)  (CLKPR = (1<<CLKPCE),CLKPR = x) //for any delays, they are multiplied by the prescaler time
#define LED 5
int Prescaler_mode = 4;
const int A0_pin = A0;


/*********************
* WATCHDOG INTERRUPT
*********************/
ISR(WDT_vect)
{
  wdt_disable();  // disable watchdog
}


/*********************
* myWatchdogEnable
*********************/
void myWatchdogEnable(const byte interval)

  MCUSR = 0;                          // reset various flags
  WDTCSR |= 0b00011000;               // see docs, set WDCE, WDE
  WDTCSR =  0b01000000 | interval;    // set WDIE, and appropriate delay

  wdt_reset();
  set_sleep_mode (SLEEP_MODE_PWR_DOWN); 
  sleep_mode();            // now goes to Sleep and waits for the interrupt
}


/*********************
* VOID SETUP
*********************/
void setup()
{
  Set_prescaler(Prescaler_mode);  // 1/16 clock speed
  int i;
  for(i = 0; i <= 19; i++)  // all pins set as input with pullup resistor
  {
    pinMode(i, INPUT);
    digitalWrite(i, HIGH);
  }
  pinMode (LED, OUTPUT);
  pinMode(A0_pin, INPUT);
}


/*********************
* VOID LOOP
*********************/
void loop()
{
  if (analogRead(A0_pin) >= 1000)
  {
    digitalWrite (LED, LOW);
  }
  else
  {
    digitalWrite (LED, HIGH);
  }
  delay(65);

  /*********************
   * SLEEP BIT PATTERNS
   * 1 second:  0b000110
   * 2 seconds: 0b000111
   * 4 seconds: 0b100000
   * 8 seconds: 0b100001
   *********************/

  // sleep for a total of 1 seconds
  int i;
  for (i = 0; i < 1; i++)
  { 
    myWatchdogEnable (0b000110);  // 1 seconds
  }
}

nickgammon

I'm not sure I understand the confusion. Naturally you take the measurements during the awake phase.

Quote
I put that code here?


I think I misunderstood what you mean by "that code". I thought you meant "the sleeping code" but you appear to have meant "the measuring code".
Please post technical questions on the forum, not by personal message. Thanks!

More info: http://www.gammon.com.au/electronics

bloogoo


I think I misunderstood what you mean by "that code". I thought you meant "the sleeping code" but you appear to have meant "the measuring code".

Perfect. Thanks for all your help!

roman_d

How can you do that at the end of sleep time to reboot the controller? In your code:
Code: [Select]
// Example of sleeping and saving power
//
// Author: Nick Gammon
// Date:   25 May 2011

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

#define LED 13

// watchdog interrupt
ISR(WDT_vect)
 {
 wdt_disable();  // disable watchdog
 }

void myWatchdogEnable(const byte interval)
 {  
 MCUSR = 0;                          // reset various flags
 WDTCSR |= 0b00011000;               // see docs, set WDCE, WDE
 WDTCSR =  0b01000000 | interval;    // set WDIE, and appropriate delay

 wdt_reset();
 set_sleep_mode (SLEEP_MODE_PWR_DOWN);  
 sleep_mode();            // now goes to Sleep and waits for the interrupt
 }

void setup()
{
pinMode (LED, OUTPUT);
}  // end of setup

void loop()
{
 digitalWrite (LED, HIGH);  // awake
 delay (2000);    // ie. do stuff here
 digitalWrite (LED, LOW);  // asleep

 // sleep for a total of 20 seconds
 myWatchdogEnable (0b100001);  // 8 seconds
 myWatchdogEnable (0b100001);  // 8 seconds
 myWatchdogEnable (0b100000);  // 4 seconds

}  // end ofloop

// sleep bit patterns:
//  1 second:  0b000110
//  2 seconds: 0b000111
//  4 seconds: 0b100000
//  8 seconds: 0b100001

snowmix

Can someone explane how this part of the code works

Code: [Select]
  // sleep for a total of 240 seconds
  int i;
  for (i = 0; i <=30; i++)
  { 
    myWatchdogEnable (0b100001);  // 8 seconds



I don't understand how it can make the arduino sleep

enormousdreamer

#13
Sep 23, 2015, 09:44 am Last Edit: Sep 23, 2015, 09:49 am by enormousdreamer
I know this post is a little bit too old to be replied. However, I got some questions for Mr Nick Gammon.

Here are the questions.

1. From your code the WDTCSR is written in 10 bit.
Code: [Select]

MCUSR = 0;                          // reset various flags
WDTCSR |= 0b00011000;               // see docs, set WDCE, WDE
WDTCSR =  0b01000000 | interval;    // set WDIE, and appropriate delay

But from the datasheet, it's only 8 bit. Which one is the correct one ?

2. How can the bit is filled with "b" value when it's an binary bit. Could you explain what does "b" mean ?

3. I tried the extended sleep code for my program. The sleep program is working. However the time of the sleep is almost no correct.  For Example to set 600 sleep with this code
Code: [Select]

 void tidur ()
{
// total waktu tidur 600s
int  i;
for (i=1; i <=60; i++)
   myWatchdogEnable (0b100001);  // 8s
           myWatchdogEnable (0b000111);  // 2s
}


However, the result shows the time sleep that I count is less than 600s.

Hope you could answer my questions, thanks.

Whandall

The 0b... notation is the standard way of C to express a numeric constant in binary.

The 0 lets the compiler know that it is a numeric constant, the b denotes binary.

10 decimal = 0b00001010 binary = 0x0A hexadecimal = 012 octal

So 0b00011000 still is a byte.
Ah, this is obviously some strange usage of the word 'safe' that I wasn't previously aware of. (D.Adams)

Go Up