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.
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.
Recommended: 10 kΩ + 100 nF (code 104)
τ = 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
- Connect 3.3V → 10 kΩ resistor → GPIO 17
- Connect 100 nF capacitor between GPIO 17 and GND
- Connect push button between GPIO 17 and GND
- 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
- 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.
- No pull-up resistor. Without it, the GPIO floats when the button is open — random readings.
- 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.
- 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.