[RESOLVED] Arduino staying in sleep mode

Hi,

I am using the Arduino Uno R3 board and the Arduino IDE.

The Arduino is getting ‘stuck’ if the interrupt line is held low for longer than the delay inside of the main loop(). Is there a way around this? I plan to be using a push button to wake up and take a temperature measurement and then go back to sleep again. So would be great if I did not have to keep a delay inside the loop/ be careful of when pushing the button.

Thank you!

#include <avr/sleep.h>

void setup() 
{
  Serial.begin(9600);//Start Serial Comunication
  pinMode(LED_BUILTIN, OUTPUT);//We use the led on pin 13 to indecate when Arduino is asleep
  pinMode(2, INPUT_PULLUP);//Set pin d2 to input using the buildin pullup resistor
  digitalWrite(LED_BUILTIN, HIGH);//turning LED on
}

void loop() 
{
  delay(5000); // needed, otherwise gets stuck in sleep mode? 
  Sleep_CPU(); // Call function to sleep CPU
}

void Sleep_CPU(void)
{
  sleep_enable(); // set sleep bit
  attachInterrupt(digitalPinToInterrupt(2), Wake_CPU, LOW); // attach interrupt to wake CPU after sleep. 
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Sets the sleep mode
  digitalWrite(LED_BUILTIN, LOW); // turn off LED to show sleep mode
  delay(1000);
  sleep_cpu(); // put to sleep - will wake up here. 
  Serial.println("woke up"); // first line of code executed after sleep. 
  digitalWrite(LED_BUILTIN, HIGH);//turning LED on
}

void Wake_CPU(void)
{
  sleep_disable();
  detachInterrupt(digitalPinToInterrupt(2)); //Removes the interrupt from pin 2;
}

attachInterrupt(digitalPinToInterrupt(2), Wake_CPU, LOW);

This interrupt fires as long as the pin is held down. Try changing the LOW to FALLING your problem is probably gone.

pylon:
This interrupt fires as long as the pin is held down. Try changing the LOW to FALLING your problem is probably gone.

I tried the different triggering edges. When using FALLING it appears to get stuck even pressing it every 5 seconds. LOW seemed to work reliably except for holding it down for longer than that delay.

Try this:

void Sleep_CPU(void)
{
  sleep_enable(); // set sleep bit
  attachInterrupt(digitalPinToInterrupt(2), Wake_CPU, LOW); // attach interrupt to wake CPU after sleep.
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Sets the sleep mode
  digitalWrite(LED_BUILTIN, LOW); // turn off LED to show sleep mode
  delay(1000);
  sleep_cpu(); // put to sleep - will wake up here.
  sleep_disable();
  detachInterrupt(digitalPinToInterrupt(2)); //Removes the interrupt from pin 2;
  Serial.println("woke up"); // first line of code executed after sleep.
  digitalWrite(LED_BUILTIN, HIGH);//turning LED on
}

void Wake_CPU(void)
{
}

arduarn:
Try this:

void Sleep_CPU(void)

{
  sleep_enable(); // set sleep bit
  attachInterrupt(digitalPinToInterrupt(2), Wake_CPU, LOW); // attach interrupt to wake CPU after sleep.
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Sets the sleep mode
  digitalWrite(LED_BUILTIN, LOW); // turn off LED to show sleep mode
  delay(1000);
  sleep_cpu(); // put to sleep - will wake up here.
  sleep_disable();
  detachInterrupt(digitalPinToInterrupt(2)); //Removes the interrupt from pin 2;
  Serial.println(“woke up”); // first line of code executed after sleep.
  digitalWrite(LED_BUILTIN, HIGH);//turning LED on
}

void Wake_CPU(void)
{
}

That appears to solve the problem of getting stuck! Is this a good solution? To not be doing anything inside the ISR?

Thank you though!

Yes it is a good solution. It is not unusual to have an empty interrupt handler just for wake-ups.

