One tap, five "pressed" messages? Fix it with ESP32 Uno button debounce code that turns every real press into exactly one event. Inside a push button, two metal strips hit each other and spring back for a few milliseconds. The ESP32 Uno form board samples its pins thousands of times per second, so it catches each of those tiny jumps. A short time filter in your Arduino code, written by hand or taken from a library, hides them.
What you will do:
Upload a plain button sketch and count the false events it prints
Build a debounce filter based on the millis() timer
Shrink the same job to a few lines with the ezButton library
Read three buttons at once, each with its own debounce timer
Here is a scope-style view of the noisy edge a single press produces:
Disclosure: Some of the links provided in this section are Amazon affiliate links. We may receive a commission for any purchases made through these links at no additional cost to you. Additionally, some of these links are for products from our own brand, DIYables .
Button Basics
After this part, you know which page explains the button itself.
About 1 to 20 ms, depending on the button and its age
Pin level with this wiring
HIGH when free, LOW when pushed
Good to know: ESP32 Uno pins run at 3.3V logic. With a button wired to GND and the internal pull-up on, no other voltage ever reaches the pin.
ESP32 Uno Pinout
The printed labels on the Uno headers are not the numbers you type in a sketch. Look up D6, D7 and D8 in this picture to find the GPIO number behind each one.
How to Connect the Button
By the end of this part, one button sits between D7 and GND.
This image is created using Fritzing. Click to enlarge image
Component Pin
ESP32 Uno Pin
Button leg 1
D7 (GPIO14)
Button leg 2
GND
Good to know: you need no resistor here. The sketches call INPUT_PULLUP, which links GPIO14 to 3.3V inside the chip.
The first two sketches use this exact circuit. Run the raw version first, then the filtered one, and compare the two outputs.
ESP32 Uno Code - Raw Button Read (No Debounce)
This sketch prints a line every time the pin level flips, with no filter at all.
/* * This ESP32 Uno code was developed by newbiely.com * * This ESP32 Uno code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/esp32-uno/esp32-uno-button-debounce */#define BUTTON_PIN 14 // D7 (GPIO14) on the ESP32 Uno reads the buttonint button_state; // level read in this loop passint prev_button_state = LOW; // level read in the pass beforevoidsetup() {// start the Serial Monitor link at 9600 baudSerial.begin(9600);// turn on the internal pull-up so the pin sits HIGH when releasedpinMode(BUTTON_PIN, INPUT_PULLUP);}voidloop() {// get the present level of the button pin button_state = digitalRead(BUTTON_PIN);if (prev_button_state == HIGH && button_state == LOW)Serial.println("The button is pressed");elseif (prev_button_state == LOW && button_state == HIGH)Serial.println("The button is released");// remember this level for the next comparison prev_button_state = button_state;}
Build the circuit by following the wiring table above.
Plug in the USB Type-C cable between the ESP32 Uno and your computer.
Select the board in Arduino IDE: pick ESP32 Dev Module and the COM port of the board (CH340).
Paste the sketch into a new Arduino IDE window.
Click Upload (Upload) and wait until the IDE reports the upload is done.
Start the Serial Monitor and set it to 9600 baud.
Push the button, keep it down for two or three seconds, and let go.
Read the log. Yours may look like this:
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
ESP32 Dev Module
Newbiely.ino
···
8Serial.println("Hello World!");
Output
Serial Monitor
Message (Enter to send message to 'ESP32 Dev Module' on 'COM15')
New Line
9600 baud
The button is pressed
The button is released
The button is pressed
The button is pressed
The button is released
The button is released
The button is released
Ln 11, Col 1
ESP32 Dev Module on COM15
2
Pro tip: tap the button with a very light touch. A soft, slow press usually makes the contacts bounce even more, so the extra lines show up more often.
That log came from a single press and a single release. Every line after the first pair is a bounce that the ESP32 Uno read as a real event.
※ NOTE THAT:
No debounce time fits every case. A clicky tactile switch may settle in 10 ms, while a big panel button or a worn one may need far longer, so tune DEBOUNCE_TIME for your own part.
ESP32 Uno Code - Debounced Button with millis()
This sketch only accepts a new level after it has stayed unchanged for 50 ms.
/* * This ESP32 Uno code was developed by newbiely.com * * This ESP32 Uno code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/esp32-uno/esp32-uno-button-debounce */#define BUTTON_PIN 14 // D7 (GPIO14) on the ESP32 Uno reads the button#define DEBOUNCE_TIME 50 // quiet time in ms; raise it if extra messages show upint last_steady_state = LOW; // last level that stayed stable long enoughint last_flickerable_state = LOW; // last raw level, bounces includedint current_state; // raw level read in this loop passunsignedlong last_debounce_time = 0; // millis() value of the latest raw changevoidsetup() {// start the Serial Monitor link at 9600 baudSerial.begin(9600);// turn on the built-in pull-up, so the pin idles HIGHpinMode(BUTTON_PIN, INPUT_PULLUP);}voidloop() {// get the raw level of the button pin current_state = digitalRead(BUTTON_PIN);// a raw change means a bounce or a real press, so restart the clockif (current_state != last_flickerable_state) {// note the time of this change last_debounce_time = millis();// remember the new raw level last_flickerable_state = current_state; }if ((millis() - last_debounce_time) > DEBOUNCE_TIME) {// the level has been quiet long enough, so act on a real changeif (last_steady_state == HIGH && current_state == LOW)Serial.println("The button is pressed");elseif (last_steady_state == LOW && current_state == HIGH)Serial.println("The button is released");// store the accepted stable level last_steady_state = current_state; }}
Upload and Test
Load this sketch into the Arduino IDE in place of the old one.
Send it to the board by clicking Upload.
Leave the Serial Monitor open with the same baud rate.
Press and hold the button for a moment, then release it.
Check the log:
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
ESP32 Dev Module
Newbiely.ino
···
8Serial.println("Hello World!");
Output
Serial Monitor
Message (Enter to send message to 'ESP32 Dev Module' on 'COM15')
New Line
9600 baud
The button is pressed
The button is released
Ln 11, Col 1
ESP32 Dev Module on COM15
2
Pro tip: press the button ten times as fast as you can and count the lines. You should get exactly ten pairs. If some taps go missing, your presses are shorter than 50 ms, so try 30 ms.
One push now gives one "pressed" line, and one release gives one "released" line. The bounce is filtered out.
The Shortcut: ESP32 Uno Button Debounce Code with ezButton
This part gets the same clean result with far less code.
The timer logic above grows long when you copy it for each button. Our ezButton library keeps one timer per button for you. Read the ezButton library guide for all its functions.
Step 1 — Install the library
Open Library Manager in the Arduino IDE, type "ezButton", and click Install on the library by ArduinoGetStarted.com.
Step 2 — Debounce one button
The wiring stays the same: one button from D7 (GPIO14) to GND.
/* * Created by ArduinoGetStarted.com * * This example code is in the public domain * * Tutorial page: https://arduinogetstarted.com/tutorials/arduino-button-library * * ESP32 Uno sketch: the ezButton library filters the bounce on one button * and sends each press and release to the Serial Monitor. */#include <ezButton.h>ezButtonbutton(14); // one ezButton object on D7 (GPIO14)voidsetup() {Serial.begin(9600);button.setDebounceTime(50); // ignore changes shorter than 50 ms}voidloop() {button.loop(); // let the library sample the pin before any checkif(button.isPressed())Serial.println("The button is pressed");if(button.isReleased())Serial.println("The button is released");}
Code Part
What It Does
ezButton button(14)
Creates a button object on D7 (GPIO14) with the pull-up on
setDebounceTime(50)
Sets the quiet time to 50 ms
button.loop()
Samples the pin and runs the timer, so call it first in every pass
isPressed() / isReleased()
Returns true once for each clean press or release
The output matches the hand-made filter, in about a third of the lines.
Step 3 — Debounce three buttons
Add two more buttons. Each one gets its own pin, its own object and its own timer.
This image is created using Fritzing. Click to enlarge image
Component Pin
ESP32 Uno Pin
Button 1 leg 1
D6 (GPIO27)
Button 2 leg 1
D7 (GPIO14)
Button 3 leg 1
D8 (GPIO12)
Leg 2 of all three buttons
GND
WARNING
The third button uses D8, which is GPIO12 and one of the boot strapping pins. It must be LOW when the board starts, or the flash gets the wrong voltage and the board will not boot. A button to GND can only pull the pin LOW, so this circuit is safe. Never wire anything to D8 that could pull it HIGH (for example a module output or a pull-up resistor to 3.3V). If the board fails to start or upload, unplug the D8 wire and try again.
/* * This ESP32 Uno code was developed by newbiely.com * * This ESP32 Uno code is made available for public use without any restriction * * For comprehensive instructions and wiring diagrams, please visit: * https://newbiely.com/tutorials/esp32-uno/esp32-uno-button-debounce */#include <ezButton.h>ezButton button_1(27); // first button on D6 (GPIO27)ezButton button_2(14); // second button on D7 (GPIO14)ezButton button_3(12); // third button on D8 (GPIO12), a strapping pinvoidsetup() {Serial.begin(9600); button_1.setDebounceTime(50); // 50 ms quiet time for the first button button_2.setDebounceTime(50); // 50 ms quiet time for the second button button_3.setDebounceTime(50); // 50 ms quiet time for the third button}voidloop() { button_1.loop(); // sample the first button button_2.loop(); // sample the second button button_3.loop(); // sample the third buttonif(button_1.isPressed())Serial.println("The button 1 is pressed");if(button_1.isReleased())Serial.println("The button 1 is released");if(button_2.isPressed())Serial.println("The button 2 is pressed");if(button_2.isReleased())Serial.println("The button 2 is released");if(button_3.isPressed())Serial.println("The button 3 is pressed");if(button_3.isReleased())Serial.println("The button 3 is released");}
Try pressing button 3, then button 1, then holding button 2 while you tap button 3. Each button reports on its own:
Newbiely | Arduino IDE 2.3.8
──
☐
✕
File
Edit
Sketch
Tools
Help
ESP32 Dev Module
Newbiely.ino
···
8Serial.println("Hello World!");
Output
Serial Monitor
Message (Enter to send message to 'ESP32 Dev Module' on 'COM15')
New Line
9600 baud
The button 3 is pressed
The button 3 is released
The button 1 is pressed
The button 1 is released
The button 2 is pressed
The button 3 is pressed
The button 3 is released
The button 2 is released
Ln 11, Col 1
ESP32 Dev Module on COM15
2
Video Tutorial
See the ESP32 Uno build in action in the video below.
Code Explanation
This part shows how the millis() filter decides what is real.
Code Part
What It Does
BUTTON_PIN 14
Points the sketch at D7 (GPIO14)
last_flickerable_state
Holds the newest raw level, bounces included
last_debounce_time = millis()
Restarts the clock on every raw change
millis() - last_debounce_time > DEBOUNCE_TIME
True only after 50 ms with no change
last_steady_state
Holds the accepted level and is compared with the new one to print pressed or released
A bounce keeps resetting the clock, so only the final settled level passes the 50 ms test and gets printed.
ESP32 Uno vs UNO R4 for This Project
Moving this debounce project from the UNO R4 to the ESP32 Uno changes only the pin numbers and the logic level.
Item
Arduino UNO R4
ESP32 Uno
Logic voltage
5V
3.3V
Single button pin in code
7 (D7)
14 (D7)
Three-button pins in code
6, 7, 8
27, 14, 12
D8 at boot
Normal pin
Strapping pin, must stay LOW
Debounce logic and ezButton
Same code
Same code
Arduino IDE board
Arduino UNO R4 WiFi or Minima
ESP32 Dev Module
The buttons go into the same header holes, so the circuit stays the same. Just never pull D8 HIGH at power-on.
Go Wireless: See Button Presses on Your Phone
This part gives you an idea for taking the debounced button beyond the USB cable.
The ESP32 Uno has WiFi and Bluetooth (Classic and BLE) built in. A clean, debounced press is a good event to send to a phone: show the button state on a web page, log each press with a time, or use one button to switch something in a web app. Without debounce, the phone would get a burst of fake events for every tap.
Good to know: web and Bluetooth libraries need loop() to run often. Both debounce methods on this page never call delay(), so they fit well next to a wireless library.
Troubleshooting
Problem
Possible Cause
Solution
No COM port shows up in Arduino IDE
CH340 driver not installed or a charge-only cable
Install the CH340 driver and use a USB cable that carries data
Upload stops at "Failed to connect" or "Waiting for download"
Board did not enter download mode
Hold the BOOT button while the upload starts, then release it
Board will not boot or upload with three buttons wired
Something pulls D8 (GPIO12) HIGH at startup
Make sure button 3 goes to GND only, or unplug the D8 wire while uploading
No lines appear when you press
Button leg not on D7 or not on GND
Put one leg on D7 (GPIO14) and the other on the GND rail
Lines appear with nobody touching the button
Floating pin from a loose jumper
Push the wires in firmly and keep INPUT_PULLUP in pinMode()
Doubled lines even with the debounce sketch
Button bounces longer than 50 ms
Raise DEBOUNCE_TIME or setDebounceTime() to 80 or 100 ms
Quick taps are lost
Debounce time longer than your press
Lower the value to 20 or 30 ms
Sketch uses pins 6, 7 and 8 and nothing works
Old Uno pin numbers left in the code
Use 27, 14 and 12, the GPIO numbers for D6, D7 and D8
Compile error about ezButton.h
Library missing
Install ezButton from Library Manager
FAQ
What is contact bounce in a push button?
It is the short burst of on-off changes that happens when the metal parts inside a button touch or separate. It lasts a few milliseconds, which is too fast for you to notice but slow enough for the ESP32 Uno to read many times.
Can a capacitor debounce the button instead of code?
Yes. A small capacitor (around 100 nF) from the pin to GND smooths the edge. Code is simpler, costs nothing and is easy to tune, so most projects use the software way shown here.
Is the ezButton library better than the millis() sketch?
They use the same idea. The hand-written sketch shows how the timer works. ezButton hides that code, gives one timer per button and adds handy functions like isPressed() and getCount().
Does debouncing slow down my ESP32 Uno sketch?
No. Neither method uses delay(). The code only compares two numbers in each loop pass, so the rest of your sketch keeps running at full speed.
Which GPIO number do I type for D7 on the ESP32 Uno?
Type 14. The ESP32 Uno code uses GPIO numbers, not the Uno labels, so D6 is 27 and D8 is 12. Typing 7 would point at a chip pin that is not on the D7 hole.
Why is D8 a risky pin on this board?
D8 is GPIO12, which the chip reads at boot to set the flash voltage. If it is HIGH at that moment, the board does not start. A button to GND is fine, but a module or resistor that pulls D8 up can stop the board from booting.
Can I wire a button to A2, A3, A4 or A5?
Yes, but these pins (GPIO35, 34, 36, 39) are input only and have no internal pull-up. INPUT_PULLUP does nothing on them, so add a 10k ohm resistor from the pin to 3.3V. The digital header pins avoid that extra part.
Does the UNO R4 debounce code run on the ESP32 Uno unchanged?
The logic does, and ezButton supports both boards. You only change the pin numbers, for example 7 becomes 14. The UNO R4 uses 5V logic, while ESP32 Uno pins take 3.3V only.
Do I need to install a driver for the ESP32 Uno?
Many computers need the CH340 driver, because this board uses a CH340 USB-to-serial chip. If no COM port appears after you plug it in, install the driver and try again.
Can I count how many times the button was pressed?
Yes. With ezButton, call button.setCountMode(COUNT_FALLING) in setup() and read button.getCount() in the loop. Thanks to the debounce, each press adds exactly one.
What if I need more button pins than the Uno headers give?
The ESP32 Uno has a few extra holes near the power header: GPIO32 and GPIO33 (I/O, PWM, analog on ADC1, so they also work while WiFi is on) and GPIO15 (I/O, PWM, analog on ADC2). Add pin headers to those holes and put the bare GPIO number in your sketch. For extra buttons, GPIO32 and GPIO33 are the best choice, since they have internal pull-ups. Two warnings: GPIO15 and GPIO0 (the IO0 pin at the unused Uno NC position) are boot strapping pins, and a wrong connection can stop the board from booting or uploading code, so use GPIO32/33 first. Never use the six holes under the digital header (SD0 to SD3, CMD, CLK = GPIO6 to GPIO11); they are wired to the flash chip and using them crashes the board.
Please feel free to share the link of this tutorial. However, Please do not use our content on any other websites. We invested a lot of effort and time to create the content, please respect our work!