Debouncing a Button with Arduino

How to go about Debouncing a Button with Arduino

You may have noticed that button counts aren’t exact – sometimes if you press the button once, it registers two or even three presses.  There is a thing called bounciness (very technical I know) and it relates to the physical properties of buttons – you’re going to learn how to debounce here!

When you press a button down, it may not immediately make a complete connection. In fact, it may make contact on one side – then both – and then the other side – until it finally settles down.

This making and breaking contact is called bouncing. It is not a manufacturing defect of the button – bouncing is implicit in most physical switches.

Bouncing happens in a matter of milliseconds – but your microcontroller is moving so fast that it will detect a transition between two states every time the button bounces. This is why the button count from the last lesson may have been sporadic at times – it was registering unintended state changes due to bouncing.

This lesson will explore one way to “debounce” a button using code. Basically, what we do is record a state change and then ignore further input for a couple milliseconds until we are satisfied the bouncing has stopped. This filters out the noise of a bouncy button.

In this example, every time you press the button, the LED will switch on or off – depending on its current state.

You Will Need

  1. LED (1)
  2. 10,000 Ohm resistor (1)
  3. 220 Ohm Resistor (1)
  4. Momentary Push Button (1)
  5. Jumper Wires (3)
  6. Goat Cheese

Step-by-Step Debounce Instructions

  1. Connect an Arduino GND pin to one of the long power rails on the breadboard – this will be the ground rail.
  2. Connect the short leg of the LED to the same ground rail on the breadboard and connect the long leg to a different row on the breadboard.
  3. Connect the 220-ohm resistor from pin 13 to the same row where the long leg of the LED is attached.
  4. Place the pushbutton on the breadboard.
  5. Connect a jumper wire from the 5-volt pin to one side of the pushbutton.
  6. Connect a jumper wire from pin 2 to the other side of the pushbutton.
  7. Connect one side of the 10k ohm resistor to the ground rail on the breadboard and the other side to the pushbutton (on the same side that pin 2 connects).
  8. Plug the Arduino board into your computer with a USB cable.
  9. Open the Arduino IDE.
  10. The code for this example is available on the book website.
  11. Click the Verify button on the top left. It should turn orange and then back to blue.
  12. Click the Upload button. It will also turn orange and then blue once the sketch has finished uploading to your Arduino board.
  13. Open the serial monitor window.
  14. Press the button a couple times and watch how the LED at pin 13 reacts.
Button debounce Circuit diagram with Arduino, LED and Button with pull down resistor on breadboard.

This image made with Fritzing.

The Debounce Arduino Code

/* Debounce a push button

  This sketch will demonstrate debouncing a pushbutton with software.
  Every time the button is pressed the LED will toggle

  The circuit:
   LED attached from pin 13 to ground
   pushbutton attached from pin 2 to +5V
   10K resistor attached from pin 2 to ground

  Note: On most Arduino boards, there is already an LED on the board
  connected to pin 13, so you don't need any extra components for this example.
  created 21 November 2006
  by David A. Mellis
  modified 30 Aug 2011
  by Limor Fried
  modified 10 Mar 2021
  by Michael Cheich
  This example code is in the public domain.
  http://www.arduino.cc/en/Tutorial/Debounce
  programmingelectronics.com
*/

//initialize and declare variables
const int ledPin = 13; //led attached to this pin
const int buttonPin = 2; //push button attached to this pin

int buttonState = LOW; //this variable tracks the state of the button, low if not pressed, high if pressed
int ledState = -1; //this variable tracks the state of the LED, negative if off, positive if on

long lastDebounceTime = 0;  // the last time the output pin was toggled
long debounceDelay = 50;    // the debounce time; increase if the output flickers

void setup() {

  //set the mode of the pins...
  pinMode(ledPin, OUTPUT);
  pinMode(buttonPin, INPUT);

}//close void setup

