debouncing + interrupt ?

Hi, im quite new to arduino, and im having a bit of a problem in implementing a debounced interrupt.
So i have 4 leds, which are programmed to light based on a pattern i coded into an array. I have to "pause" the pattern using the pushbutton. However, when i release the button, the result is not always what i expect it to be. Like it jumps to the next pattern, or resets the cycle.

Do you guys have any suggestions for this? I have my attached my code so you can comment on it. Thanks. :slight_smile:

const int buttonPin = 2;     
int led = 13;
int led0 = 12; 
int led1 = 11;
int led2 = 10;
//               0,1,2,3,4,5,6,7,8,9,10
int ledArr [] = {0,0,0,0,1,1,1,1,1,1,1};
int ledArr0[] = {0,0,0,1,0,0,0,1,1,1,1};
int ledArr1[] = {0,0,1,0,0,0,1,0,0,1,1};
int ledArr2[] = {0,1,0,0,0,1,0,0,1,0,1};

volatile int  i;
int j = 0;
volatile int buttonState = 0;             
int lastButtonState = LOW;   


long lastDebounceTime = 0;  
long debounceDelay = 40;    


void setup() {
  Serial.begin(9600);
  pinMode(buttonPin, INPUT);
  pinMode(led, OUTPUT); 
  pinMode(led0, OUTPUT);
  pinMode(led1, OUTPUT);
  pinMode(led2, OUTPUT);
  digitalWrite(buttonPin, LOW);
  attachInterrupt(0, doNothing, RISING);

}

void loop() {
  ledPattern();
  
  
}


void debounceThis(){
  int reading = digitalRead(buttonPin);
  if (reading != lastButtonState) {
   
    lastDebounceTime = millis();
  } 
  
  if ((millis() - lastDebounceTime) > debounceDelay) {   
    buttonState = reading;
  } 
  lastButtonState = reading;

}

void ledPattern(){
    for (i = 0; i < 11; i++){ 
      debounceThis();
      if(buttonState == 0){
      delay(400);
      digitalWrite(led2, ledArr[i]);
      digitalWrite(led1, ledArr0[i]);
      digitalWrite(led0, ledArr1[i]);
      digitalWrite(led, ledArr2[i]);
         
      Serial.println(i);  
     }
  }
  
  
}

void doNothing(){
 buttonState != buttonState;
 Serial.print("i during ISR: ");
 Serial.println(i);
 
 
}

EXER2.ino (1.41 KB)

Serial.print in an ISR ?

NO !

I'm really sorry for that. I was trying to see at what part of the array it was when i push the button, so there. I'm completely new at this. :slight_smile:

This is a simple debounce, using millis(), http://arduino.cc/en/Tutorial/Debounce

Why are the arrays ledArr, ledArr0, etc. int, when they only hold 0 or 1. Perhaps you could make them long and waste twice as much space.

This is a simple debounce, using millis(), http://arduino.cc/en/Tutorial/Debounce

That's the one I used for this..

Why are the arrays ledArr, ledArr0, etc. int, when they only hold 0 or 1. Perhaps you could make them long and waste twice as much space.

I don't know what you meant by that, but is there a way I could make it more efficient? What I would like to do with it is to pause the pattern when i hold the button, and resume to where it left before it responded to the interrupt. Thanks. :slight_smile:

You should always use the smallest size variable that can hold the data that you need to hold. All your values are either 0 or 1. If you look at the reference page, you can see a list of types. Look at each one, and see what range of values that type can hold. Then, determine if there are smaller types that can hold the value, or not. If so, use the smaller type. The types that can hold integer values are byte, int, long, and long long. Which ones can hold the values that you have? What are the sizes? Which is the smallest? Which is, therefore, the most appropriate?

I will keep that in mind sir. Thank you. :slight_smile:

Do you have any suggestions for my problem? :slight_smile:
I'm still at it, but unfortunately, I can't figure this one out. Is the problem with the array? Should I find another way to code the pattern?

You are polling the switch, to determine if it is bouncing. Why do you also need an interrupt? The interrupt is NOT being debounced.

Why is the interrupt service routine called doNothing()? If it really did nothing, there would be no point in calling it.

Okay, so i thought by interrupting the for loop, I would be able to pause it, because that's what I think interrupting does. Am I right? What I mean is, at whatever 'i' in the for loop is at, whenever I hold the button, it would pause incrementing, and instead do whatever is in the function called in the attachInterrupt(), and then resume at whatever value it left off. So.. you're saying that i do not need to debounce the button? I'm a bit confused.

I tried to compare the value of i during the loop and when I hold the button. As I pressed on it, the value would still increment by at least 2, and then stop. And when I release it, the LED pattern is not what it should be.

