Arduino MKR WiFi 1010 - Button - Debounce
Welcome to this essential guide on button debouncing with the Arduino MKR WiFi 1010! If you've ever experienced unreliable button behavior—where one press registers as multiple presses—this tutorial will solve that frustrating problem.
Here's a question: When you press or release a button once, does its electrical state change exactly once from LOW to HIGH (or HIGH to LOW)?
You might think: Yes, one press = one state change.
The reality: No! In the physical world, mechanical buttons don't make clean transitions. When you press a button, the metal contacts inside bounce against each other multiple times before settling. This happens incredibly fast—within milliseconds—but microcontrollers like the Arduino are fast enough to detect each bounce as a separate press!
This phenomenon is called chattering or bounce, and it causes serious problems:
A single button press might be detected as 3, 5, or even 10 presses
Toggle functions behave erratically
Counters increment incorrectly
User interfaces become frustrating and unreliable
The solution is called debouncing—a technique to filter out these false signals and detect only genuine button presses.
What You'll Learn:
How to debounce a single button using manual timing code
How to debounce one button using the ezButton library (simpler!)
How to debounce multiple buttons efficiently with the library
Understanding the timing principles behind debouncing
Or you can buy the following kits:
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 .
Before tackling debouncing, you should understand basic button operation with Arduino. If you're new to buttons, we highly recommend starting with our foundational button tutorial.
Prerequisite Tutorial: Arduino MKR WiFi 1010 - Button tutorial
This foundational guide covers:
How push buttons work electrically
Pull-up and pull-down resistors
Reading button states with digitalRead()
Basic button wiring to Arduino pins
Understanding pressed vs released states
Once you're comfortable reading button states, debouncing becomes a straightforward extension—you're just adding timing logic to filter out noise.
Connect a button to your Arduino MKR WiFi 1010 as shown. This simple circuit will let us demonstrate the chattering problem and its solution.

