RC Filters and Debouncing for Raspberry Pi GPIO

Use RC circuits to debounce buttons and filter noise on Raspberry Pi GPIO inputs. Covers time constant calculations, component selection, and Python code.

Andreas · April 16, 2026 · 8 min read

Introduction

Push a button on a breadboard, and the Raspberry Pi sees dozens of rapid on-off transitions before the contact settles. This is called bounce, and it turns a single press into multiple false triggers. An RC filter — just a resistor and a capacitor — smooths the signal and eliminates bounce entirely in hardware. No software debouncing needed.

What is switch bounce?

A mechanical switch doesn't make clean contact. The metal surfaces literally bounce against each other for 1–10 milliseconds. An oscilloscope shows a jagged series of high/low transitions before the signal stabilizes.

Button press:
HIGH ──┐ ┌─┐ ┌─┐ ┌──────────────
       │ │ │ │ │ │
LOW    └─┘ └─┘ └─┘

With RC filter:
HIGH ──┐
       │    ╭──── gradual transition
LOW    └────╯────────────────────

Without debouncing, a GPIO interrupt fires on every bounce — one button press triggers 5–50 events instead of one.

The RC low-pass filter

An RC circuit slows down voltage changes. Connect a resistor in series with the signal, and a capacitor from the signal to ground:

Button ──[ R ]──┬── GPIO input
                │
              [ C ]
                │
              GND

When the button is pressed, the capacitor charges through R. The voltage at the GPIO pin rises gradually instead of instantly. Bounce pulses are too fast and too short to charge the capacitor — they get filtered out.

Time constant

τ = R × C

The time constant τ is the time for the capacitor to charge to 63.2% of the supply voltage. After 5τ, the voltage reaches 99.3% — effectively fully charged.

For debouncing, you want τ to be longer than the bounce period (typically 5–10ms) but short enough that the response feels instant to the user.

Target: τ = 10–50 ms

The RC time constant calculator computes τ for any R and C combination, plus the time to reach custom voltage percentages.

Choosing components

Option 1: 10 kΩ + 1 µF

τ = 10,000 × 0.000001 = 10 ms

Bounce filtered in about 50 ms (5τ). Response feels instant. This is the go-to combination for GPIO debouncing.

Option 2: 10 kΩ + 10 µF

τ = 10,000 × 0.00001 = 100 ms

Full charge in ~500 ms. This feels sluggish — there's a noticeable delay between pressing the button and the GPIO registering the change. Too slow for responsive UI.

Option 3: 1 kΩ + 10 µF

τ = 1,000 × 0.00001 = 10 ms

Same time constant as Option 1, but the lower resistance means more current flows when the button is pressed. At 3.3V with 1 kΩ, that's 3.3 mA — acceptable but higher than needed.

τ = 10,000 × 0.0000001 = 1 ms

This is faster than the standard recommendation, but with GPIO internal pull-ups and a clean read loop, it works well. The 5τ settling time is 5 ms — fast enough for responsive input, slow enough to filter most bounce.

Can't read the code on your capacitor? "104" = 100 nF. The capacitor decoder converts any 3-digit marking. For identifying the resistor, use the resistor color code decoder.

Complete debounced button circuit

Schematic

3.3V ──[10kΩ pull-up]──┬── GPIO 17 (input)
                        │
                      [100nF]
                        │
Button ─────────────────┤
                        │
                      GND

The 10 kΩ pull-up keeps the GPIO high when the button is open. When pressed, the button connects the GPIO to ground through the RC filter. The capacitor smooths the transition.

Wiring on breadboard

  1. Connect 3.3V → 10 kΩ resistor → GPIO 17
  2. Connect 100 nF capacitor between GPIO 17 and GND
  3. Connect push button between GPIO 17 and GND
  4. No additional components needed — the Pi's internal ESD protection handles the rest

Python code

import RPi.GPIO as GPIO
import time

BUTTON_PIN = 17

GPIO.setmode(GPIO.BCM)
GPIO.setup(BUTTON_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)

def button_pressed(channel):
    print(f"Button pressed at {time.time():.3f}")

# Edge detection — the RC filter means we only get one clean edge
GPIO.add_event_detect(
    BUTTON_PIN,
    GPIO.FALLING,
    callback=button_pressed,
    bouncetime=50  # Software backup: ignore edges within 50ms
)

try:
    print("Waiting for button presses (Ctrl+C to exit)...")
    while True:
        time.sleep(1)
except KeyboardInterrupt:
    GPIO.cleanup()

The bouncetime=50 parameter is a software backup. With the RC filter in place, it's rarely needed — but defense in depth costs nothing.

RC filters for sensor signals

Beyond debouncing, RC filters clean up noisy analog signals before they reach an ADC or comparator.

Filtering PWM to analog

If you're generating PWM on a GPIO pin and want a smooth analog voltage (e.g., for driving an analog meter), an RC low-pass filter converts the PWM to DC:

GPIO PWM output ──[10kΩ]──┬── Analog output (smoothed DC)
                           │
                         [10µF]
                           │
                         GND

With f_PWM = 1 kHz and τ = 0.1s, the cutoff frequency is fc = 1/(2πRC) = 1/(2π × 10,000 × 0.00001) = 1.6 Hz. The 1 kHz PWM is attenuated by ~56 dB — effectively pure DC.

Filtering sensor noise

Temperature sensors (DHT22, DS18B20) and other digital sensors occasionally produce glitchy readings. An RC filter on the data line with τ ≈ 1 µs (1 kΩ + 1 nF) smooths high-frequency noise without affecting the data rate.

Caution: Don't use too large a time constant on digital communication lines (I2C, SPI, UART). The filter will round the edges of the data signal, causing bit errors. For these protocols, keep τ well below the bit period.

Cutoff frequency

The RC filter's cutoff frequency (−3 dB point) is:

fc = 1 / (2π × R × C)

R C τ fc
10 kΩ 100 nF 1 ms 159 Hz
10 kΩ 1 µF 10 ms 15.9 Hz
1 kΩ 100 nF 0.1 ms 1.59 kHz
100 kΩ 10 nF 1 ms 159 Hz

For button debouncing, a cutoff of 15–160 Hz is ideal. Human button presses are 50–200 ms events (2–20 Hz), while bounce is 100 Hz–10 kHz. The filter passes the press but blocks the bounce.

Hardware vs. software debouncing

Approach Pros Cons
RC filter (hardware) Zero CPU usage, works with interrupts, deterministic Extra components, fixed time constant
Software debouncing No extra parts, adjustable in code Uses CPU cycles, can miss fast edges
Both Best reliability Slightly more complex

For production Raspberry Pi projects (kiosks, IoT devices, automation), always use hardware debouncing. Software debouncing in Python has timing jitter from the OS scheduler — events can be missed during garbage collection pauses.

Common mistakes

  1. Capacitor too large. A 100 µF cap with a 10 kΩ pull-up gives τ = 1 second. The button feels broken because it takes 5 seconds to fully register a press.
  2. No pull-up resistor. Without it, the GPIO floats when the button is open — random readings.
  3. RC on output pins. Don't put a filter on a GPIO configured as output. The capacitor fights the driver, slows transitions, and wastes current.
  4. Mixing signal types. An RC debounce circuit is for slow signals (buttons, switches). Don't use it on data buses, clock lines, or encoder outputs without understanding the timing budget.

Comments