Extending the sleep on an Attiny13, extending WDT

I’m using the attiny13 to drive an LED, on for X hours, off for Y. Putting it into sleep mode for Y.
However, I am having a problem figuring out how to go beyond 8 second on the watchdog timer…

Tried using a for loop, but the count seems to be getting reset with each sleep cycle. Help appreciated.

#include <avr/sleep.h>
#include <util/delay.h>

int counter = 5;

void setup() {
  DDRB  = 0b000001; // all but PB0 INPUT, want to use PB0
  PORTB = 0b000000; // all LOW
}

void loop() {
  if (counter > 4){
  digitalWrite(0, HIGH);
  delay(1000);
  digitalWrite(0, LOW);
  counter = 0;
  }
  counter++;
    sleepNow();

}

void sleepNow() {

static int newcounter = counter; // trying to save the count...
  {
    ACSR |= (1 << ACD); //Analog comparator off
    ACSR = ADMUX = ADCSRA = 0;
  }

  //  WDTCR |= (1<<WDP3) ;      //Watchdog set for about 4 seconds
  //set timer to 8 sec

  WDTCR |= (1 << WDP3 ) | (1 << WDP0); // 8s
    


  // Enable watchdog timer interrupts
  WDTCR |= (1 << WDTIE) | (1<<WDCE )|(0<<WDE  );
  sei(); // Enable global interrupts
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);


  sleep_mode();
  sleep_disable();
counter = newcounter;
}

Make an external watchdog. A 555 chip can trigger a reset of the controller after several minutes.

thehardwareman:
Make an external watchdog. A 555 chip can trigger a reset of the controller after several minutes.

Thanks, but I would think some sort of loop should be able to do this? I don't need the timing to be very accurate- and want it to be in sleep for up to 18 hours.

Also, a 555 or other external component would diminish the value of going into sleep (to save power)

If the counter variable is loosing the count the watchdog (or something) must be reseting the device. Double check your watchdog configuration. Looping with a for statement is the classic way to extend sleep time.

http://www.gammon.com.au/power

He has some demos about sleeping a micro, along with other power saving stuff.

Have a look at this thread. I've posted the code to sleep for an hour using multiple watchdog sleeps.

markd833:
Have a look at this thread. I've posted the code to sleep for an hour using multiple watchdog sleeps.

Quite cool, but doesn't look to be compatible with the Attiny.

tf68:
If the counter variable is loosing the count the watchdog (or something) must be reseting the device. Double check your watchdog configuration. Looping with a for statement is the classic way to extend sleep time.

yeah, was thinking that... but haven't figured out what it could be. I think I set the WDT properly to only do an interrupt, not reset, but doesn't seem to be working.

tf68:
Gammon Forum : Electronics : Microprocessors : Power saving techniques for microprocessors

He has some demos about sleeping a micro, along with other power saving stuff.

Thanks, went through it, but couldn't quite get it to work. Will try again...

Appreciate everyone whos trying to help here!

Other approaches I tried:

Using millis, does what I want in reverse (sleeps, then lights up), but wont work for my application as i need it to light up first.

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

// Interval is how long we wait
// add const if this should never change (50 is 25s)
int interval = 50;
// Tracks the time since last event fired
unsigned long previousMillis = 0;

void setup() {
  DDRB  = 0b000001; // all but PB0 INPUT, want to use PB0 ...
  PORTB = 0b000000; // all LOW

}

void loop() {
  unsigned long currentMillis = millis();
  if (((unsigned long)(currentMillis - previousMillis) >= interval)) {
    digitalWrite(0, HIGH);
    //    delay(300000);
    delay(5000);
    digitalWrite(0, LOW);
    previousMillis = currentMillis;
  }
  else {


    sleepNow();
  }
}

void sleepNow() {

  {
    ACSR |= (1 << ACD); //Analog comparator off
    ACSR = ADMUX = ADCSRA = 0;
  }

  //  WDTCR |= (1<<WDP3) ;      //Watchdog set for about 8 seconds
  //set timer to 8 sec

  WDTCR |= (1 << WDP3 ) | (1 << WDP0); // 8s



  // Enable watchdog timer interrupts
  WDTCR |= (1 << WDTIE) | (1 << WDCE ) | (0 << WDE  ); // do not reset?
  //  WDTCR |= (1 << WDTIE) ;

  sei(); // Enable global interrupts
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);


  sleep_mode();
  sleep_disable();
  //counter = newcounter;
}

Tried this but no dice either:

I think the way you are using |= is leaving the reset enabled. Line two clears the reset.

