1

In short, I am attempting to debounce several buttons using something like a while loop instead of delays.

This code runs in a box with a series of buttons on it, if the correct sequence of buttons is pressed, then the arduino will run a flashing LED sequence and open a solenoid. There are two correct sequences, a "right" answer (Green LED), and a master code (Blue LED). Anything else results in the failed sequence (Red LED). The problem I am currently having is that my current debouncing system is very finicky, I would like to implement something allowing more flexibility for speed of button press etc.

The problem currently is I am relying on delay to debounce. If I delay long enough it becomes difficult to type the correct code in because the user has to know to wait a certain amount of time before pressing the next key. If I do not delay long enough my combination is regularly corrupted with extraneous button pushes.

Below is my code, The debouncing occurs on lines 128-160. Below that is a lot of LED flashing.

#define CPU_RESTART_ADDR (uint32_t *)0xE000ED0C
#define CPU_RESTART_VAL 0x5FA0004
#define CPU_RESTART (*CPU_RESTART_ADDR = CPU_RESTART_VAL);

const int button5 = 2;    //fifth  button is on pin 2
const int button4 = 3;    //fourth button is on pin 3
const int button3 = 4;    //third  button is on pin 4
const int button2 = 5;    //second button is on pin 5
const int button1 = 6;    //first  button is on pin 6
const int systemLED = 7;  //Red LED on front of Box (Power Indicator)
const int redLed1 = 8;    //red LED is on pin 7
const int greenLed1 = 9;  //green LED is on pin 8
const int blueLed1 = 10;   //blue LED is on pin 9
const int redLed2 = 11;   //red LED is on pin 10
const int greenLed2 = 12; //green LED is on pin 11
const int blueLed2 = 13;  //blue LED is on pin 12
const int solenoid = 14;  //activates solenoid Relay Circuit
const int relay2 = 15;     // >:-}


int code[] = {1,2,3,4,5}; //the desired code is entered in this array, separated by commas

int entered[5]; //create a new empty array for the code entered by the user (has 4 elements)

void setup(){ //run once at sketch startup
  Serial.begin(9600); //begin Serial
  pinMode(systemLED, OUTPUT);
  pinMode(button1, INPUT); //button 1 is an input
  pinMode(button2, INPUT); //button 2 is an input
  pinMode(button3, INPUT); //button 3 is an input
  pinMode(button4, INPUT); //button 4 is an input
  pinMode(button5, INPUT); //button 5 is an input
  pinMode(redLed1, OUTPUT); //the red LED is an output
  pinMode(redLed2, OUTPUT); //the red LED is an output
  pinMode(greenLed1, OUTPUT); // the green LED is an output
  pinMode(greenLed2, OUTPUT); // the green LED is an output
  pinMode(blueLed1, OUTPUT); 
  pinMode(blueLed2, OUTPUT);
  pinMode(relay2, INPUT_PULLUP); //enable relay outputs as input_pullup in order to prevent triggering when initial power is provided
  pinMode(solenoid, INPUT_PULLUP); 

  delay(500);

  pinMode(relay2, OUTPUT);   //convert pinmode for relay back to operable mode
  pinMode(solenoid, OUTPUT);//this prevents the relay and relay2 from triggering at startup

  digitalWrite(relay2, HIGH);
  digitalWrite(solenoid, HIGH);

  digitalWrite(blueLed1, HIGH); //turn both
  digitalWrite(greenLed1, HIGH);
  digitalWrite(blueLed2, HIGH);
  digitalWrite(greenLed2, HIGH); //LEDs on
  delay(100); //wait
  digitalWrite(blueLed1, LOW); //turn both
  digitalWrite(greenLed1, LOW);
  digitalWrite(blueLed2, LOW);
  digitalWrite(greenLed2, LOW); //LEDs off
  digitalWrite(systemLED, HIGH); //turn the red LED on
  delay(75); //wait

  for (int i = 0; i < 5;i++){ //work through numbers 0-4
    Serial.println(code[i]); //print each digit of the code
    Serial.println(entered[i]); //print each element of the entered[] array (this was for me to check that it started at 0)
  }
}

void checkEntered1(int button /* define the 1,2,3 or 4 as an integer called button */){ //check the first element of the entered[] array
  if (entered[0] != 0){ //if it is not a zero, i.e. it has already been input
    checkEntered2(button); //move on to checkEntered2, passing it "button"
  }

  else if(entered[0] == 0){ //if it is zero, i.e. if it hasn't been defined with a button yet
    entered[0] = button; //set the first element as the button that has been pressed
    Serial.print("1: ");Serial.println(entered[0]); //for debugging
  }
}

void checkEntered2(int button){ //check the second element of the entered[] array
  if (entered[1] != 0){ //if it is not a zero, i.e. it has already been input
    checkEntered3(button); //move on to checkEntered3, passing it "button"
  }

  else if(entered[1] == 0){ //if it is zero, i.e. if it hasn't been defined with a button yet
    entered[1] = button; //set the second element as the button that has been pressed
    Serial.print("2: ");Serial.println(entered[1]); //for debugging
  }
}