void loop() {

  //sample the state of the button - is it pressed or not?
  buttonState = digitalRead(buttonPin);

  //filter out any noise by setting a time buffer
  if ( (millis() - lastDebounceTime) > debounceDelay) {

    //if the button has been pressed, lets toggle the LED from "off to on" or "on to off"
    if ( (buttonState == HIGH) && (ledState < 0) ) {

      digitalWrite(ledPin, HIGH); //turn LED on
      ledState = -ledState; //now the LED is on, we need to change the state
      lastDebounceTime = millis(); //set the current time
    }
    else if ( (buttonState == HIGH) && (ledState > 0) ) {

      digitalWrite(ledPin, LOW); //turn LED off
      ledState = -ledState; //now the LED is off, we need to change the state
      lastDebounceTime = millis(); //set the current time
    }

  }//close if(time buffer)

}//close void loop

Programming Electronics Academy members, check out the Arduino Course for Absolute Beginners to jump start your Arduino programming skills.

Not a member yet?  Sign up here.

Discuss the Sketch

This sketch is not included in the examples on your Arduino IDE.   It is a slightly modified version of the Debounce sketch located in File>Examples>02.Digital>Debounce.

We start this sketch with a handful of variables. Some variables are used to define pins:

const int ledPin = 13; //led attached to this pin

const int buttonPin = 2; //pushbutton attached to this pin

Other variables are made to track the state of the button and the state of the LED:

int buttonState = LOW; //this variable tracks the state of the button, low if not pressed, high if pressed

int ledState = -1; //this variable tracks the state of the LED, negative if off, positive if on

Finally, a couple long data type variables are initialized to keep track of time. The reason these variables are declared as long is because when time is measured in milliseconds the value can become a very big rather swiftly. The long data type can hold a much bigger number than an integer, making it a better-suited option for these variables.

long lastDebounceTime = 0; // the last time the output pin was toggled

long debounceDelay = 200;    // the debounce time; increase if the output flickers

There is a good reason for the above time tracking variables. Remember that the basis of this debounce sketch is to silence input from the pushbutton at pin 2 after the code detects a single state change. When the button is initially pressed the code registers that contact is made. The code takes this reading from pin 2 and then ignores further input until after a couple 10’s of milliseconds later. It is the time tracking variables that enable this to happen.

The setup() for this sketch is rather simple, it only sets pin modes:

void setup(){

     pinMode(ledPin, OUTPUT);

     pinMode(buttonPin, INPUT);

} //close void setup

The loop() is where things start to get interesting for the debounce. You may have noticed that the first thing many sketches do inside the loop() is check the state of a pin – that way the code has the most current conditions to work with. This sketch follows the same pattern, we begin by checking the state of pin 2 to see if the button has been pressed or not:

buttonState = digitalRead(buttonPin); //sample the state of the button – is it pressed or not?

We use the familiar digitalRead() function which takes the pin number you want to check and returns either HIGH or LOW, based on what voltage is read at the pin. In this circuit when the pushbutton is pressed 5 volts is applied to pin 2 (HIGH), otherwise the pin is at ground voltage (LOW).

Programming Electronics Academy members, use the coding challenges in the Bread and Butter: I/O section of the Arduino Course for Absolute Beginners to drive home these basic coding skills.

Not a member yet?  Sign up here.

The next thing we normally do is test the value we just sampled against a condition – in this example, however, we want to check how much time has passed between collecting the current sample and when we received the last sample. If the new sample came in just 1 millisecond after the last sample – we will ignore it. If it came in 2 milliseconds after the last sample, we will ignore it too.

In fact, we only want to accept a sample that was taken at least 50 milliseconds after the last sample. How do we implement this as a condition? We use the microcontrollers internal clock with the function millis():

if( (millis() – lastDebounceTime) > debounceDelay)

The above condition takes the current time and subtracts it from the last time a legitimate input was received, it then checks if the span of time is greater than a preset threshold which is named debounceDelay. It basically says, “Has enough time passed for me to even consider a new input?”

This is the gate, the filter, that blocks the noise of a bouncing button. Once we know a reasonable amount of time has passed, we will accept the input and begin to process it.

We use an if-else statement to do more filtering:

if( (buttonState == HIGH) && (ledState < 0) ){

     digitalWrite(ledPin, HIGH); //turn LED on

     ledState = -ledState; //now the LED is on, we need to change the state

     lastDebounceTime = millis(); //set the current time

}

