Sleep Mode for Newbies?

Hello everyone! New to forum so please be patient with me.

So before I started this posts a did a bit of research, and couldn't find an answer to my problem. :confused: I was hoping to get help from the community or find a better solution to what I'm trying to achieve.

But, what i did find is mostly old stuff and hard to find. So I'm hoping everything can come together in this post.

What I have been trying to accomplish is esenetialy have an on and off button for my project. I would like to press a "power" button and have all my inputs turn off and then have it ignore all other input until the button is pressed again.

The problem I have ran into is that when I run the sleep mode sketch everything stays in the same state.

I've also had a very hard time modifying the sketch or even trying to add it to another sketch. My biggest problem is not having the sleep function trigger internally. I want it to stay on after initial start up and stay on until the power button is pressed and then back on when its pressed again latter.

Read nick's gammon power blog for the 328p

'Old stuff' does not mean it's bad stuff...

TheMunkee:
The problem I have ran into is that when I run the sleep mode sketch everything stays in the same state.

I've also had a very hard time modifying the sketch or even trying to add it to another sketch. My biggest problem is not having the sleep function trigger internally. I want it to stay on after initial start up and stay on until the power button is pressed and then back on when its pressed again latter.

This isn't EXACTLY what you are asking, but it may offer some guidance or ideas. It's a piece of the code I use with an ATTiny85 AVR which is meant to sleep (i.e. run on battery), then wake up when the button is pressed (thereby pulling the INT0 pin low). The AVR wakes up, runs some code, then goes back to sleep.

ISR (INT0_vect) // INT0 handler - we arrive here when the button is pressed
{
    cli (); // interrupts off
    GIMSK &= ~_BV(INT0); // disable hardware INT0
    sleep_disable (); // prevent going back to sleep
}

void setup (void)
{
    // turn off ADC to save power
    ADCSRA = 0x00;
    // setup INT0 to be triggered by the button
    GIMSK &= ~_BV(INT0); // disable hardware INT0
    MCUCR &= ~(_BV(ISC01) | _BV(ISC00)); // set hardware INT0 active on low LEVEL
}

void loop (void)
{
    cli (); // disable interrupts
    set_sleep_mode (SLEEP_MODE_PWR_DOWN); // set CPU sleep mode
    sleep_enable (); // enable CPU to be powered down
    sleep_bod_disable (); // turn off brown-out detector
    GIMSK |= _BV(INT0); // enable INT0 to trigger active low
    sei (); // enable interrupts (enable button to be detected)
    sleep_cpu (); // power down cpu (all clocks stopped)

    ////////////////////////////////////////////////////////////////////////////
    ///////// cpu is now asleep - awaiting pushbutton LOW to fire INT0 /////////
    ////////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////////////////////////////////////////////
    //////// THIS CODE RUNS WHEN THE BUTTON IS PRESSED, THEN SLEEP AGAIN ///////
    ////////////////////////////////////////////////////////////////////////////
    }
}

Of course, the interrupt and pin names will be different for different AVR devices, but the idea is the same.

Hope this helps.

(edit to add): You may need to add a few include statements to your sketch, depending on which IDE you use:

#include <avr/interrupt.h> // interrupt vectors & misc
#include <avr/sleep.h>     // CPU low power sleep mode support
#include <avr/power.h>     // CPU power save

J-M-L:
Read nick's gammon power blog for the 328p

'Old stuff' does not mean it's bad stuff...

I should have chosen my words better. What I meant was, I am concerned that some of the code or tutorials i am reading don't work on the new IDE or the newer chips. There has been a few times that I follow something to the "T" and the sketches don't work. Then I try to copy and paste them and still don't work. So I was hoping to see some new code explained or shown.

Krupski:
This isn't EXACTLY what you are asking, but it may offer some guidance or ideas. It's a piece of the code I use with an ATTiny85 AVR which is meant to sleep (i.e. run on battery), then wake up when the button is pressed (thereby pulling the INT0 pin low). The AVR wakes up, runs some code, then goes back to sleep.

