Preventing interrupts creating multiply inputs.

HI,

How can i prevent an interrupt from a button causing multiply inputs. At the moment i am just using this for learning, but as you can see the idea is that the one button would control the brightness and one the timer.

I know i can just use the “digital read” for this, however in the final version of this I expect the device to be asleep 99% of the time, battery powered and that the button press will wake up the device, the light will come on for a short amount of time, and then the device will go back to sleep.

I have tried a few different methods. One i was thinking of was having any button press wake up the device, disable interrupts, check what button was pressed, run the loop, and then go back to sleep.

at the moment a single press of the button (micro switch) give this output.

As i say I know in this case there are other ways to do this, but i am just learning about interrupts and would like to know how to correctly manage a button press during sleep mode.

Thank you

Aaron

New Second
New Second
Bright - 4
Bright - 5
Bright - 0
New Second
New Second
#include <Adafruit_NeoPixel.h>
#include   <avr/sleep.h>

//set up the pins for neopixel strip
#define PINP 6  // input pin Neopixel is attached to
#define NUMPIXELS 6 // number of neopixels in strip
// Create the Neopixel Object
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PINP, NEO_GRB + NEO_KHZ800);

//set up intrupt pins
const byte interruptPinT = 2;
const byte interruptPinB = 3;

volatile byte bright = 0;
volatile byte timer = 0;

//this is used for the debugging of the timer intrupt led flash
boolean toggle1 = 0;

int redColor = 10;
int greenColor = 10;
int blueColor = 10;

void setup() {
  Serial.begin(9600);
  // Initialize the NeoPixel library.
  pixels.begin();
  
  pinMode(interruptPinT, INPUT_PULLUP);
  pinMode(interruptPinB, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPinT), intimer, CHANGE);
  attachInterrupt(digitalPinToInterrupt(interruptPinB), intbright, CHANGE);
  
  //set timer1 interrupt at 1Hz
  TCCR1A = 0;// set entire TCCR1A register to 0
  TCCR1B = 0;// same for TCCR1B
  TCNT1  = 0;//initialize counter value to 0
  // set compare match register for 1hz increments
  OCR1A = 15624;// = (16*10^6) / (1*1024) - 1 (must be <65536)
  // turn on CTC mode
  TCCR1B |= (1 << WGM12);
  // Set CS10 and CS12 bits for 1024 prescaler
  TCCR1B |= (1 << CS12) | (1 << CS10);  
  // enable timer compare interrupt
  TIMSK1 |= (1 << OCIE1A);

  //allow interrupts during sleep
  sei();//allow interrupts
  
}

void loop() {
  setColor();

  for (int i=0; i < NUMPIXELS; i++) {
    // pixels.Color takes RGB values, from 0,0,0 up to 255,255,255
    pixels.setPixelColor(i, pixels.Color(redColor, greenColor, blueColor));

    // This sends the updated pixel color to the hardware.
    pixels.show();

    sleep_enable();
    //set_sleep_mode(); ignoring this and just default to idle, leaves the timers running so can be used and an intrupt. 
    sleep_mode();
    
  }
  
 
  
}

// setColor()
// picks random values to set for RGB
void setColor(){
  redColor = (bright * 20);
  greenColor = (bright * 20);
  blueColor = (bright * 20);
}

void intimer() {
  detachInterrupt(digitalPinToInterrupt(interruptPinT));
  timer = timer+1;
  if (timer > 5)
  timer = 0;
  delay(250);
  Serial.print("Timer - ");
  Serial.println(timer);
  attachInterrupt(digitalPinToInterrupt(interruptPinT), intimer, FALLING);
}

void intbright() {
  detachInterrupt(digitalPinToInterrupt(interruptPinB));
  bright = bright+1;
  if (bright > 5)
  bright = 0;
  delay(250);
  Serial.print("Bright - ");
  Serial.println(bright);
  attachInterrupt(digitalPinToInterrupt(interruptPinB), intbright, FALLING);
}

ISR(TIMER1_COMPA_vect){//timer1 interrupt 1Hz toggles pin 13 (LED)
//generates pulse wave of frequency 1Hz/2 = 0.5kHz (takes two cycles for full wave- toggle high then toggle low)
  Serial.println("New Second");
  if (toggle1){
    digitalWrite(13,HIGH);
    toggle1 = 0;
  }
  else{
    digitalWrite(13,LOW);
    toggle1 = 1;
  }
}

First of all you should never delay or serial print inside of an ISR. Secondly a mechanical switch bounces so you should expect more than one interrupt. Mechanical switches should be debounced.

I would design the program so the first interrupt wakes the Arduino and all the rest of the button code is handled with polling and digitalRead()s

That way it doesn't matter if the interrupt detects several inputs - the first one will be the only one that matters,

...R

ToddL1962:
First of all you should never delay or serial print inside of an ISR. Secondly a mechanical switch bounces so you should expect more than one interrupt. Mechanical switches should be debounced.

The delay and serial print where just there for debugging, I have played around with the code a bit.

When you say “dbounce” you mean using capacitors in the circuit correct? I wanted to do the debounce in software, so the idea was to disable interrupt on the first “press” and then just as @Robn2 suggests use the standard digital reads.

Why when the first command of IRS is to “detach the pin for interrupts”, it still interrupts multiple times.

