Nicks low power code, modifying for two push buttons

Hi all, I would like to modify Nick Gammons' power saving code to have two buttons (on interrupt pins). In the current code it seems that if the processor wakes up it runs the whole loop function, but I would like it to run a prefined function on one button input and another on the second button input?

Thanks

#include <avr/sleep.h>

const byte LED = 9;
  
void wake ()
{
  // cancel sleep as a precaution
  sleep_disable();
  // must do this as the pin will probably stay low for a while
  detachInterrupt (0);
}  // end of wake

void setup () 
  {
  digitalWrite (2, HIGH);  // enable pull-up
  }  // end of setup

void loop () 
{
 
  pinMode (LED, OUTPUT);
  digitalWrite (LED, HIGH);
  delay (50);
  digitalWrite (LED, LOW);
  delay (50);
  pinMode (LED, INPUT);
  
  // disable ADC
  ADCSRA = 0;  
  
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);  
  sleep_enable();

  // Do not interrupt before we go to sleep, or the
  // ISR will detach interrupts and we won't wake.
  noInterrupts ();
  
  // will be called when pin D2 goes low  
  attachInterrupt (0, wake, LOW);
 
  // turn off brown-out enable in software
  // BODS must be set to one and BODSE must be set to zero within four clock cycles
  MCUCR = bit (BODS) | bit (BODSE);
  // The BODS bit is automatically cleared after three clock cycles
  MCUCR = bit (BODS); 
  
  // We are guaranteed that the sleep_cpu call will be done
  // as the processor executes the next instruction after
  // interrupts are turned on.
  interrupts ();  // one cycle
  sleep_cpu ();   // one cycle

  } // end of loop

Hi,

I've not done what you're suggesting but have a couple of thoughts and am very interested to read more (better) solutions to your puzzle.

Firstly since you attach one pin to the wake interrupt I was thinking if you only need two functions why not use the reset pin with the 2nd switch. Before the code goes into sleep mode, write a flag to the EEPROM. If the normal wake interrupt occurs do one action, and if that flag is present in EEPROM when you start your sketch you know to act on the 2nd wake-up action.

The strength of that plan is no additional power is consumed over Nick's design. The weakness in that plan is if the Arduino loses its DC supply while in low power mode, when the power returns it will be in a state where it will complete the 2nd action. So there's probably need for a further check of some type...

The other thought needs more development yet. It was to have your two switches wired such that they pull the wake interrupt pin low when pressed as well as their own pin (I'm thinking via a diode so they don't pull each other low too). The wake interrupt fires, then immediately check both the switch pins via digitalRead() to see which is closed and act accordingly.

Cheers ! Geoff

Hmmm I'm not sure how one one would use diodes? Is it not possible to decide which interupt has been triggered and run a routine based on that?

Thanks :slight_smile:

Instead of:

// will be called when pin D2 goes low  
  attachInterrupt (0, wake, LOW);

You would have:

// will be called when pin D2 goes low  
  attachInterrupt (0, wake_A, LOW);
// will be called when pin D3 goes low  
  attachInterrupt (1, wake_B, LOW);

So you immediately know which interrupt woke you. Each interrupt routine sets a variable. Then in loop you test which one was set. So what if you go through loop again after that? You are basically doing to the code to put it back to sleep.

Hi Nick, many thanks for the great resources!

What is the variable that it sets?

Whatever variable you want. eg.

volatile byte wakeUpPin;

void combinedWake ()
{
  // cancel sleep as a precaution
  sleep_disable();
  // must do this as the pin will probably stay low for a while
  detachInterrupt (0);
}  // end of combinedWake

void wake_A ()
{
wakeUpPin = 2;
combinedWake ();
}  // end of wake_A

void wake_B ()
{
wakeUpPin = 3;
combinedWake ();
}  // end of wake_B

Then in loop, after you wake:

if (wakeUpPin == 2)
  doSomething ();
else if (wakeUpPin == 3)
  doAnotherThing ();

Thanks Nick! I can kind of follow that but I can't get the code working, D12 is always flashing? Not sure what I am doing wrong?