ISR (INT0_vect) // INT0 handler - we arrive here when the button is pressed

{
    cli (); // interrupts off
    GIMSK &= ~_BV(INT0); // disable hardware INT0
    sleep_disable (); // prevent going back to sleep
}

void setup (void)
{
    // turn off ADC to save power
    ADCSRA = 0x00;
    // setup INT0 to be triggered by the button
    GIMSK &= ~_BV(INT0); // disable hardware INT0
    MCUCR &= ~(_BV(ISC01) | _BV(ISC00)); // set hardware INT0 active on low LEVEL
}

void loop (void)
{
    cli (); // disable interrupts
    set_sleep_mode (SLEEP_MODE_PWR_DOWN); // set CPU sleep mode
    sleep_enable (); // enable CPU to be powered down
    sleep_bod_disable (); // turn off brown-out detector
    GIMSK |= _BV(INT0); // enable INT0 to trigger active low
    sei (); // enable interrupts (enable button to be detected)
    sleep_cpu (); // power down cpu (all clocks stopped)

}

That code is blowing my mind! Is that two different types of code? I really dont get the "GIMSK &= ~_BV(INT0)" parts. So, what exactly is AVR?

TheMunkee:
That code is blowing my mind! Is that two different types of code? I really dont get the "GIMSK &= ~_BV(INT0)" parts. So, what exactly is AVR?

OK... (LOL!) AVR is the "brand name" of the Atmel microcontrollers that are used in (most of) the Arduino boards.

For example, the full name of the chip in an Arduino UNO is "Atmel AVR-MEGA ATmega328P". The chip in the larger "Mega2560 R3" board is an "Atmel AVR-MEGA ATmega2560".

The Atmel AVR series of microcontrollers are very similar as far as internal layout, register names, bit names, etc... they mostly differ in memory size (for example, the 328P has 32K of flash memory to store programs while the 2560 has 256K of flash memory - similarly the 328P has 2K of RAM while the 2560 has 8K - the 328P has one UART (serial interface) while the 2560 has 4, etc.......).

See? Different amounts of memory and features, but all belong to the "AVR family".

As far as the "mind blowing code", let me try to help you there (the following examples assume an Arduino UNO board):

Let's say you want to connect an LED to pin 8 and make it blink. You can do it 2 different ways. The first way is to use the "Arduino style" function "digitalWrite" as such:

void setup (void)
{
    pinMode (8, OUTPUT);
}

void loop (void)
{
    digitalWrite (8, HIGH);
    // some delay
    digitalWrite (8, LOW);
    // some delay
}

But, what exactly IS "pinMode" and "digitalWrite" doing? Well, you can probably guess that first the bit you want to use ( the particular one that drives Pin 8 ) needs to be set as an OUTPUT and that then you need to set that pin high, wait a while, set it low, wait a while, etc.....

So, first I will look at the schematic diagram for the Arduino UNO and find what "Pin 8" is actually connected to... hang on a sec... OK got it... Pin 8 is connected to the ATmega328P pin 14 which happens to be PORT B, Bit 0.

Looking elsewhere in the databook, I find that there are 3 different "registers" that control PORTB. These are "PORTB" (the output), PINB (the input) and DDRB (the data direction register - that is "make a bit input or output?").

I want PORTB, Bit 0 to be an output, so first thing I need to do is this:

[b][tt][b]
DDRB |= _BV(0);[/b]

// "|" means "OR"[/b][/tt]

Read out loud, that says "Take the bit value of bit 0, get the current value of data direction register B and OR in the bit 0 value".

In the chip, let's assume that DDRB bits 6 and 7 are already set as outputs by something else. DDRB looks like this:

[b]BIT    7  6  5  4  3  2  1  0[/b]
[b]VALUE  1  1  0  0  0  0  0  [color=red]0[/color]


[/b]

after I "or-in" the bit value for bit 0, DDRB will look like this (the difference shown in red):

