Sitemap
The Zarth Dev

The Zarth Dev

If not in front of my computer, I am procrastinating.

Understanding Switch Debouncing

6 min readJul 18, 2026

--

Press enter or click to view image in full size
Title image

Mechanical switches do not transition from ON to OFF in an instant. Due to the mechanical properties of the internals like springiness of the internal component, the contacts actually bounce between ON and OFF states for few milliseconds and the input pin rapidly toggles HIGH/LOW in that period (which looks like this on oscilloscope) -

This is how a signal bounce looks

Microcontroller, that runs a loops many times each second, sees it as multiple button presses instead of one. Which gives a lot of flickering which is not ideal.

There are two ways to fix this issue — via software and via hardware.

Software debouncing

The Software fix is the most easiest one to implement and there are two ways of doing this -

1. Using delays

We pause the program’s execution for some time (about 20 ms) after button press to wait out the bounce.

// Software Debounce in Arduino framework using delay
if (digitalWrite(BUTTON_PIN) == LOW) {
delay(20);
....
}

It is the simplest to implement but since it blocks whole microcontroller, it is not a good process. We cannot allow our microcontroller to sit idly when it is tasked with handling important input data like motor control, sensor communication etc.

2. Timestamp-based debounce

In this method we do not block everything using delay(). We keep a timestamp of the last time the signal (like our button press) changed and we do not consider any other changes until a certain defined time period has passed -

// Software Debounce in Arduino framework using timestamps
unsigned long last_debounce = 0;
unsigned long debounce_delay = 20; // timeperiod where we have to wait

uint8_t button_state;
uint8_t last_reading = HIGH;

void loop() {
uint8_t reading = digitalRead(BUTTON_PIN);

if (reading != last_reading) {
// detect change to record the timestamp
last_debounce = millis(); //reset the timer
}

if((millis() - last_debounce) > debounce_delay) {
// if we have waited for more than the time period
if (reading != button_state){
button_state = reading; //update the state
if (button_state == LOW) {
// confirm the event
....
}
}
last_reading = reading;
}

This method waits out the debounce period but it does not stall anything else.

Get The Zarth Dev’s stories in your inbox

Join Medium for free to get updates from this writer.

At pro level we use timers and Interrupt Service Routines — a function that runs automatically when an interrupt is fired. It works in following way -

  1. We set up a condition to generate an interrupt using GPIO pin, a timer, a UART etc
  2. We write a handler function for that interrupt — it is the Interrupt Service Routine.
  3. Now when the button was pressed (as in our example) -Microcontroller pauses whatever the main program was doing and it saves its current state on the stack, jumps to the ISR function and runs it after ISR is done, the saved states are restored from the stack and main program resumes from where it stopped.

There are some rules for the ISR function —

  1. They must be short
  2. It should not have any blocking calls (like delay() or millis()) or long loops etc
  3. All variables shared between ISR and main code must be using volatile type qualifier.
  4. The function should not do any heavy work, it should be minimal.

Hardware Debouncing

In this approach we use electrical components to fix the bouncing. We do it when out Microcontroller is already busy with important stuff. There are two methods of doing this debouncing as well -

1. RC Low-Pass filter

A resistor and capacitor are placed on the button’s signal line to smooth out the fast, noisy voltage spikes created by the bounce.

Low Pass RC filter

The capacitor charges and discharges through the resistor as per this formula -

Formula for Instantaneous voltage over time

A RC filter only passes signal changes with periods long relative to τ — the time period. This is formally referred to as the Cutoff frequency of the filter denoted by -

Formula for cutoff frequency

Any input signal with frequency below this frequency is allowed to pass while rest are blocked. Since the bounce happens very quickly, its frequency is higher than the cutoff frequency and the circuit attenuates them. Its output looks like this -

Design procedure for a RC filter -

  • Choose τ larger than the expected bounce duration. Since a typical bounce lasts for about 30 ms, the frequency would be about 5.3 Hz.
  • Pick up ceramic capacitors and resistors with common values like 0.1µF, 0.01µF etc
  • Use it to find the value of resistor -

A common practical rule:

It takes about 5τ for the capacitor to settle to within ~1% of its final value. This total settle time is what determines your effective “debounce delay” at the hardware level, and is the number you’d compare against your switch’s datasheet bounce spec.

Interfacing with a GPIO -

Because the output from the filter is like a ramp, it risks multiple triggers, so we use a Schmitt-trigger input that guarantees a clean transition since the input will not switch state until the ramp fully crosses the appropriate threshold and wont switch back until it crosses other one.

2. Debounce ICs/ Schmitt trigger buffers -

As mentioned before, the output of a RC filter looks like a ramp and it reacts multiple triggers. Schmitt trigger (like 74HC14 or 74HC7014) or dedicated debounce IC solves this issue. They either clean up the already filtered signal or handle the entire bounce problem in hardware without needing a RC filter at all.

Schmitt Trigger

A standard input has a single threshold, if the voltage is above it, we say the pin is HIGH otherwise if voltage is below that threshold, we say that the pin is LOW. But a Schmitt trigger has 2 thresholds -

  • Upper Threshold (V_T+) — if input is above it, it is HIGH
  • Lower Threshold (V_T-) — if input below it, it is LOW
  • A Hysteresis band which is the gap between them

Once the output is HIGH, it remains High until the input drops all the way to below V_T- and to become HIGH, the input must spike above V_T+. It will not flip back for any value that lies in the hysteresis band, this suppresses the bounce.

Many microcontrollers (like STM32 already has Schmitt-triggers built into their input buffers, eliminating the need for an external component.

Dedicated Debounce IC like MAX6816. MAX6817, MAX6818 contain internal timing logic purpose built for switch debouncing and output a clean logic transition with no external components needed. We have to wire the raw switch directly into the IC input and the output is a bounce free digital signal directly usable by a GPIO — no extra steps needed.

Switch bouncing is a fundamental issue in digital electronics, but it’s also one of the easiest to solve. We should choose a solution based on out application -

For most hobbyist and Arduino-based projects, a well-implemented software debounce offers is sufficient, but in performance-critical or high-reliability applications (industrial controls, medical devices, automotive), hardware debouncing especially Schmitt-trigger inputs become the preferred choice.

Apart from switches, the principles you learned here could solve similar noise issues in sensors, encoders, and relays too.

--

--