The reason is was going wrong was because in between attaching the interrupt handler then going to sleep, an interrupt was occurring that detached the interrupt handler.

The problem is you are disabling the ISR in the ISR itself. LOW sets the interrupt to trigger off the signal’s level, so it will trigger anytime the pin is LOW, not just when it transitions to LOW.

If you’re holding the pin LOW when you attachInterrupt, the ISR will run immediately. The code inisde it will then disable itself and also disable sleep.

Come to think of it how can you be getting stuck? sleep_cpu() should do nothing if sleep is disabled. Meh, software’s weird.

The reason FALLING does not work is because an external level change interrupt (which is what FALLING and RISING are) is not one of the interrupt sources the datasheet says you can use to wake up from Power-Down Mode. An external level interrupt does work though.

Try this:

#include <avr/sleep.h>

void setup() 
{
  Serial.begin(9600);//Start Serial Comunication
  pinMode(LED_BUILTIN, OUTPUT);//We use the led on pin 13 to indecate when Arduino is asleep
  pinMode(2, INPUT_PULLUP);//Set pin d2 to input using the buildin pullup resistor
  digitalWrite(LED_BUILTIN, HIGH);//turning LED on
}

void loop() 
{
  delay(5000); // needed, otherwise gets stuck in sleep mode? 
  Sleep_CPU(); // Call function to sleep CPU
}

void Sleep_CPU(void)
{
  Serial.flush();
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // Sets the sleep mode
  cli();
  sleep_enable(); // set sleep bit
  attachInterrupt(digitalPinToInterrupt(2), Wake_CPU, LOW); // attach interrupt to wake CPU after sleep. 
  digitalWrite(LED_BUILTIN, LOW); // turn off LED to show sleep mode
  sei();
  sleep_cpu(); // put to sleep - will wake up here. 
  sleep_disable();
  Serial.println("woke up"); // first line of code executed after sleep. 
  digitalWrite(LED_BUILTIN, HIGH);//turning LED on
}

void Wake_CPU(void)
{
  detachInterrupt(digitalPinToInterrupt(2)); //Removes the interrupt from pin 2;
}

For my additions:

Because serial transmission happens in the background through interrupts, it’s a good idea to call Serial.flush before going to sleep to make sure all the letters get out. If you don’t then it could stop in the middle of sending values. Won’t happen in the exact sketch becasue of the long delays, but it will happen if you remove them.

cli() disables interrupts. We have to do this before attaching the interrupt because we don’t want any chance of the ISR running before we go to sleep. The sei() statement a few lines down re-enables interrupts. However, after re-enabling interrupts the very next instruction (in this case sleep_cpu) is run with interrupts still disabled*. This sequence gives you a 100% chance of being able to attach the interrupt and go to sleep without anything interfering.

  • Generally speaking, one line of code usually doesn’t equal a single assembly instruction. sleep_cpu() (along with a few other macros) is special in this regard.

Thank you Arduarn.

Jiggy-Ninja that works and those good points you are making that I did not consider. Will definitely use Serial.flush() then as I will not be using the delays. Thank you :slight_smile:

1 Like

Using detachInterrupt() is usually a mistake - don't call it unless you fully understand the complex and
surprizing behaviour of interrupt handling.

Its dead easy to add a volatile bool variable to control the body of an ISR:

volatile bool enable_isr = true;

void my_isr()
{
    if (!enable_isr)
        return;
    ....
}

MarkT:
Using detachInterrupt() is usually a mistake - don’t call it unless you fully understand the complex and
surprizing behaviour of interrupt handling.

Its dead easy to add a volatile bool variable to control the body of an ISR:

volatile bool enable_isr = true;

void my_isr()
{
    if (!enable_isr)
        return;
    …
}

Will this work if you hold down a level triggered interrupt? I suspect that the ISR will keep getting called as long as the button is held down. Making the ISR do nothing is not the same thing as turning the interrupt off.