[b]BIT    7  6  5  4  3  2  1  0[/b]
[b]VALUE  1  1  0  0  0  0  0  [color=red]1[/color][/b]

Got it so far? Good.

Now we want to turn the bit on and off, and it's bit 0 so we will use the same _BV(0) statement. By the way, "_BV" is just a macro or a "helper" that returns the numeric value of a bit. For example, the decimal value of bit 5 is "32", so _BV(5) returns "32" (saves you the grief of having to figure it out yourself).

OK, now the rest of the code. Since we want to control an output, we need PORTB (not PINB, which is an input). So, to turn the LED on, we would do this:

[tt][b]PORTB |= _BV(0); // the "|" means "or"
[/b]

[/tt]simple enough... we OR in the bit value of bit 0 into PORTB, then do our time delay (not shown above). But how to turn the bit OFF??? Looking at logic, we know that 0 or 1 is 1, and also 1 or 1 is one. But how do we get 0? By using AND.

0 and 1 is 0, while 1 and 1 is one, so to get that bit to be a zero, we need to AND it with a 0. Notice that we DO NOT want to mess up any other bits... we ONLY want to set bit 0 to 0 (low).

So, using AND we need this:

[tt][tt][b]BIT  7  6  5  4  3  2  1  0
     1  1  1  1  1  1  1  [color=red]0[/color][/b]

[/tt]
[/tt]but, how do we get that value using _BV? We could just use "0xFE" now that we know the value needed, but in a large program, do you want to bother figuring out the AND value to use for every and any bit? Heck no, it's a pain in the rear!

Luckily, there's a shortcut... the "~" (tilde) character. It means "take any 1 bit and make it 0, take any 0 bit and make it 1".

Cool!!! So, our original _BV(0) looked like this:

[b]BIT  7  6  5  4  3  2  1  0
     0  0  0  0  0  0  0  [color=red]1[/color][/b]

...and when we use the tilde to "flip it over" we get this:

[b]     1  1  1  1  1  1  1  [color=red]0[/color][/b]

how cool is that?

So, to turn OFF the LED, all we need is this:

[tt][b]PORTB &= ~_BV(0); // the "&" means "and"[/b][/tt]

now, put it all together and we have:

DDRB |= _BV(0); // set port b, bit 0 as an output

while (1) { // do forever (loop)
    PORTB |= _BV(0); // LED on
    // some delay
    PORTB &= ~_BV(0); // LED off
    // some delay
}

Does this all make sense now? If not, read it through a few times... you'll get it!

:slight_smile:

I do get it! Thank you for taking the time to explain that. I see you have to reference to something to figure out what all the pins are called in that logic language. Do you have any links I can refer to maybe?

Ok, so that first sketch, it turns itself off as well, and despite my best efforts I still can't figure out how to write something that wont turn itself off untill I press something and won't stay on till I press it again.

I modified this code that would blink the led turn off, you push a button and it turns on, blinks again, and turns back off. Arduino Sleep, pin interrupt - Arduino Forum - Arduino - element14 Community

    #include <avr/sleep.h>  
      
    int wakePin = 2;                 // pin used for waking up  
    int led=10;  
    int sleep = 0;
    
    void wakeUpNow() {  
      // execute code here after wake-up before returning to the loop() function  
      // timers and code using timers (serial.print and more...) will not work here.  
      // we don't really need to execute any special functions here, since we  
      // just want the thing to wake up  

    }  
      
    void setup() {  
      pinMode(wakePin, INPUT);  
      pinMode(led, OUTPUT);   
      attachInterrupt(0, wakeUpNow, LOW); // use interrupt 0 (pin 2) and run function wakeUpNow when pin 2 gets LOW  
    }  
      
    void sleepNow() { 
      digitalRead(wakePin);
      if(wakePin, LOW){ 
        set_sleep_mode(SLEEP_MODE_PWR_DOWN);   // sleep mode is set here  
        sleep_enable();          // enables the sleep bit in the mcucr register  
        attachInterrupt(0,wakeUpNow, LOW); // use interrupt 0 (pin 2) and run function  
        sleep_mode(); 
      }
        // here the device is actually put to sleep!!  
        // THE PROGRAM CONTINUES FROM HERE AFTER WAKING UP  
        sleep_disable();         // first thing after waking from sleep: disable sleep...  
             
    }
    void loop() {  
      digitalWrite(led, HIGH);  
      delay(1000);  
      digitalWrite(led, LOW);  
      delay(200);
      digitalWrite(led,HIGH);
      delay(1000);
      digitalWrite(led, LOW);
      sleep = digitalRead(wakePin);
      if(wakePin, LOW){
      sleepNow();     // sleep function called here  
      }
      sleep_disable();
      digitalWrite(led, HIGH);

    }
    

}