Oh, and I named the function doNothing() because it really does nothing but to hold the state at which the LEDs are at when I hold the button. At least that's what I intend it to do. Yeah, I think I should rename that. Thanks again. :slight_smile:

Okay, so i thought by interrupting the for loop, I would be able to pause it, because that's what I think interrupting does. Am I right?

No. You are in the middle of cooking dinner, stirring the gravy (that's a for loop). The phone rings (that's an interrupt). What do you do when you end the phone call? Have a beer and a cigar? No, you return to stirring the gravy (hoping it hasn't burned and lumped).

So.. is there any way I could turn off the stove and turn it on again once i finish the call? :slight_smile:

So.. is there any way I could turn off the stove and turn it on again once i finish the call?

Of course, though that really is not necessary. Each pass through the loop in ledControl() you could listen for the phone to ring (the switch was pressed). If the phone is ringing, return (to loop()), turning off the stove (leds) as needed.

There really is no need for an interrupt in this program.

There really is no need for an interrupt in this program.

Our professor requires it though. :expressionless:

And in the next part of this exercise, I should be able to reverse the pattern (using a dip switch) and also pause it.

I think I should try a new approach for this. I'll post my code once I get it to work properly. :slight_smile:

Thanks for the answers!

amd:
Our professor requires it though. :expressionless:

Well, in that case ... :slight_smile:

In the interrupt routine call micros or millis to see if enough time has elapsed since the last interrupt to amount to a debounce. If not, ignore it.

eg.

interrupted: note time
interrupted again: if less than (say) 10 mS has elapsed, ignore it (debounce)
interrupted again: more than 10 mS has elapsed, process it (and note time again)

I finally got it to work! YAY!

As PaulS said, it could be done without using interrupt, however, it was required for this exercise. So I made the debounce() function as my ISR (as suggested by Nick). It worked as it should! And then I tried to remove the attachInterrupt() function, and it also worked (without debouncing).

I just declared another variable, j that would take in the value of i once the button is held. And use this value in the for loop, when the button is released. :slight_smile:

I'll post my code here, so you guys can suggest other ways to do it.

THANKS!

I'm gonna make a robot now. LOL! :slight_smile:

void setup() {
  pinMode(buttonPin, INPUT);
  pinMode(led, OUTPUT); 
  pinMode(led0, OUTPUT);
  pinMode(led1, OUTPUT);
  pinMode(led2, OUTPUT);
  attachInterrupt(0, debounce, CHANGE);
}

void loop() {
  ledPattern();  
}

void debounce(){
  int reading = digitalRead(buttonPin);
  if (reading != lastButtonState) {
    lastDebounceTime = millis();
  }
  if ((millis() - lastDebounceTime) > debounceDelay) {    
    buttonState = reading;
  }
  lastButtonState = reading;
}

void ledPattern(){  
  for (i = 0; i < 11; i++){ 
  buttonState = digitalRead(buttonPin);  
    if(buttonState == LOW){   
        digitalWrite(led2, ledArr[i]);
        digitalWrite(led1, ledArr0[i]);
        digitalWrite(led0, ledArr1[i]);
        digitalWrite(led, ledArr2[i]);
        delay(400);
        j++;
        if (i == 0){
          j = 0;
        }
     }  
     else{
       i = j;
     }
  } 
}

Sorry for taking over the thread, but the subject corresponds to my issue... I am trying to create something like "typematic rate" thing for infrared remote, but my delay for the second trigger gets ignored... If I press a button on the remote, I get light turned on and off a lot of times :frowning:
This is my interrupt routine:

void getIR() {
detachInterrupt(0);                                                 // Not sure if I need to do this, but detaching, just to be sure, we don't get interrupted while in this routine
interrupt_time = millis();
 int tempKey = getIRKey();                                      // Here we get the key number
  if ((interrupt_time - last_interrupt_time) < 1000){
 presscount++;                                                       // counting number of times we get the button press
  } else {
    presscount = 0;                                                  // previous button press was more than a second age, so resetting the counter
  }
 if (presscount == 1) delay(3000);                             // this should be done if we get the second command within one second

 ... different things for every button here ..

  last_interrupt_time = interrupt_time;                      // saving the last interrupt time
  attachInterrupt(0, getIR, LOW);                             // re-attach interrupt 0
}
void getIR() {
...
 if (presscount == 1) delay(3000);                             // this should be done if we get the second command within one second
...
}

Don't do delays inside an ISR.

Also, you don't need to detach the interrupt, as interrupts are off inside an ISR (which is why delay won't work).

@Arty:

This is a cross-post of: Arduino Forum

Please do not cross-post. This wastes time and resources as people attempt to answer your question on multiple threads.

Thread locked.

  • Moderator