- The contact bouncing problem
- Debouncing solutions
- Making a debounced button circuit+software solution
- Next Lesson
In this lesson, we’re going to learn about contact bouncing (or chatter), a common problem with mechanical switches, buttons, and relays, and how to address it. As electromechanical devices, switch contact points are subject to momentum and elasticity, which act together to create rapid contact oscillations (“bounces”) when a switch is “opened” or “closed”. As an exaggerated but helpful analog, think of a hammer striking a surface: the hammer will “bounce” with dampened oscillations before entering a steady resting state with a surface.
Video. A slow-motion video of a hammer bouncing off a table to help illustrate how switch contacts bounce before entering a steady state. Video from Episode 37 “Contact and Bounce” of The Current Source YouTube channel.
So, what can you do? The solution is to “debounce” your switches, which can be done via software or pure hardware solutions, which we’ll address below. But first, let’s learn a bit more about the problem before discussing solutions. We’re going to use the term ‘switch’ to refer to any class of electromechanical device that has electrical contact points that can mechanically move to open or close an electrical circuit.
The Current Source recorded slow motion videos of switches bouncing during activations and deactivations. Just like the hammer, this electrical contact visibly bounces when first activated, creating a noisy contact signal.
Video. A slow-motion video of a switch mechanically bouncing off its contacts. Buttons are mechanical devices. When a button is pressed or a contact switch moved, it creates a rapid oscillation of open- and closed-circuits before settling to its final state. In comparison to computation, mechanical motion is slow. Microcontrollers—even old, slow ones like the ATmega328—work so fast that they will read these rapid oscillations as
LOW input state changes. Video from Episode 37 “Contact and Bounce” of The Current Source YouTube channel.
When you press a button or slide a switch, you are interacting with an electro-mechanical device. There is a physical mechanism moving to close a connection. When a switch/button first hits its contact point, it does not immediately stick. Instead, it rapidly “bounces” back and forth before eventually settling into a steady state (see animation above). This “bouncing” action only takes a few milliseconds (rarely more) but can cause problems if you naively assume that every contact is a new button press.
Indeed, if you look at a switch signal with an oscilloscope, you can see the “bouncing” action—the voltage rapidly fluctuates between
LOW (0V) and
HIGH (5V) before finally settling into its steady state of
HIGH. If you counted each of these fluctuations as a button press, you would have very erroneous input!
Figure. Image from ladyada.net
Yes! Take a look at the bouncing graphs below from an oscilloscope reading of both a switch activation (open to close) and deactivation (close to open). Depending on the switch type, the bouncing action may be more extreme for either action.
Figure. Switches can bounce during both closing and opening operations. Image derived from The Current Source.
So, how long do buttons bounce for? The answer: it varies depending on the switch type.
Thankfully, we have Jack Ganssle, an expert in embedded systems, to help answer this question. He painstakingly tested nearly twenty different switches ranging from cheap joystick and old mouse buttons to toggle and slide switches (see figure below). For each switch, he hooked up their contact signals to an oscilloscope, individually pressed them 300 times, and logged the min and max amount of bouncing for both closing and opening activations. The results of his research are documented here.
Figure. Switches tested by Jack Ganssle in his “switch bouncing” experiences (source).
In sum, most switches exhibited an average of 1.5 milliseconds (ms) of bouncing; however, two outlier switches exceeded 6.2ms. The worst was a red pushbutton, which had an open bounce of 157ms but only 20 microseconds (μs) on close. Interestingly, each switch seemed to have its own “bounce pattern” off rapid oscillations.
I am tracking both non-debounced button presses (“raw” button presses) and debounced button presses. In the video, you’ll observe how errant the non-debounced button press count gets (shown with the blue line)!
Video. A video demonstrating the importance of debouncing your buttons. We track the number of “raw” button presses (in blue) and the number of debounced button presses (in red) and plot the values in Serial Plotter.
Like with many problems involving microcontrollers and circuit, there are multiple solution approaches including those that rely purely on software, on hardware, or some combination of both. In this lesson, we’ll primarily focus on software solutions but briefly describe a common hardware solution below.
Although in physical computing, we often emphasize software solutions, hardware solution can be just as functionally good (though it does complicate the build and require more components). For example, in this Texas Instruments video, the instructor shows how to use a capacitor and a Schmitt Trigger to debounce a switch. The capacitor smooths out the the rising and falling edges of a button state transition and the Schmitt Trigger converts this smoothed signal back into digital output.
Video. This Texas Instruments video shows how to use a capacitor and a Schmitt Trigger to debounce a switch.
So, how do we solve debouncing in software? The key is to first detect a switch state change (let’s call this
state1), then wait for a set amount of time (a “debouncing window”), then check the switch state again (let’s call this
state2). If the initial state and the post-debouncing window state match (i.e.,
state1 == state2), then we can confidently conclude that the switch has transitioned from one steady state to another. See graph below.
Figure. Both graphs are showing the same open-to-close switch state change but with different annotations. The graph on the left highlights the first steady state (
LOW), the transition and bouncing contact state, and the second steady state (
HIGH). The graph on the right shows a depiction of our “debouncing window”, which is key to the software solution. Image made in PowerPoint.
So, how long should you set your debounce window? This depends on the switch type and on the expected use of your switch. Is it an occasionally-used toggle switch, a keyboard (a fast typist can generate ~10 characters/second), or a joystick button (button mashing anyone!?)?
As a second consideration, what’s a human perceptible amount of lag? Wikipedia suggests that “input lag”—from controller input to display response—of ~200ms are perceptible and distracting and that “quick twitch” games like first-person shooters and fighting games have response times of 67ms. Similarly, Ganssle suggests that, in his tests, a 100ms delay is noticeable but 50ms seems instantaneous.
Let’s build the following test circuit and then walk through some possible software solutions to debouncing.
Figure. The button-based circuit. Image made in Fritzing and PowerPoint.
Just like with the buttons lesson, we’ll need the following materials:
|Breadboard||Arduino Uno, Leonardo, or similar||Red LED||220Ω Resistor||12x12mm “Tactile Switch Buttons”|
For our first and most basic solution, we will read the button state, wait a given time period (the “debouncing window”), and then read the button state again. Notably, you should minimize the debouncing window while still satisfying the steady state requirement.
We’re going to use
delay here to wait for the “debouncing window” time period, which we already know should generally be avoided but is sometimes helpful and appropriate (if it’s not negatively impacting the responsiveness of your program, for example).
This source code is on GitHub.
Just as we did for our rate blinking LEDs lesson, we can modify the above program to eliminate delays and simply use timestamps to track state transitions. Indeed, this is how the official Arduino debounce tutorial works (link). Before looking at our solution, can you come up with your own?
For the two debouncing solutions above, we observed an initial state change on our digital input pin (
state1) and then, after some time period (the debouncing window), we verified this state change via a second read to the digital input pin (
state2). This approach protects against contact bounce, errant button presses (of time less than the debouncing window), and electrical interference (e.g., electric static discharge that cause transient digital input pin changes).
However, if we soften our requirement and assume that the
state1 change was correct and not some errant signal, then we can apply a few other solutions. In these cases, we do not read from the digital input pin again after the debouncing window but, instead, simply ignore input for that time period.
Figure. Two approaches to debouncing buttons.
I have learned two truths when it comes to debouncing:
Buttons don’t push themselves
Humans can’t do repeated button presses faster than 30 ms
So my go-to to avoid tuning things for various mechanical buttons has been:
As soon as the GPIO state changes to active, you declare the button is pressed (due to rule #1 above).
Ignore all further input on that button for 30 ms.
Go back to step #1.
This allows for interrupt-driven input and has zero delay between user action and input processing because you don’t wait the debounce period before declaring it pressed. It is important to have that low delay in highly reactive control surfaces (games).
The downside is that it won’t work if you need to pass regulatory ESD testing.
Here’s a quick implementation:
This solution is less robust but works well for human input in environments with limited electrical noise (see this Reddit discussion). However, as is pointed out in the Reddit thread (link), this simple solution does not protect against electrostatic discharge (ESD) and thus fails regulatory requirements (which require the two state reads like we did in Solutions 1 and 2).
Debouncing a single button is relatively simple but our state tracking code does not scale well to multiple buttons (it would look very messy). So, what should we do?
This problem is very similar to our state tracking issue for rate blinking LEDs. For that, we developed an object-oriented approach called
Blinker. Similarly, we could develop a
Button class that works largely the same way—and could even have special features like tracking double clicks, long presses, etc.
Indeed, there are a number of custom
Button classes online for Arduino, including:
kristianklein’s PushButton Arduino Button Library, which supports debouncing, double clicking, and long presses
JChristensen’s JC_Button Arduino Button Library, which includes debouncing support
ThomasGravekamp’s Arduino Debounced Switched Library, which includes support for callback functions when a trigger state is reached.
As a disclaimer, I have not tested these libraries myself but please do peruse them (to learn about how they work) and try them out yourself, if you’d like.
There are many other software debouncing solutions, including using interrupts. For example, here’s a version we made for the Redbear Duo boards (https://github.com/makeabilitylab/arduino/blob/master/RedBearDuo/RedBearDuoButtonInterruptWithDebouncing/RedBearDuoButtonInterruptWithDebouncing.ino). See Resources below.
For your prototyping journals, modify your piano code from the previous activity to use debouncing. You can use one of the aforementioned button classes or roll your own solution. Do you notice anything different? Why or why not?
There are lots of debouncing resources and various solutions covered online and in the literature:
Chapter 13: Microcontrollers - Debouncing, Scherz and Monk, Practical Electronics for Inventors
Debounce Code: One Post To Rule Them All, Hackaday
Reliable User Input with Unreliable Physical Switches, Reddit discussion
Debounce, Official Arduino tutorial
In the next lesson, we’ll move beyond digital input to the far more exciting and flexible world of analog input!