I tried writing it in two different places in two different ways and it just would not work. it would just keep blinking, so turn itself on and off, and when I pressed the button it would blink faster.

I think that (sleep_mode) isn't what I was going for after all. I don't see it being able to turn off everything else along with it. It seems to just pause the loop() and turn off the internal clock. Am I correct at assuming this or am I totally off?

Can you clarify then what you are trying to achieve? If you want to loop to keep running while a button is in a certain state and not do anything otherwise, it just takes a if at the begining of the loop checking th state of the button before executing any code for example

void loop()
{
    if (digitalRead(holdButton) == HIGH) { // assuming input pull-up on button so HIGH means not pressed
          // then proceed

   } else {
         // suspended mode, blink a led or provide user feedback or do something else
   }
}

That construct will basically have your code suspended when button is pressed. It will only be caught when the loop() loops, so not in the middle of your code as an interrupt would do. In most cases if your code is architected in the right way that is good enough .

Exactly what I'm trying to do is have a capasitive touch button that turns my entire project on and off. Also, when it is switching on or off it might or might not be running on back-up batteries, so saving as much energy as possible when "off" would be ideal.

So what I thought I could do with sleep mode is: power up, execute code, press a button, all output pins go to low, enters sleep mode.

But out of all the videos and foruns and blog and help I've gotten it seems like all it can do is: power up, put self to sleep, push button, wake up, execute more code, go to sleep, wait for button to be pressed again.

I was pretty sure that its possible, the code is just out of my level probably.

The code you are looking at will most likely do the job- the only extra step appears to be setting all the outputs to zero before you sleep.

However, as an alternative..

If you have a proper digital signal you can use the arduino reset pin for this.

  1. Wire arduino RESET pin to ground via pull-down resistor (say 2K2 or so)

  2. Wire your pushbutton between RESET pin and +5V via a resistor (say 270 ohm)

  3. Wire one of the digital output pins to RESET via a resistor (say 270 ohm).

Then your in your program's setup() section.

  1. Set digital pin to HIGH so preventing RESET
  2. Do what you need
  3. Set all the other outputs to LOW
  4. Set the connected output to LOW (which will force reset).

No code needs to go in the loop() section.

(its probably a bad idea to press the button when trying to program your arduino as the RESET is also used for uploading your program)

Its likely you will have to deal with switch bounce..

rw950431:
The code you are looking at will most likely do the job- the only extra step appears to be setting all the outputs to zero before you sleep.

However, as an alternative..

If you have a proper digital signal you can use the arduino reset pin for this.

  1. Wire arduino RESET pin to ground via pull-down resistor (say 2K2 or so)

  2. Wire your pushbutton between RESET pin and +5V via a resistor (say 270 ohm)

  3. Wire one of the digital output pins to RESET via a resistor (say 270 ohm).

Then your in your program's setup() section.

  1. Set digital pin to HIGH so preventing RESET
  2. Do what you need
  3. Set all the other outputs to LOW
  4. Set the connected output to LOW (which will force reset).

No code needs to go in the loop() section.

(its probably a bad idea to press the button when trying to program your arduino as the RESET is also used for uploading your program)

Its likely you will have to deal with switch bounce..

Just to make sure I understood you. Something like this?

