Pushbuttons are ubiquitous components used for user input in Arduino projects. However, reading pushbutton inputs reliably can be challenging due to issues such as contact bounce and the need for responsive behaviour. In this article, we’ll explore how to read pushbutton inputs using interrupts and debounce techniques, ensuring accurate and responsive interaction with Arduino microcontrollers.
Those who are not completely sure what interrupts and bounce are, must read the below articles:
Our earlier example projects were examples of utilizing interrupt function:
---
- Arduino Interrupt: Blink LED and Beep Every 1 Second, Pauses Upon Button Press
- Arduino 3V DC Mini Pump with Push Button Control and LED Indicators (For Kids)
The second project was intended for the children. I avoided to make the circuit more complicated.
Pushbuttons, also known as momentary switches, are mechanical switches that make or break electrical connections when pressed. They are commonly used for tasks such as toggling LEDs, triggering actions, and navigating menus in Arduino projects. Challenges with Pushbutton Input:
Contact Bounce: When a pushbutton is pressed or released, its contacts bounce against each other multiple times before settling in a stable state. This bouncing phenomenon can lead to multiple false readings, causing erratic behaviour in the Arduino.
Responsiveness: In applications where rapid or precise response is required, such as gaming or real-time control, delays caused by software polling of pushbutton inputs may be unacceptable.
Interrupts offer a more efficient way to handle pushbutton inputs by allowing the Arduino to respond immediately to changes in the button state, without continuously polling the button status in the main loop. Here’s how to use interrupts to read pushbutton inputs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | const int buttonPin = 2; // Pin connected to the pushbutton volatile bool buttonState = LOW; // Current state of the button void setup() { pinMode(buttonPin, INPUT_PULLUP); // Enable internal pull-up resistor attachInterrupt(digitalPinToInterrupt(buttonPin), buttonInterrupt, CHANGE); // Attach interrupt } void loop() { // Your main loop code here } void buttonInterrupt() { // Read the state of the button buttonState = digitalRead(buttonPin); // Your code to handle button state change here } |
If you use an oscilloscope, you’ll see the theoretical problem as real:

When testing our setup, we would expect the variable to increase by one each time. But we will see that it jumps several times. That saw tooth like |||| peaks are the effect of the bounce. It can generate multiple interrupts every time we push the button. The result can kill the reliability of the pushbutton – which can get worse with the ageing of the pushbutton.
This is the reason we need debouncing.
How We Can Read a Pushbutton Both with Interrupts and Debounce?
Debouncing without using interrupts is just easy. From the above-explained logic, we can not avoid utilizing the interrupt function. But adding software debouncing alone invites a lot of trouble. That is why I will suggest thinking about adding a hardware debounce. It is not only a robust solution but also avoids the disadvantage of increasing the coding complexity of our setup. Of course, we can add software debounce to the above sketch in this way:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | const int buttonPin = 2; // Pin connected to the pushbutton const int debounceDelay = 50; // Debounce delay in milliseconds unsigned long lastDebounceTime = 0; // Last time the button state changed volatile bool buttonState = LOW; // Current state of the button void setup() { pinMode(buttonPin, INPUT_PULLUP); // Enable internal pull-up resistor attachInterrupt(digitalPinToInterrupt(buttonPin), buttonInterrupt, CHANGE); // Attach interrupt } void loop() { // Your main loop code here } void buttonInterrupt() { // Read the state of the button int reading = digitalRead(buttonPin); // Check if the button state has changed and debounce if (reading != buttonState) { lastDebounceTime = millis(); // Update debounce time } if (millis() - lastDebounceTime > debounceDelay) { // Update button state only if the button state has been stable for the debounce delay buttonState = reading; // Your code to handle button state change here } } |
The Schmitt trigger works similarly to an analog comparator but compares an input voltage and one of two possible threshold voltages. This makes it a threshold switch: if the upper threshold voltage is exceeded, the output takes on the maximum possible output voltage (HIGH) in the non-inverting version; as a binary number, it is coded as 1 for positive logic and 0 for negative logic. If the threshold voltage falls below the lower threshold, the output assumes the minimum possible output voltage (LOW).
The reverse is true for the inverting Schmitt trigger: If the input voltage exceeds the upper switching threshold of the Schmitt trigger, its output voltage tilts from the maximum voltage value to the minimum voltage value (LOW). If the input voltage then falls below the lower switching threshold, the output voltage tilts back to the maximum output voltage (HIGH).
The easiest way to apply hardware debounce is to place a 1uF capacitor in parallel with the push button when the circuit is not time-critical. In layman’s language – you are shorting the positive and negative ends of the push button with a capacitor. This is the easiest design:

The problem of that solution you can say will arise from deliberately holding the push button.
Practically, there is internal resistance in the capacitor plus the wires themselves will have some small resistance. Take it as 1 ohm. When you close the switch you have 3 to 5 volts discharging into 1 ohm = 5A. After one time constant this current drops by 63%. After 5 time constants (50us) the current is zero. Most small circuit wiring can handle this current for this duration. The inductance of the wiring and the capacitor plates will limit any current surge in the circuit.
Just add a 10-ohm resistor in series with the capacitor to the ground to get rid of fear.

For time-critical operations, it is better to use a 74HC14 chip for using the Schmitt trigger theory.
Tagged With injtk