DevilWAH:
Why when the first command of IRS is to "detach the pin for interrupts", it still interrupts multiple times.

I suspect that is because you don't clear the interrupt flag before re-attaching the interrupt. On an Uno this line will clear the flags for both interrupts

EIFR = 0b00000011;

Also, it might be better to move the attachInterrupt() out of the ISR and put it in your main code so it is not re-attached so soon.

And you should not have print statements in an ISR even for testing.

...R

OK so i tidied this up a bit, as per some of the suggestions,

I am sure there are neater ways to do it but please forgive novice coder. Now the interrupt wakes the device and the main loop checks what was pressed and deals with it.

but my issues now is that the timer is not waking up the device (power down monde) or it does not stay asleep for 1 second as expected from timer1 if i use IDLE status.

I guess you can see what i am attempting, and this is just trying to get my head around sleep, timers and interrupts. I am guessing I need to define more what can and can’t wake it from sleep. but i am not sure how. I want only the timer1 overflow and pins 2 + 3 to wake it up. with the timer giving me a 1 second (ish) loop timing and the buttons giving me user input. The buttons work fine now.

Any thoughts how to get the sleep and timer interrupting working?

Thank you

Aaron

#include <Adafruit_NeoPixel.h>
#include   <avr/sleep.h>

//set up the pins for neopixel strip
#define PINP 6  // input pin Neopixel is attached to
#define NUMPIXELS 6 // number of neopixels in strip
// Create the Neopixel Object
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PINP, NEO_GRB + NEO_KHZ800);

//set up intrupt pins
const byte interruptPinT = 2;
const byte interruptPinB = 3;

int bright = 0;
int countt = 0;
int countdown = 0;

//this is used for the debugging of the timer intrupt led flash
boolean toggle1 = 0;

int redColor = 0;
int greenColor = 0;
int blueColor = 0;

void setup() {
  Serial.begin(9600);
  // Initialize the NeoPixel library.
  pixels.begin();
  
  pinMode(interruptPinT, INPUT_PULLUP);
  pinMode(interruptPinB, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(interruptPinT), buttonwake, LOW);
  attachInterrupt(digitalPinToInterrupt(interruptPinB), buttonwake, LOW);
  
  //set timer1 interrupt at 1Hz
  TCCR1A = 0;// set entire TCCR1A register to 0
  TCCR1B = 0;// same for TCCR1B
  TCNT1  = 0;//initialize counter value to 0
  // set compare match register for 1hz increments
  OCR1A = 15624;// = (16*10^6) / (1*1024) - 1 (must be <65536)
  // turn on CTC mode
  TCCR1B |= (1 << WGM12);
  // Set CS10 and CS12 bits for 1024 prescaler
  TCCR1B |= (1 << CS12) | (1 << CS10);  
  // enable timer compare interrupt
  TIMSK1 |= (1 << OCIE1A);

  //allow interrupts during sleep
  sei();//allow interrupts
  
}

void loop() {
   
   //delay(20);

   if (digitalRead(interruptPinB) == LOW)
      {
        bright = bright+1;
        if (bright > 5)
        bright = 0;
        while (digitalRead(interruptPinB) == LOW){};
      }

   // if the timer button is pressed set the countdwon value to set * 600 seconds (10 miniute steps)  
   if (digitalRead(interruptPinT) == LOW)
      {
        countt = countt+1;
        if (countt > 5)
        countt = 0;
        countdown = countt * 600;
        while (digitalRead(interruptPinT) == LOW){};
      }

    //each loop cont down by one second
    //countdown = countdown - 1;
      
    Serial.print(digitalRead(interruptPinB));
    Serial.print(digitalRead(interruptPinT));
    Serial.println(bright);
      setColor();

  for (int i=0; i < NUMPIXELS; i++) {
    // pixels.Color takes RGB values, from 0,0,0 up to 255,255,255
    pixels.setPixelColor(i, pixels.Color(redColor, greenColor, blueColor));

    // This sends the updated pixel color to the hardware.
    pixels.show();
  }

    attachInterrupt(digitalPinToInterrupt(interruptPinT), buttonwake, LOW);
    attachInterrupt(digitalPinToInterrupt(interruptPinB), buttonwake, LOW);

    Going_To_Sleep();
}

void Going_To_Sleep(){
    sleep_enable();//Enabling sleep mode
    set_sleep_mode(SLEEP_MODE_IDLE);//Setting the sleep mode, in our case full sleep
    sleep_cpu();//activating sleep mode
  }

// setColor()
// picks random values to set for RGB
void setColor(){
  redColor = (bright * 20);
  greenColor = (bright * 20);
  blueColor = (bright * 20);
}

void buttonwake() {
  detachInterrupt(digitalPinToInterrupt(interruptPinT));
  detachInterrupt(digitalPinToInterrupt(interruptPinB));
  EIFR = 0b00000011;
  sleep_disable();
}


ISR(TIMER1_COMPA_vect){//timer1 interrupt 1Hz toggles pin 13 (LED)
  sleep_disable();
//generates pulse wave of frequency 1Hz/2 = 0.5kHz (takes two cycles for full wave- toggle high then toggle low)
//  if (toggle1){
//    digitalWrite(13,HIGH);
//    toggle1 = 0;
//  }
//  else{
//    digitalWrite(13,LOW);
//    toggle1 = 1;
//  }
}