SALES INQUIRIES: 1 (888) 767-9864

Tutorial 19: Debouncing a Button with Arduino

Arduino Course for Absolute Beginners

Debouncing a Button with Arduino

In the last lesson you may have noticed that the button counts weren’t exact – sometimes if you pressed the button once, it would register two or even three presses. Maybe you pressed the button four times in a row and it only registered twice. If you would stop cursing at me – I will happily explain.

There is a thing called bounciness – very technical I know – and it relates to the physical properties of buttons. 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 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.

State Change Detection

This image made with Fritzing.

The Arduino Code

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:

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

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.

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:

The loop() is where things start to get interesting. 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:

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).

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():

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:

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)

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

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.

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

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:

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:

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