This image is created using Fritzing. Click to enlarge image
Our Testing Strategy: To clearly demonstrate why debouncing is necessary, we'll run two experiments:
Without Debouncing: You'll see the chattering problem in action
With Debouncing: You'll see the clean, reliable button detection
Comparing these two approaches will make the value of debouncing crystal clear!
Let's first see the problem in action. This code reads the button state directly without any debouncing. Watch what happens!
New to Arduino MKR WiFi 1010? Complete our Getting Started with Arduino MKR WiFi 1010 tutorial first to set up your development environment.
Connect the components to the Arduino MKR WiFi 1010 board as depicted in the diagram
Plug your Arduino MKR WiFi 1010 into your computer's USB port
Launch the Arduino IDE on your computer
Select the Arduino MKR WiFi 1010 board and its COM port
Copy the code below and paste it into the Arduino IDE
#define BUTTON_PIN 2
int prev_state = LOW;
int button_state;
void setup() {
Serial.begin(9600);
pinMode(BUTTON_PIN, INPUT_PULLUP);
}
void loop() {
button_state = digitalRead(BUTTON_PIN);
if (prev_state == HIGH && button_state == LOW)
Serial.println("The button is pressed");
else if (prev_state == LOW && button_state == HIGH)
Serial.println("The button is released");
prev_state = button_state;
}
Click the Upload button to compile and upload
Open the Serial Monitor
Press the button and hold it for a few seconds, then release
Observe the results on the Serial Monitor
The button is pressed
The button is pressed
The button is pressed
The button is released
The button is released
What Happened? You pressed the button once and released it once, but the Serial Monitor shows multiple press and release events! This is chattering in action.
Why This Happens: When the button contacts touch, they don't make a solid connection immediately. They bounce apart and together several times within 5-50 milliseconds. Your Arduino, running at millions of cycles per second, detects each bounce as a separate state change.
※ NOTE THAT:
Testing Note: Chattering doesn't occur consistently—it depends on how you press the button, the button's mechanical quality, and even environmental factors like temperature. If you don't observe chattering on your first try, test several times with different pressing speeds and pressures. You'll likely see it appear.
Now let's solve the problem! This code implements debouncing using millis() timing. It ignores state changes that happen within a short time window (typically 50ms), filtering out the bounces.
#define BUTTON_PIN 2
#define DEBOUNCE_TIME 50
int prev_state_steady = LOW;
int prev_state_flick = LOW;
int button_state;
unsigned long last_debounce_time = 0;
void setup() {
Serial.begin(9600);
pinMode(BUTTON_PIN, INPUT_PULLUP);
}
void loop() {
button_state = digitalRead(BUTTON_PIN);
if (button_state != prev_state_flick) {
last_debounce_time = millis();
prev_state_flick = button_state;
}
if ((millis() - last_debounce_time) > DEBOUNCE_TIME) {
if(prev_state_steady == HIGH && button_state == LOW)
Serial.println("The button is pressed");
else if(prev_state_steady == LOW && button_state == HIGH)
Serial.println("The button is released");
prev_state_steady = button_state;
}
}
Click the Upload button to compile and upload
Open the Serial Monitor
Press and hold the button for a few seconds, then release
Observe the results on the Serial Monitor
The button is pressed
The button is released
Success! Now you see exactly one press event and one release event—matching your physical button action perfectly. The false signals have been eliminated!
How Debouncing Works: The code uses millis() to track time. When it detects a state change, it waits 50 milliseconds before checking again. During this "debounce period," any additional state changes are ignored. After 50ms, the bouncing has settled, and the button is in its true stable state.
The 50ms delay is:
Long enough to let mechanical bouncing settle
Short enough that users don't perceive any lag
A good default for most buttons (adjust if needed for specific hardware)
While the manual millis() approach works, implementing debouncing for multiple buttons gets complex quickly. You need separate timing variables for each button, careful state tracking, and lots of repetitive code.
Enter the ezButton Library: We created this library specifically to make button handling simple and reliable, especially for beginners and multi-button projects.
Why Use ezButton?
Automatic Debouncing: Built-in debounce handling—no timing code needed
Clean Syntax: Simple, readable methods like isPressed() and isReleased()
Multiple Buttons: Easily manage many buttons without complexity explosion
Additional Features: Press duration, count presses, state changes, and more
Proven and Tested: Used in thousands of projects worldwide
Learn more: Arduino Button Library Tutorial
Here's how simple it becomes with ezButton:
#include <ezButton.h>
#define DEBOUNCE_TIME 50
ezButton button(D2);
void setup() {
Serial.begin(9600);
button.setDebounceTime(DEBOUNCE_TIME);
}
void loop() {
button.loop();
if (button.isPressed())
Serial.println("The button is pressed");
if (button.isReleased())
Serial.println("The button is released");
}
Handling multiple buttons is where ezButton really shines. Instead of managing separate timing variables for each button, you simply create multiple button objects. The library handles everything!
Example: Let's debounce three buttons simultaneously.

This image is created using Fritzing. Click to enlarge image
#include <ezButton.h>
#define DEBOUNCE_TIME 50
ezButton button1(D6);
ezButton button2(D7);
ezButton button3(D8);
void setup() {
Serial.begin(9600);
button1.setDebounceTime(DEBOUNCE_TIME);
button2.setDebounceTime(DEBOUNCE_TIME);
button3.setDebounceTime(DEBOUNCE_TIME);
}
void loop() {
button1.loop();
button2.loop();
button3.loop();
if (button1.isPressed())
Serial.println("The button 1 is pressed");
if (button1.isReleased())
Serial.println("The button 1 is released");
if (button2.isPressed())
Serial.println("The button 2 is pressed");
if (button2.isReleased())
Serial.println("The button 2 is released");
if (button3.isPressed())
Serial.println("The button 3 is pressed");
if (button3.isReleased())
Serial.println("The button 3 is released");
}
The optimal DEBOUNCE_TIME value depends on your specific hardware:
Typical Range: 20-50ms works for most buttons
High-Quality Buttons: May need only 10-20ms
Cheap/Old Buttons: Might require 50-100ms
Switches: Toggle and slide switches often need longer (50-100ms)
How to Find Your Value: Start with 50ms. If you still see occasional double-triggers, increase it in 10ms increments until behavior is reliable.
Debouncing isn't just for buttons! These devices also benefit from debouncing:
Toggle/Slide Switches: Any mechanical switch can chatter
Limit Switches: Common in CNC and robotics
Reed Switches: Magnetic sensors used in door/window sensors
Touch Sensors: Can have electrical noise requiring debouncing
The same debouncing techniques apply to all these devices—they're all reading digital inputs that can have noisy transitions!