Thanks

#include <avr/sleep.h>

const byte LEDA = 13;
const byte LEDB = 12;
  
volatile byte wakeUpPin;

void combinedWake ()
{
  // cancel sleep as a precaution
  sleep_disable();
  // must do this as the pin will probably stay low for a while
  detachInterrupt (0);
}  // end of combinedWake

void wake_A ()
{
wakeUpPin = 2;
combinedWake ();
}  // end of wake_A

void wake_B ()
{
wakeUpPin = 3;
combinedWake ();
}  // end of wake_B

void setup () 
  {
  digitalWrite (2, HIGH);  // enable pull-up
  }  // end of setup

void loop () 
{
  if (wakeUpPin == 2)
  {
  pinMode (LEDA, OUTPUT);
  digitalWrite (LEDA, HIGH);
  delay (50);
  digitalWrite (LEDA, LOW);
  delay (50);
  pinMode (LEDA, INPUT);
  }
  
else if (wakeUpPin == 3)
{
  pinMode (LEDB, OUTPUT);
  digitalWrite (LEDB, HIGH);
  delay (50);
  digitalWrite (LEDB, LOW);
  delay (50);
  pinMode (LEDB, INPUT);
}
 

  // disable ADC
  ADCSRA = 0;  
  
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);  
  sleep_enable();

  // Do not interrupt before we go to sleep, or the
  // ISR will detach interrupts and we won't wake.
  noInterrupts ();
  
// will be called when pin D2 goes low  
  attachInterrupt (0, wake_A, LOW);
// will be called when pin D3 goes low  
  attachInterrupt (1, wake_B, LOW);
 
 
 
 
  // turn off brown-out enable in software
  // BODS must be set to one and BODSE must be set to zero within four clock cycles
  MCUCR = bit (BODS) | bit (BODSE);
  // The BODS bit is automatically cleared after three clock cycles
  MCUCR = bit (BODS); 
  
  // We are guaranteed that the sleep_cpu call will be done
  // as the processor executes the next instruction after
  // interrupts are turned on.
  interrupts ();  // one cycle
  sleep_cpu ();   // one cycle


  } // end of loop
void setup () 
  {
  digitalWrite (2, HIGH);  // enable pull-up
  }  // end of setup

You need another pull-up? (pin 3)

Thanks, I never seem to stop the simple oversights!

It seems to be working now, but I'm getting strange behavior - the LED on D13 - D2 triggered is flashing at the expected rate, but the LED on D12 - D3 triggered is flashing at around twice the expected rate?

Yes, well I overlooked that the detachInterrupt had to be different. So we get rid of the shared function and just have two wake functions:

void wake_A ()
{
  wakeUpPin = 2;
  // must do this as the pin will probably stay low for a while
  detachInterrupt (0);
  // cancel sleep as a precaution
  sleep_disable();
}  // end of wake_A

void wake_B ()
{
  wakeUpPin = 3;
  // must do this as the pin will probably stay low for a while
  detachInterrupt (1);
  // cancel sleep as a precaution
  sleep_disable();
}  // end of wake_B

I just tested that and it looked OK.

Thanks Nick. I had completely overlooked the possibility of two wakeup routines. Talk about overcomplicating things :slight_smile:

many thanks Nick, works like a charm now :slight_smile:

Amazing how small the current is, and how it wakes so quickly

Now to try and add some code to expand my project :slight_smile:

I'm now trying to integrate the code with some code i have had working to send a IR signal.

Sometimes the led stays high, I guess it could be due to the button press being to long? The orignal code used a serial command so only would run one, I tried adding a delay of 200 after signal is sent but didn't help.

Not sure if the new code is messing with the interrupts as it seems to disable then re-enable them?

Thanks

#include <avr/sleep.h>

//const byte LEDA = 13;
const byte LEDB = 12;

#define IRledPin 13
#define NumIRsignals 76

