ISR and attachInterrupt statement to toggle power

Hi All,

New to interrupts and wanted to know if the code below is "workable".

I want to have 3 buttons monitored so that a button press on any one would power-up my xbee module.
So really all 3 buttons can trigger the same interrupt routine and it's OK if one button press cancels all the ISR until it's time to sleep the Xbee and Arduino again.
This would be run on an Arduino Micro since it has more available interrupts than a Uno or Nano.

So can I put 3 attachInterrupt and detachInterrupt statements in the same ISR code ??

void sleepNow()         
{
  digitalWrite(Xbee_Pwr_Toggle, LOW);
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);  
  sleep_enable();
  attachInterrupt(2, wakeUp, LOW);
  attachInterrupt(3, wakeUp, LOW);
  attachInterrupt(7, wakeUp, LOW);
  sleep_mode();
}


void wakeUp()
{
  sleep_disable();
  detachInterrupt(2);
  detachInterrupt(3);
  detachInterrupt(7);
  digitalWrite(Xbee_Pwr_Toggle, HIGH);
}

dan_movie:
So can I put 3 attachInterrupt and detachInterrupt statements in the same ISR code ??

Yes.

Thank you.

UPDATED code with 3 ISR routines…

Need help with code - when using 3 attachInterrupt statements, one of them does not seem to fire.

The idea is to use interrupts to wake the Arduino Micro & the XBee board to transmit a character to 4 receiver units. They will activate relays that ring a bell or turn on/off lights.
Both light buttons (on & off) are working as expected. Button is pressed, “system” wakes up, code is sent and response is received. The bell button does not trigger the ISR routine. Yet when I tried it alone on a Nano board it worked.

Since I need to monitor when the bell button is pressed to ring it but also when it’s released to stop the noise, I used a CHANGE isr.

Would appreciate help / ideas on why is not working or what I did wrong here.

Thanks,
Dan

#include <avr/sleep.h>


// Declare pin numbers for feedback LEDs 1 thru 4
const int LED1_pin = 10;
const int LED2_pin = 16;
const int LED3_pin = 14;
const int LED4_pin = 15;

// Declare pin numbers for bell and light buttons and power
const int BTN_Bell = 7;
const int BTN_Light_ON = 2;
const int BTN_Light_OFF = 3;
const int Xbee_Pwr_Toggle = 19; 


int BTN_Bell_state = 1;    
int previousBTN_Bell_state = 0;    
unsigned long previousMillis = 0;
const long AwakeDuration = 3000;
volatile int Pressed_BTN_num = 0;

void setup()
{
  // all unused pins to output low
  // pins are not consecutive numbers, I want pin# 4,5,6,8,9,10,14,15,16,18,19,20,21
  for (int i = 4; i <= 6; i++) {
    pinMode(i, OUTPUT);
    digitalWrite(i, LOW);  
  }
  for (int i = 8; i <= 10; i++) {
    pinMode(i, OUTPUT);
    digitalWrite(i, LOW);  
  }
  for (int i = 14; i <= 16; i++) {
    pinMode(i, OUTPUT);
    digitalWrite(i, LOW);  
  }
  for (int i = 18; i <= 21; i++) {
    pinMode(i, OUTPUT);
    digitalWrite(i, LOW);  
  }
  
  pinMode(LED1_pin, OUTPUT);
  pinMode(LED2_pin, OUTPUT);
  pinMode(LED3_pin, OUTPUT);
  pinMode(LED4_pin, OUTPUT);
  pinMode(Xbee_Pwr_Toggle, OUTPUT);
  pinMode(BTN_Bell, INPUT_PULLUP);       
  pinMode(BTN_Light_ON, INPUT_PULLUP);
  pinMode(BTN_Light_OFF, INPUT_PULLUP);

  
// Open Serial com ports to send data via Xbee board,
   Serial1.begin(9600); 
}


void sleepNow()
{
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  sleep_enable();
  attachInterrupt(digitalPinToInterrupt(BTN_Bell), wakeUpBell, CHANGE);
  attachInterrupt(digitalPinToInterrupt(BTN_Light_ON), wakeUpLightON, LOW);
  attachInterrupt(digitalPinToInterrupt(BTN_Light_OFF), wakeUpLightOFF, LOW);
  // disable ADC
  ADCSRA = 0;  
  sleep_mode();
}