void checkEntered3(int button){  //check the third element of the entered[] array
  if (entered[2] != 0){ //if it is not a zero, i.e. it has already been input
    checkEntered4(button); //move on to checkEntered4, passing it "button"
  }

  else if (entered[2] == 0){ //if it is zero, i.e. if it hasn't been defined with a button yet
    entered[2] = button; //set the third element as the button that has been pressed
    Serial.print("3: ");Serial.println(entered[2]); //for debugging
  }
}

void checkEntered4(int button){ //check the fourth element of the entered[] array
  if (entered[3] != 0){ //if it is not a zero, i.e. it has already been input
    checkEntered5(button); //set the final element as the button that has been pressed
  }

  else if (entered[3] == 0){ //if it is zero, i.e. if it hasn't been defined with a button yet
    entered[3] = button; //set the fourth element as the button that has been pressed  
    Serial.print("4: ");Serial.println(entered[3]); //for debugging
  } 
}

This is where I need help debouncing, as can be seen I have attempted to debounce with delay(75) originally I had delay(1000) as the debounce which worked but required the user to wait or break normal rhythm while entering the code due to the long delay between inputs being accepted.

void checkEntered5(int button){ //check the fifth element of the entered[] array
  if (entered[4] == 0){ //if it is zero, i.e. if it hasn't been defined with a button yet
    entered[4] = button; //set the final element as the button that has been pressed
    Serial.print("5: ");Serial.println(entered[4]); //for debugging
    delay(100); //allow time for processing
    compareCode(); //call the compareCode function
  }
}
void loop(){ //run repeatedly
  if (digitalRead(button1) == HIGH){ //if button1 is pressed
    checkEntered1(1); //call checkEntered and pass it a 1
    //delay(1000); //wait
    while( digitalRead(button1) == HIGH) { } // do nothing
    delay(75);//wait, needed for correct functioning, otherwise
              //buttons are deemed to be pressed more than once
  }
  else if (digitalRead(button2) == HIGH){ //if button2 is pressed
    checkEntered1(2); //call checkEntered1 and pass it a 2
    //delay(1000); //wait
    while( digitalRead(button2) == HIGH) { } // do nothing
    delay(75); //wait
  }
  else if (digitalRead(button3) == HIGH){ //if button3 is pressed
    checkEntered1(3); //call checkEntered1 and pass it a 3
    //delay(1000); //wait
    while( digitalRead(button3) == HIGH) { } // do nothing
    delay(75); //wait
  }
  else if (digitalRead(button4) == HIGH){ //if button4 is pressed
    checkEntered1(4); //call checkEntered1 and pass it a 4
    //delay(1000); //wait
    while( digitalRead(button4) == HIGH) { } // do nothing
    delay(75); //wait
  }
  else if (digitalRead(button5) == HIGH){ //if button4 is pressed
    checkEntered1(5); //call checkEntered1 and pass it a 5
    //delay(1000); //wait
    while( digitalRead(button5) == HIGH) { } // do nothing
    delay(75); //wait
  }
}

Everything below this point is included simply to provide context for the check entered code seen above. Everything functions as is expected except that the debounce is imperfect.

void compareCode(){ //checks if the code entered is correct by comparing the code[] array with the entered[] array
  for (int i = 0; i<4;i++){
    Serial.write(entered[i]);
  }
  if ((entered[0]==code[0]) && (entered[1]==code[1]) && (entered[2]==code[2]) && (entered[3]==code[3]) && (entered[4]==code[4])){ //if all the elements of each array match the passcode

    //--. .-. . . -.

    digitalWrite(solenoid, LOW); 
    digitalWrite(greenLed1, HIGH); //turn the green LED on
    digitalWrite(greenLed2, HIGH); //turn the green LED on
    delay(50); //wait for a bit
    digitalWrite(greenLed1, LOW); //turn the green LED off
    digitalWrite(greenLed2, LOW); //turn the green LED off
    delay(500);
    digitalWrite(solenoid, HIGH); 
    delay(2500);

  }

  else if ((entered[0]==code[4]) && (entered[1]==code[2]) && (entered[2]==code[0]) && (entered[3]==code[3]) && (entered[4]==code[1])){ 

    //-... .-.. ..- .

    digitalWrite(blueLed1, HIGH);
    digitalWrite(blueLed2, HIGH);
    delay(1000);
    digitalWrite(solenoid, LOW); 
    delay(4000);
    digitalWrite(blueLed1, LOW);
    digitalWrite(blueLed2, LOW);
    delay(50);
    digitalWrite(redLed1, HIGH);
    digitalWrite(redLed2, HIGH);
    delay(1000);
    digitalWrite(redLed1, LOW);
    digitalWrite(redLed2, LOW);
    delay(50);
    digitalWrite(solenoid, HIGH); 
    delay(1000);
  }

  else { //if you (or the intruder) get the code wrong
    flash(); //call the flash function
    for (int i = 0; i < 4; i++){ //this next loop is for debugging
      entered[i] = 0;
      Serial.println(entered[i]);
    }
  } 
}