// This is the code I determined works for my Duraflame heater
int IRsignal[] = {
  // ON, OFF (in 10's of microseconds)
  884, 436,
  58, 52,
  58, 162,
  58, 50,
  58, 162,
  58, 162,
  56, 162,
  58, 162,
  58, 52,
  56, 162,
  58, 52,
  58, 160,
  58, 52,
  58, 52,
  58, 50,
  58, 52,
  58, 162,
  58, 160,
  58, 162,
  58, 162,
  56, 162,
  58, 162,
  58, 50,
  60, 50,
  58, 52,
  58, 52,
  58, 50,
  58, 52,
  58, 52,
  56, 52,
  58, 162,
  58, 160,
  58, 162,
  58, 3900,
  882, 216,
  58, 2844,
  882, 216,
  58, 0};



volatile byte wakeUpPin;

void wake_A ()
{
  wakeUpPin = 2;
  // must do this as the pin will probably stay low for a while
  detachInterrupt (0);
  // cancel sleep as a precaution
  sleep_disable();
}  // end of wake_A

void wake_B ()
{
  wakeUpPin = 3;
  // must do this as the pin will probably stay low for a while
  detachInterrupt (1);
  // cancel sleep as a precaution
  sleep_disable();
}  // end of wake_B

void setup () 
{
  pinMode(IRledPin, OUTPUT);
  digitalWrite(IRledPin, LOW);   //Make sure LED starts "off"
  Serial.begin(9600);            //Initialize Serial port
  
  
  digitalWrite (2, HIGH);  // enable pull-up
  digitalWrite (3, HIGH);  // enable pull-up
}  // end of setup

void loop () 
{
  if (wakeUpPin == 2)
  {
    for (int i = 0; i < NumIRsignals; i+=2) {         //Loop through all of the IR timings
      pulseIR(IRsignal[i]*10);              //Flash IR LED at 38khz for the right amount of time
      delayMicroseconds(IRsignal[i+1]*10);  //Then turn it off for the right amount of time
    }
  }

  else if (wakeUpPin == 3)
  {
    pinMode (LEDB, OUTPUT);
    digitalWrite (LEDB, HIGH);
    delay (100);
    digitalWrite (LEDB, LOW);
    delay (100);
    pinMode (LEDB, INPUT);
  }


  // disable ADC
  ADCSRA = 0;  

  set_sleep_mode (SLEEP_MODE_PWR_DOWN);  
  sleep_enable();

  // Do not interrupt before we go to sleep, or the
  // ISR will detach interrupts and we won't wake.
  noInterrupts ();

  // will be called when pin D2 goes low  
  attachInterrupt (0, wake_A, LOW);
  // will be called when pin D3 goes low  
  attachInterrupt (1, wake_B, LOW);




  // turn off brown-out enable in software
  // BODS must be set to one and BODSE must be set to zero within four clock cycles
  MCUCR = bit (BODS) | bit (BODSE);
  // The BODS bit is automatically cleared after three clock cycles
  MCUCR = bit (BODS); 

  // We are guaranteed that the sleep_cpu call will be done
  // as the processor executes the next instruction after
  // interrupts are turned on.
  interrupts ();  // one cycle
  sleep_cpu ();   // one cycle


} // end of loop


// This function allows us to PWM the IR LED at about 38khz for the sensor
void pulseIR(long microsecs) {
  // we'll count down from the number of microseconds we are told to wait
 
  cli();  // this turns off any background interrupts
 
  while (microsecs > 0) {
    // 38 kHz is about 13 microseconds high and 13 microseconds low
   digitalWrite(IRledPin, HIGH);  // this takes about 3 microseconds to happen
   delayMicroseconds(10);         // hang out for 10 microseconds, you can also change this to 9 if its not working
   digitalWrite(IRledPin, LOW);   // this also takes about 3 microseconds
   delayMicroseconds(10);         // hang out for 10 microseconds, you can also change this to 9 if its not working
 
   // so 26 microseconds altogether
   microsecs -= 26;
  }
 
  sei();  // this turns them back on
}

Sometimes the led stays high, I guess it could be due to the button press being to long?

Debounce it, and/or wait until the button is released before doing anything.

Thanks Nick. I added a while loop after sending the signals to stall the code while the button is still pressed - works great now.

Thanks again for all your work