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.