void flash(){ // this is basically the blink example
  //.-. . -..

  digitalWrite(relay2, LOW); 
  digitalWrite(redLed1, HIGH);
  digitalWrite(redLed2, HIGH);
  delay(50);
  digitalWrite(redLed1, LOW);
  digitalWrite(redLed2, LOW);
  delay(250);
  digitalWrite(redLed1, HIGH);
  digitalWrite(redLed2, HIGH);
  delay(50);
  digitalWrite(redLed1, LOW);
  digitalWrite(redLed2, LOW);
  digitalWrite(relay2, HIGH); 
  delay(2500);
  CPU_RESTART;
3
  • If you want to solve the denouncing problem, make a simpler sketch that only accepts input, and maybe outputs the result to the serial monitor. On the other hand if you can accept input in isolation and the issue seems more in the interaction with your input vs. display code, see if you can make a simpler case (fewer outputs, etc) that illustrates the interaction. At present this is still too big to be readily understood, but is it possible you may have a situation where each distinct user input triggers a time-consuming sequence of output operations? Oct 19, 2016 at 17:39
  • I believe I understand you, however I may have phrased my question poorly, I am simply looking to see if anyone knows of a simple method of debouncing which is more reliable than the delay function. This code is adapted from someone else's which I found a long time ago, I do not entirely understand the intricacies of it to simplify on my own.
    – user26383
    Oct 19, 2016 at 20:31
  • 1
    The simplest means of debouncing is that once you have any input you will accept, you ignore all inputs for some fraction of a second. If you also trigger time consuming operations from the inputs is when that gets complicated - simplest there is to not accept new input until they are done, but the user may not enjoy that. Or if you need to recognize a different button immediately and only have a debounce delay to the same button. Or if you want to have auto-repeate if a button is held down long enough. Oct 19, 2016 at 20:47

3 Answers 3

0
    while( digitalRead(button2) == HIGH) { } // do nothing

Doing that right after a digital read assumes there is only one bounce, That's highly unlikely. I would put the delay right after the read and before the delay. Depending on the type of switch bouncing varies, but shouldn't be over 150mS. Less for the little tactile push button used on pcboards.

    // read button
    delay(75); //wait
    while( digitalRead(button2) == HIGH) { } // do nothing

You can always use an RC filter to debounce each button, then you don't have to worry about the difficult key presses, just the extra parts and expense.

2

Many of the lines shown in the program box are superfluous – totally redundant and unnecessary. I suggest that you revise it [see (a)-(d) below], then replace all the stuff in the program box with the revised version.

(a) Use more subroutine parameters. In cases where you have four or five otherwise-identical routines that differ only in some constants, pass those values into one routine as parameters. Don't write out long lists of nearly-identical routines.

(b) Don't create artificial dependencies. Your code in the five checkEntered- routines is obscure, so I can't say without taking more time than I care to spend, but it appears that you have made tests for button 2 depend on tests for button 1, and so on down the line. That makes no sense. Test each button separately and in an identical manner, using a parameterized subroutine. To enforce an order of button presses, create an array P of 5 bytes (or however many you like) and initialize a counter Q to zero. Whenever your debounce routine detects a valid button press, if Q is small enough store the button number in P[Q] and then increase Q. (If Q is (size P) or more, don't store.)

(c) Don't nest subroutine calls more deeply than necessary. Your checkEntered1() can call checkEntered2(), which can call checkEntered3(), and so forth. Besides not making sense, that series of calls uses five times as much stack memory as necessary. (Generally, RAM is a scarce resource on AVR microcontrollers.) Just use five calls, one after another, to a properly parameterized subroutine.

(d) For repetitious sequences like the following,

digitalWrite(greenLed1, HIGH); //turn the green LED on
digitalWrite(greenLed2, HIGH); //turn the green LED on
delay(1000); //wait for a bit
digitalWrite(greenLed1, LOW); //turn the green LED off
digitalWrite(greenLed2, LOW); //turn the green LED off
delay(250); //wait for a bit

which seem to recur with minor variations about a dozen times, write a properly parameterized subroutine and call it as necessary. For example:

void flashPair(byte L1, byte L2, int del1, int del2) {
  digitalWrite(L1, HIGH);   // turn the green LED on
  digitalWrite(L2, HIGH);   // turn the green LED on
  delay(del1);          // wait a little
  digitalWrite(L1, LOW);    // turn the green LED off
  digitalWrite(L2, LOW);    // turn the green LED off
  delay(del2);          // wait a little
}
...
flashPair(greenLed1, greenLed2, 1000, 250);
flashPair(redLed1,   redLed2,     50, 2500);
...

There are, of course, other ways of handling those dozen tedious repetitions. For example, one could write a for loop that gets pin numbers and delays from an array and turns LEDs on or off as needed. For example, see an answer to question 17453 for a method of treating multiple buttons systematically using information in arrays along with a for loop.

2

I have used and know of many others who use the Bounce2 Library to provide an easy way to debounce the switch without using any delays so the user wouldn't have to pause between presses. Behind the scenes it uses the Millis function to provide the debounce which doesn't interrupt the sketch running at all. You could also write the Debounce with Millis function yourself.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.