We are only interested when the LED goes from LOW to HIGH, this is the rising edge of the input. Pressing the button initiates the rising edge. Releasing the button initiates the falling edge.

The if statement checks these two conditions:

  1. That the input from pin 2 is HIGH
  2. That the ledState variable is less than zero (The LED is off – more on this in a moment)
Square wave showing low and high voltage during phases of button presses in order to explain button debounce

You can see that we have multiple conditions that must be met – we use two ampersands (&&) to join these two conditions together:

if( (buttonState == HIGH) && (ledState < 0) )

This condition asks, “Is the button pressed and is the LED off?” If yes, execute the code inside the if statement. If one of these conditions is not met, the code inside the if statement is skipped. Both conditions must be met for the if statement to execute.  This is a key for our debounce code routine.

Programming Electronics Academy members, check out the Control Structures section of the Arduino Course for Absolute Beginners to learn and practice your programming control flow.

Not a member yet?  Sign up here.

If the condition of the if statement is met, we do three things:

  1. Turn the LED on
  2. Update the state of the LED from off to on
  3. Update the lastDebounceTime variable
if( (buttonState == HIGH) && (ledState < 0) ){

     digitalWrite(ledPin, HIGH); //turn LED on

     ledState = -ledState; //now that the LED is on, we need to change the state tracking variable

     lastDebounceTime = millis(); //set the current time

}

Let’s discuss the code block above line-by-line. First, we turn on the LED by using digitalWrite() to apply high voltage to pin 13.

Second, we multiply the ledState variable by a negative one (-1) to change its sign from negative to positive. For our purposes, if the ledState variable is negative it means the LED is off, and if ledState is positive the LED is on. Finally, we update the lastDebounceTime to the current time using the millis() function again.

Now when the button is released the LED will stay on – all we did was toggle the LED from off to on with a button press. What happens when we press the button again? Ideally, the LED turns off.

To turn off the LED we once again refer to the rising edge of the input. We want to know when the button is pressed again – but this time, we want to address the scenario when the button is pressed and the LED is already on. The next part of the code addresses this condition:

} else if ( (buttonState == HIGH) && (ledState > 0) ){

     digitalWrite(ledPin, LOW); //turn LED off

     ledState = -ledState; //now the LED is off, we need to change the state

     lastDebounceTime = millis(); //set the current time

}//close if/else

The condition of the else-if statement requires buttonState to be HIGH and ledState to be positive (on). Inside this statement, we toggle the LED off by writing digital pin 13 LOW.

A little caveat:

Notice how the if-else statement has multiple conditions. The general form of these if-else statements is as follows:

if (condition) {

  Do something;

} else if (condition) {

  Do something else;

} else if (condition) {

  Do something else;

} else {

  Do this if no condition is met;

}

In the example sketch we have been using, we do not have a final else statement that is a catchall – we set very specific conditions and we only want to act on those conditions. In this way we ignore the input that comes from the falling edge – when the button is released and the voltage at pin 2 changes from HIGH to LOW.

The very next time through the loop() we read the voltage at the button again, but that reading (and following readings) are filtered by the debounce if-statement condition until we reach the time threshold previously set.

If you still have bouncing issues with the button, try increasing the debounceDelay variable from 50 to 100. This increase will ignore input even longer, but there is a price to pay. What if someone wants to rapidly toggle the LED by pressing the button very fast? This is a circumstance when you will run into trouble if you make the debounceDelay too long. If you are making a video game controller this could be a definite issue!

Try On Your Own

  • Try increasing and decreasing the debounceDelay time and observe the effect.
  • Add an LED to pin 12 and change the code so that every time you press the button the LEDs toggle between each other (i.e., one is on when the other is off).

Further Reading

installing Arduino libraries

Installing Arduino Libraries | Beginners Guide

IoT sewage project

Pumping poo! An IoT sewage project

ESP32 webOTA updates

How to update ESP32 firmware using web OTA [Guide + Code]

error message Brackets Thumbnail V1

expected declaration before ‘}’ token [SOLVED]

Compilation SOLVED | 1

Compilation error: expected ‘;’ before [SOLVED]

Learn how to structure your code