void wakeUpBell()
{
  sleep_disable();
  if (digitalRead(BTN_Bell) == LOW){
      Pressed_BTN_num = 3;
  }
  else { 
      Pressed_BTN_num = 4; 
  }
  digitalWrite(Xbee_Pwr_Toggle, HIGH); 
}

void wakeUpLightON()
{
  sleep_disable();
  Pressed_BTN_num = 2;   
  digitalWrite(Xbee_Pwr_Toggle, HIGH);
}

void wakeUpLightOFF()
{
  sleep_disable();
  Pressed_BTN_num = 1;   
  digitalWrite(Xbee_Pwr_Toggle, HIGH);
}


void loop()
{
   
  // button send portion to remote units
  // on button press turn remote light (relay) on or off or ring bell
  switch (Pressed_BTN_num) {    
          case 1:                                 // Light OFF btn pressed  
              Serial1.write('Z');                  
              delay(50);                          // long delay to avoid sending multiple presses
              Pressed_BTN_num = 0;
              break;
          case 2:                                 // Light ON btn pressed  
              Serial1.write('L');            
              delay(50);                          // long delay to avoid sending multiple presses
              Pressed_BTN_num = 0;
              break;
          case 3:                                 // Bell btn is pressed  
              Serial1.println('B');             
              delay(50);                          // long delay to avoid sending multiple presses
              Pressed_BTN_num = 0;
              break;       
           case 4:                                 // Bell btn is released 
              Serial1.println('O');                   
              delay(50);                          // long delay to avoid sending multiples
              Pressed_BTN_num = 0;
              break;   
           default:
              unsigned long currentMillis = millis();
              if (Pressed_BTN_num == 0 && currentMillis - previousMillis > AwakeDuration) {
                   digitalWrite(Xbee_Pwr_Toggle, LOW);
                   sleepNow();
                   delay(25);                          // time to wake system & Xbee
                   currentMillis = millis();           // reset time loop now that all is awake
                   previousMillis = currentMillis;
              }
              break; 
  }

  // feedback section from remote units - who got the message ?
  // turn corresponding LED on or off to know that remote light is on or off
  if (Serial1.available() > 0) { 
      char Msg = Serial1.read();
      switch (Msg) {
        case '1':                         // If received '1' station 1 at OFF
          digitalWrite(LED1_pin, LOW);
          break;
        case '2':                         // If received '2' station 2 at OFF
          digitalWrite(LED2_pin, LOW);
          break;
        case '3':                         // If received '3' station 3 at OFF
          digitalWrite(LED3_pin, LOW);
          break;
        case '4':                         // If received '4' station 4 at OFF
          digitalWrite(LED4_pin, LOW);
          break;
        case '5':                         // If received '5' station 1 at ON
          digitalWrite(LED1_pin, HIGH);
          break;
        case '6':                         // If received '6' station 2 at ON
          digitalWrite(LED2_pin, HIGH);
          break;
        case '7':                         // If received '7' station 3 at ON
          digitalWrite(LED3_pin, HIGH);
          break;  
        case '8':                         // If received '8' station 4 at ON
          digitalWrite(LED4_pin, HIGH);
          break;  
      }
  }   
}

I'm not certain wake-up is available on pin 7.

See Arduino Playground - HomePage

Blackfin:
I'm not certain wake-up is available on pin 7.

See Arduino Playground - HomePage

I am. Pin 7 is not an external interrupt pin.

The ATMega328 equipped Arduinos only have 2 external interrupt pins - 2 and 3.

OP, you should be able to use a single wake-up pin with all of those switches if you're willing to add three small Schottky diodes to your circuit. Can you change the input pins for each, say to match the image?

Thanks for the feedback.

I am using an Arduino Pro Micro - so based on the 32U4 chip which has more interrupts.

Never tried the diodes, can you suggest a diode number / reference ?
So with this I would use one wake-up, detach the interrupt and then in the loop, read each pin and send what I need to via the XBee radio.

my last 2 options are...

1- use double pole pushbutton - connect one side to the input pin and the other side to a common interrupt pin but I was not able to find small pushbuttons.

2- use to miniature buttons next to each other and a large button cap to push down on both buttons at same time - again one connected to input pin the other to interrupt pin.

But I first wanted to understand why my current code does not work.
I figure every TV remote out there must sleep its processor and wake-up on a button press, if not I would be changing batteries in my TV remote every week or so.