Could I maybe get a little bit more help with that sketch maybe? There's still a few things I don't get. Like for instance, where in that code is the pin that the button is connected declared? How do change it so that it doesn't turn itself off? And, is it easy to add the "All pins LOW" command to execute along with the "Sleep_Mode" function?

Neat trick: AFTER you attach your image and save your post right-click on the attached image and "Copy link location". Re-edit your post, choose "insert image" and paste the link. Now your image is displayed! (Its a mystery why the forum software cant do this automatically)

The placement of the labels doesnt seem to align with the components but I assume R1 is the horizontal one from RESET to ground, R2 goes from 5V to top of switch and R3 from D2 to RESET.

You should connect the bottom of the switch (currently showing connected to ground) to the RESET pin instead- that way the switch will pull up the reset pin and remove the reset condition. Your program then sets D2 high which holds the RESET pin up until you are done. My original suggestion was 2.2K resistor rather than 22K for R1 but its not that critical.

Thank you, thank you, thank you! That trick will be very helpful! And Thank you for the correction.

I am trying to understand why this code isn't working. I'm just trying to understand why it isn't shutting down with a button press.

#include <avr/sleep.h>


int wakePin = 2;                 // pin used for waking up
int sleepStatus = 0;             // variable to store a request for sleep
int count = 0;                   // counter

void wakeUpNow()        // here the interrupt is handled after wakeup
{
  // execute code here after wake-up before returning to the loop() function
  // timers and code using timers (serial.print and more...) will not work here.
  // we don't really need to execute any special functions here, since we
  // just want the thing to wake up
}

void setup()
{
  pinMode(wakePin, INPUT);

  Serial.begin(9600);

  attachInterrupt(0, wakeUpNow, LOW); // use interrupt 0 (pin 2) and run function
                                      // wakeUpNow when pin 2 gets LOW 
}

void sleepNow()         // here we put the arduino to sleep
{
   
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);   // sleep mode is set here

    sleep_enable();          // enables the sleep bit in the mcucr register
                             // so sleep is possible. just a safety pin 

    attachInterrupt(0,wakeUpNow, LOW); // use interrupt 0 (pin 2) and run function
                                       // wakeUpNow when pin 2 gets LOW 

    sleep_mode();            // here the device is actually put to sleep!!
                             // THE PROGRAM CONTINUES FROM HERE AFTER WAKING UP

    sleep_disable();         // first thing after waking from sleep:
                             // disable sleep...
    detachInterrupt(0);      // disables interrupt 0 on pin 2 so the 
                             // wakeUpNow code will not be executed 
                             // during normal running time.

}

void loop()
{
  // display information about the counter
  Serial.print("Awake for ");
  Serial.print(count);
  Serial.println("sec");
  count++;
  delay(1000);                           // waits for a second

  // compute the serial input
  if (Serial.available()) {
    int val = Serial.read();
    if (val == 'S') {
      Serial.println("Serial: Entering Sleep mode");
      delay(100);     // this delay is needed, the sleep 
                      //function will provoke a Serial error otherwise!!
      count = 0;
      sleepNow();     // sleep function called here
    }
    if (val == 'A') {
      Serial.println("Hola Caracola"); // classic dummy message
    }
  }
if(wakePin == HIGH) {
  delay(10);
 Serial.println("Timer: Entering Sleep mode");
      delay(10);     // this delay is needed, the sleep 
                      //function will provoke a Serial error otherwise!!
      count = 0;
      sleepNow();     // sleep function called here
}

  // check if it should go to sleep because of time
  if (count >= 100) {
      Serial.println("Timer: Entering Sleep mode");
      delay(100);     // this delay is needed, the sleep 
                      //function will provoke a Serial error otherwise!!
      count = 0;
      sleepNow();     // sleep function called here
  }
}

Suspect your first problem is this

if(wakePin == HIGH) {

When you really want

if(digitalRead(wakePin) == HIGH) {

Assuming that your switch is wired so wakePin goes LOW when pressed this will mean your program can only run when its held down: not obvious if this is what you actually want or not.