WDTCR = ( _BV( WDCE ) | _BV( WDE ) ); // enable wdt config
WDTCR = ( _BV( WDTIE ) | WDTO_1S ); // config timeout and interrupt only, clear WDE

davidfetter:
Thanks, but I would think some sort of loop should be able to do this? I don't need the timing to be very accurate- and want it to be in sleep for up to 18 hours.

Also, a 555 or other external component would diminish the value of going into sleep (to save power)

CMOS version of the 555 exist. Typical power consumption is 110 uA @ 5V and 90 uA @3V. Check out TS555.

Try adding

EMPTY_INTERRUPT(WDT_vect);

The missing interrupt vector (might) cause a reset.

Hmm, been battling this for too long. An attiny85 work well with the snoore library:
https://forum.arduino.cc/?topic=695371#msg4678003

Thanks all for the attempts.

This should do what you want. Just adjust the sleep time and the loop counter.

/*
 *  sleep w/wdt wakeup t13
 */
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/wdt.h>

const uint8_t LED_PIN = PB0;

// watchdog timeouts
const uint8_t WD_1s = 6;
const uint8_t WD_2s = 7;
const uint8_t WD_4s = 32;
const uint8_t WD_8s = 33;

// watchdog timeout interrupt
ISR( WDT_vect )
  {
    wdt_disable();  
  }
  
/*
 * configure wdt timeout and interrupt clear WDE (reset)
 * configure sleep mode and put to sleep
 */
void wdtSleep ( uint8_t timeout )
  { 
    MCUSR &= ~( _BV( WDRF ));             // clear WDRF (if set)    
    cli();
    WDTCR  = ( _BV( WDCE )|_BV( WDE ) );  // enable wdt config
    WDTCR  = ( _BV( WDTIE )|timeout );    // config timeout and interrupt only, clear WDE
    wdt_reset();
    sei();
    set_sleep_mode ( SLEEP_MODE_PWR_DOWN ); 
    sleep_mode();                         // sleep till wdt timeout interrupt
  }

void setup()
  {
    DDRB |= _BV( LED_PIN );
  }  // setup

void loop()
  {
    
    // wakeup and do stuff
    for ( uint8_t i = 0 ; i < 9 ; ++i )
      {
        PINB = _BV(LED_PIN); // toggle pin
        _delay_ms(250);
      }
        
    // extend sleep by looping
    for ( uint8_t i = 0 ; i < 6 ; ++i )
      {
        wdtSleep ( WD_1s ); // 1s sleep
      }
      
  }  // loop

tf68:
This should do what you want. Just adjust the sleep time and the loop counter.

Thanks TF.
With an 8s setting, I seem to be getting about 50s of On and 50s Sleep. Adjustments to the loop or sleep time, seem to extend/reduce both On time and Sleep time equally- is this the intended behavior?

I'm trying to get On about 3x the time its Sleeping.
Final goal is to get the On time to be 5 hours, Sleep for 19 hours.

I think I misunderstood what you wanted.
Note I changed the size of the loop counters.
The micro will sleep when the output is on as well as off.

/*
 *  sleep w/wdt wakeup t13
 */
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/wdt.h>

const uint8_t LED_PIN = PB0;

// watchdog timeouts
const uint8_t WD_1s = 6;
const uint8_t WD_2s = 7;
const uint8_t WD_4s = 32;
const uint8_t WD_8s = 33;

// watchdog timeout interrupt
ISR( WDT_vect )
  {
    wdt_disable();  
  }
  
/*
 * configure wdt timeout and interrupt clear WDE (reset)
 * configure sleep mode and put to sleep
 */
void wdtSleep ( uint8_t timeout )
  { 
    MCUSR &= ~( _BV( WDRF ));             // clear WDRF (if set)    
    cli();
    WDTCR  = ( _BV( WDCE )|_BV( WDE ) );  // enable wdt config
    WDTCR  = ( _BV( WDTIE )|timeout );    // config timeout and interrupt only, clear WDE
    wdt_reset();
    sei();
//    set_sleep_mode ( SLEEP_MODE_PWR_DOWN ); 
    sleep_mode();                         // sleep till wdt timeout interrupt
  }

void setup()
  {
    DDRB |= _BV( LED_PIN );
    set_sleep_mode ( SLEEP_MODE_PWR_DOWN ); 
  }  // setup