@dan_movie

What does this sketch do?

byte myOutputPins[] = {4, 5, 6, 8, 9, 10, 14, 15, 16, 18, 19, 20, 21};

void setup()
{
  for (byte x = 0; x < sizeof(myOutputPins); x++)
  {
    pinMode(myOutputPins[x], OUTPUT);
    digitalWrite(myOutputPins[x], LOW);
  }
}

void loop()
{

}

dan_movie:
I figure every TV remote out there must...

...use pin change interrupts instead of external interrupts.

@larryD ... i read that when putting the processor to sleep you get "better power saving results" by not having any unused pins in a floating state. So the idea was to make all unused pins be "LOW".
see link : Gammon Forum : Electronics : Microprocessors : Power saving techniques for microprocessors

there is a section about : Configuring pins as inputs/outputs it shows the power consumption of the various pins - output / input, low / high.

@coding bradly ... i thought the only way to wake a sleeping arduino was by an external interrupt or watchdog timer. I will do more research on pin change interrupts. If you have any pointers, I would appreciate it. Is it one interrupt that monitors all 3 buttons ?

My point in the small sketch I posted was to show you a more efficient way of setting a range of pins with ‘pinMode’, nothing else.

Take a close look at the sketch in post #8 which uses an array. :wink:

dan_movie:
Is it one interrupt that monitors all 3 buttons ?

One interrupt per port.

dan_movie:
Thanks for the feedback.

I am using an Arduino Pro Micro - so based on the 32U4 chip which has more interrupts.
https://cdn.sparkfun.com/datasheets/Dev/Arduino/Boards/ProMicro16MHzv1.pdf

Never tried the diodes, can you suggest a diode number / reference ?

Something along the lines of a BAT83S would be easy to prototype with:

https://www.digikey.ca/product-detail/en/vishay-semiconductor-diodes-division/BAT83S-TR/BAT83S-TRGICT-ND/8564711

Larry, sorry I missed the code in your post, much simpler ! thanks.

Thanks for the diode info and the interrupt info.

Hi All,

I just came across this on page 88 on the 32U4 datasheet.
I am not so good with electronics and all the technical info on datasheets but ... does the info below point to why my change interrupt on pin 7 is not working...it needs the clock to be running ??

From page 88....

The External Interrupts can be triggered by a falling or rising edge or a low level. This is set up as indicated in
the specification for the External Interrupt Control Registers – EICRA (INT3:0) and EICRB (INT6). When the
external interrupt is enabled and is configured as level triggered, the interrupt will trigger as long as the pin is
held low. Note that recognition of falling or rising edge interrupts on INT6 requires the presence of an I/O clock,
described in “System Clock and Clock Options” on page 27. Low level interrupts and the edge interrupt on
INT3:0 are detected asynchronously. This implies that these interrupts can be used for waking the part also
from sleep modes other than Idle mode. The I/O clock is halted in all sleep modes except Idle mode.

The datasheet is (very likely) incorrect...

I believe @JChristensen contacted Atmel and they confirmed that external interrupts do NOT require a clock for ATmega processors.

A bit more information...

Ok thanks for this last response.

Could you give this link a quick read and tell me if any of it is still accurate in 2019 ??

https://forum.sparkfun.com/viewtopic.php?t=35847

Something about how the Arduino IDE does not “compile” correctly the Int6 (digital pin 7) of the 32U4.

I am wondering if this is my issue with the interrupt not firing on that pin.

I am in trouble if I need to code it directly with the “AVR commands” cause I don’t understand how they work (too much code, not enough short instructions that mean something).
I can understand : attachInterrupt(digitalPinToInterrupt(BTN_Bell), wakeUpBell, CHANGE);
but not EICRB |= (1<<ISC60)|(1<<ISC61);

thanks for the help.

Support for interrupt numbers 0 through 4…

Digital pin 7 is mapped to interrupt 4…

This will silently fail. There is no interrupt 7...

dan_movie:

...

attachInterrupt(7, wakeUp, LOW);
...

dan_movie:
Something about how the Arduino IDE does not "compile" correctly the Int6 (digital pin 7) of the 32U4.

Digital pin 7 is mapped to interrupt 4 (see the post above). If you use digitalPinToInterrupt you will not have to know that.