Sleep mode demo code

On the off chance that the internet needs more sleep examples, and because I wanted to play with GitHub Gists:

Nice. Thanks.

Thanks - that looks far more complete (and complicated) than the one I have already seen.

I do not understand what some of the BOD code is doing, but based on your comments, you turn BOD off for sleeping - but I didn't see any code to turn it back on again. Does that happen by default?

aarondc:
Thanks - that looks far more complete (and complicated) than the one I have already seen.

You're welcome, it's not all that complicated :wink: Anyway it's what works for me. Actually I was looking at it and I may be able to remove a few lines from the ATtinyX5 example. But I'll want to test it first.

I do not understand what some of the BOD code is doing, but based on your comments, you turn BOD off for sleeping - but I didn't see any code to turn it back on again. Does that happen by default?

Yes. Setting the BODS (BOD Sleep) bit only turns off the BOD once the MCU is in sleep mode, not before. When it wakes, it is automatically turned back on.

The BODSE (BOD Sleep Enable) bit allows the BODS bit to be set.

Writing to the BODS bit is controlled by a timed sequence and an enable bit, BODSE in MCUCR. To disable BOD in
relevant sleep modes, both BODS and BODSE must first be set to one. Then, to set the BODS bit, BODS must be
set to one and BODSE must be set to zero within four clock cycles.
The BODS bit is active three clock cycles after it is set. A sleep instruction must be executed while BODS is active
in order to turn off the BOD for the actual sleep mode. The BODS bit is automatically cleared after three clock
cycles.

I will admit that it can take a bit to wrap one's head around that part!

That makes sense to me, thanks.

I am curious - how much time does it take from Interrupt detected to wake the Arduino up? Would a human notice any lag, if the switch they were pressing was generating the Interrupt and they expected an instantaneous response to the button press? Where instantaneous is human-reaction time based, not electronic.

aarondc:
That makes sense to me, thanks.

I am curious - how much time does it take from Interrupt detected to wake the Arduino up? Would a human notice any lag, if the switch they were pressing was generating the Interrupt and they expected an instantaneous response to the button press? Where instantaneous is human-reaction time based, not electronic.

It seems instantaneous to my well-worn reflexes (try the demo code, it blinks an LED when it wakes). It actually depends on the settings of the CKSEL and SUT bits in the fuse bytes (see the datasheet), but the worst case scenario with the longest start-up time (which is the way the fuse bytes are set for an Uno) is 16K clock cycles, but that's just a bit over a millisecond.

An interrupt takes a minimum of four clock cycles, and when waking from sleep mode, an extra four, plus the start-up time.

If it's going to be a millisecond it's a no brainer - takes longer than that to press a button. Awesome.

Just tested your sleep_ATtinyX5.ino on a ATtiny13.

It worked wothout any changes !
I can't even measure the current when it is in sleep (cheap multimeter)

One thing that is strange:
I have to toggle pin PB1, which according to the datasheet is PCINT1 and not PB0 which is PCINT0

Thanks for sharing

Erni:
Just tested your sleep_ATtinyX5.ino on a ATtiny13.

It worked wothout any changes !
I can't even measure the current when it is in sleep (cheap multimeter)

One thing that is strange:
I have to toggle pin PB1, which according to the datasheet is PCINT1 and not PB0 which is PCINT0

Thanks for sharing

Cool, thanks for the feedback. I should probably pick up a couple ATtiny13s just to play with. I see in the datasheet that INT0 is PB1 (DIP pin 6) on the '13 where it is PB2 (DIP pin 7) on the 'X5, so that makes sense. But it should wake as soon as the pin is driven low, toggling it (low then high) should not be necessary.

Are you using the Arduino-Tiny core with the '13?

But it should wake as soon as the pin is driven low, toggling it (low then high) should not be necessary.

That was my poor english, I am actually connecting pin PB1 to ground via a pushbutton

I see in the datasheet that INT0 is PB1 (DIP pin 6)

Yes I see it now, I was looking at PCINT0 :blush:

Are you using the Arduino-Tiny core with the '13?

No, I am using Smeezekitty's core. As far as I know the Arduino-Tiny core does not support ATtiny13.
I suppose you mean this core: Google Code Archive - Long-term storage for Google Code Project Hosting.

Smeezekitty's core:
http://forum.arduino.cc/index.php?topic=89781.0

Erni:
I am using Smeezekitty's core. As far as I know the Arduino-Tiny core does not support ATtiny13.

Smeezekitty's core:
core13: An Arduino core for the Attiny13 *testers wanted* - Microcontrollers - Arduino Forum

Right, I didn't think so. Will check out Smeezekitty's core, thanks.

Hope things are well in DK :slight_smile:

Hi Jack, this is great stuff! How do you wake up the MCU when INT0 is on high?

Thanks,
Akshay

akshaybs:
Hi Jack, this is great stuff! How do you wake up the MCU when INT0 is on high?

It depends on which MCU.

There are four settings for the interrupt sense control (ISC) bits, which are in the MCUCR register for ATtinyX4 and ATtinyX5, or in the EICRA register for ATmega328P. These are low level interrupt, pin change interrupt, falling and rising edge. For example, see section 13.2 in the ATmega328P datasheet.

For the ATmega328P, any of the four types of interrupt will wake the MCU. For the ATtinyX4 and ATtinyX5, only the low level or pin change interrupts will wake the MCU.