void loop()
  {
    
    // sleeping with output off
    for ( uint16_t i = 0 ; i < 5 ; ++i )  // note I changed i to 
      {
        wdtSleep ( WD_1s ); // 1s sleep
      }
      
    PORTB |= _BV( LED_PIN ); // led on 
        
    // sleeping with output on
    for ( uint16_t i = 0 ; i < 19 ; ++i )
      {
        wdtSleep ( WD_1s ); // 1s sleep
      }

    PORTB &= ~(_BV( LED_PIN )); // led off
      
  }  // loop

tf68:
I think I misunderstood what you wanted.
Note I changed the size of the loop counters.
The micro will sleep when the output is on as well as off.

Awesome! tested and working well. Thank you!
Only change I made was to move the on cycle to the top of the loop so that a reset / power on, turns the LED on.
Not sure if many are still using the attiny13, but saw a number of threads/questions online trying to figure this out- so hopefully your code will help a number of people.

/*
 *  sleep w/wdt wakeup t13
 */
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/wdt.h>

const uint8_t LED_PIN = PB0;

// watchdog timeouts
const uint8_t WD_1s = 6;
const uint8_t WD_2s = 7;
const uint8_t WD_4s = 32;
const uint8_t WD_8s = 33;

// watchdog timeout interrupt
ISR( WDT_vect )
  {
    wdt_disable(); 
  }
 
/*
 * configure wdt timeout and interrupt clear WDE (reset)
 * configure sleep mode and put to sleep
 */
void wdtSleep ( uint8_t timeout )
  {
    MCUSR &= ~( _BV( WDRF ));             // clear WDRF (if set)   
    cli();
    WDTCR  = ( _BV( WDCE )|_BV( WDE ) );  // enable wdt config
    WDTCR  = ( _BV( WDTIE )|timeout );    // config timeout and interrupt only, clear WDE
    wdt_reset();
    sei();
//    set_sleep_mode ( SLEEP_MODE_PWR_DOWN );
    sleep_mode();                         // sleep till wdt timeout interrupt
  }

void setup()
  {
    DDRB |= _BV( LED_PIN );
    set_sleep_mode ( SLEEP_MODE_PWR_DOWN );
  }  // setup

void loop()
  {

    PORTB |= _BV( LED_PIN ); // led on
       
    // sleeping with output on
    for ( uint16_t i = 0 ; i < 5 ; ++i )
      {
        wdtSleep ( WD_1s ); // 1s sleep
      }
    PORTB &= ~(_BV( LED_PIN )); // led off
   
    // sleeping with output off
    for ( uint16_t i = 0 ; i < 20 ; ++i )  // note I changed i to
      {
        wdtSleep ( WD_1s ); // 1s sleep
      }
     


     
  }  // loop

Glad it’s working out for you.
You can tidy things up by wrapping the for code in a function, if you want.

/*
 *  sleep w/wdt wakeup t13
 */
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/wdt.h>

const uint8_t LED_PIN = PB0;

// watchdog timeouts
const uint8_t WD_1s = 6;
const uint8_t WD_2s = 7;
const uint8_t WD_4s = 32;
const uint8_t WD_8s = 33;

const uint8_t wdtTimeout = WD_1s;

const uint16_t onCount   = 16;
const uint16_t offCount  = 6;

// watchdog timeout interrupt
ISR( WDT_vect )
  {
    wdt_disable();  
  }
  
/*
 * configure wdt timeout and interrupt clear WDE (reset)
 * configure sleep mode and put to sleep
 */
void wdtSleep ( uint8_t timeout )
  { 
    MCUSR &= ~( _BV( WDRF ));             // clear WDRF (if set)    
    cli();
    WDTCR  = ( _BV( WDCE )|_BV( WDE ) );  // enable wdt config
    WDTCR  = ( _BV( WDTIE )|timeout );    // config timeout and interrupt only, clear WDE
    wdt_reset();
    sei();
//    set_sleep_mode ( SLEEP_MODE_PWR_DOWN ); 
    sleep_mode();                         // sleep till wdt timeout interrupt
  } // wdtSleep

void doSnooze ( uint8_t wdtTimeout, uint16_t loopCount )
  {
    for ( uint16_t i = 0 ; i < loopCount ; ++i )  
    {
      wdtSleep ( wdtTimeout ); 
    }
  } // doSnooze


void setup()
  {
    DDRB |= _BV( LED_PIN );
    set_sleep_mode ( SLEEP_MODE_PWR_DOWN ); 
  } // setup

void loop() 
  {
    // output off    
      PORTB &= ~(_BV( LED_PIN ));
      doSnooze ( wdtTimeout, offCount );

    // output on    
      PORTB |= _BV( LED_PIN );
      doSnooze ( wdtTimeout, onCount );
      
  } // loop