Thank you! I am using the ATTiny45. I used a pin change interrupt to wake it up.

Akshay

akshaybs:
Thank you! I am using the ATTiny45. I used a pin change interrupt to wake it up.

Akshay

Good deal, you are welcome!

Hi everyone,
I was wondering if adding an astable 555 timer instead of a button to wake up from sleep mode my attiny 85 will draw more power than a simple delay cycle.

jabella78:
Hi everyone,
I was wondering if adding an astable 555 timer instead of a button to wake up from sleep mode my attiny 85 will draw more power than a simple delay cycle.

A 555 will draw power continuously, so no that would not be the lowest power solution.
Not sure exactly what you mean by "a simple delay cycle".
Give us a little more detail on what you're trying to accomplish and maybe we can be of a little more help.

I have an attiny 85 checking every 10 seconds the temperature of the room and decides to power on the AC via 38khz LED.
This is the code:

#include <IRTinyTX.h>
 
float temp;
int tempPin = 3;
int on_off = 1;
 
unsigned int rawCodes[] = {8850,  4500, 600,  1650, 600,  1650, 600,  550, 600,  600, 550,  550, 600,  550, 600,  550, 600,  1700, 550,  550, 600,  1650, 600,  1650, 650,  550, 600,  550, 600,  550, 600,  550, 600,  550, 600,  550, 600,  550, 600,  1650, 600,  550, 650,  500, 600,  550, 600,  550, 600,  600, 600,  500, 650,  1650, 600,  500, 650,  550, 600,  550, 600,  550, 600,  1650, 600,  550, 600,  550, 600,  600, 550,  550, 600,  600, 600,  550, 600, 500, 600,  550, 600,  600, 600,  550, 600,  550, 600,  550, 600,  550, 600,  550, 600,  550, 600,  550, 600,  550, 600};             

IRsend irsend;

void setup()
{
   for (byte i=0; i<5; i++) {
     pinMode(i, INPUT);
     digitalWrite(i, HIGH);
   }  
   digitalWrite(tempPin, LOW);
   pinMode(1, OUTPUT);
   digitalWrite(1, LOW);
}

void loop() 
{
  temp = analogRead(tempPin);
  temp = temp * 0.48828125;
   
  if (temp > 26.00 && on_off == 0) {
      irsend.sendRaw(rawCodes, 99, 38); 
       on_off = 1;
      }
  else if (temp < 24.00 && on_off == 1) {
        irsend.sendRaw(rawCodes, 99, 38); 
        on_off = 0;
      }
  delay(10000);
}

Instead of delay(10000) I was wondering if implementing your code with a 555 as trigger, the power consumption would be less.

jabella78:
Instead of delay(10000) I was wondering if implementing your code with a 555 as trigger, the power consumption would be less.

Beats me, really, without checking a 555 data sheet. I'm not current on the CMOS versions, I imagine they draw less current than the older bipolar types.

But why not use the watchdog timer to wake the MCU? No additional hardware needed, and I'd bet that the ATtiny85 sleeping with just the WDT running will draw less current than any 555 (should be on the order of 4-6µA).

Checking every 10 seconds seems maybe a bit excessive but done properly it shouldn't make all that much difference to the energy budget. I might also keep a running average over about a minute or something like that. Might make for smoother operation but I see there is already some hysteresis built in so that is good.

Just see how to set watchdog timer!
Should this work?

#include <IRTinyTX.h>
#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

float temp;
volatile boolean f_wdt = 1;
int tempPin = 3;
int on_off = 0;
unsigned int rawCodes[] = {8850,  4500, 600,  1650, 600,  1650, 600,  550, 600,  600, 550,  550, 600,  550, 600,  550, 600,  1700, 550,  550, 600,  1650, 600,  1650, 650,  550, 600,  550, 600,  550, 600,  550, 600,  550, 600,  550, 600,  550, 600,  1650, 600,  550, 650,  500, 600,  550, 600,  550, 600,  600, 600,  500, 650,  1650, 600,  500, 650,  550, 600,  550, 600,  550, 600,  1650, 600,  550, 600,  550, 600,  600, 550,  550, 600,  600, 600,  550, 600, 500, 600,  550, 600,  600, 600,  550, 600,  550, 600,  550, 600,  550, 600,  550, 600,  550, 600,  550, 600,  550, 600};             

IRsend irsend;

void setup()
{
   for (byte i=0; i<5; i++) {
     pinMode(i, INPUT);
     digitalWrite(i, HIGH);
   }  
   digitalWrite(tempPin, LOW);
   pinMode(1, OUTPUT);
   digitalWrite(1, LOW);
   setup_watchdog(9);
}

void loop() 
{
   if (f_wdt==1) {  // wait for timed out watchdog / flag is set when a watchdog timeout occurs
    f_wdt=0;       // reset flag
  
  temp = analogRead(tempPin);
  temp = temp * 0.48828125;
   
  if (temp > 26.00 && on_off == 0) {
      irsend.sendRaw(rawCodes, 99, 38); 
       on_off = 1;
      }
  else if (temp < 24.00 && on_off == 1) {
        irsend.sendRaw(rawCodes, 99, 38); 
        on_off = 0;
      }
  system_sleep();
  }
}

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 for suggestion, appreciated!