Raspberry Pi Debouncing a Button

Understand switch bounce - the rapid, unintended on/off chatter a mechanical button produces for a few milliseconds when pressed - and fix it with either a manual software delay or gpiozero's built-in bounce_time parameter.

What Is Switch Bounce?

Inside a mechanical push button, pressing it snaps two metal contacts together. Those contacts don't touch cleanly on the first try - they physically bounce apart and together several times in rapid succession before finally settling into a stable closed connection. This happens in a tiny window, typically somewhere between one and a few tens of milliseconds, but a Raspberry Pi reads GPIO state far faster than that, so it can easily register that single bounce sequence as several separate presses and releases rather than one clean press.

Note: Bounce duration varies by switch quality and even by how hard or fast you press it - cheap tactile buttons often bounce for longer than a well-made mechanical switch. There's no universal number, which is exactly why debouncing needs to be handled in software rather than assumed away.

How Bounce Breaks Event-Driven Code

The problem becomes obvious as soon as you attach a counter to `when_pressed`. A single, deliberate press of the button can fire the callback two, three, or more times within a few milliseconds, because gpiozero is faithfully reporting every rapid HIGH/LOW transition the bouncing contacts produce - it has no way to know the difference between rapid switch chatter and someone pressing a button several times deliberately.

The bug: overcounting from bounce

from gpiozero import Button
from signal import pause

button = Button(2)  # no debounce configured
count = 0

def on_press():
    global count
    count += 1
    print(count)  # a single physical press can print 2, 3, or more times here

button.when_pressed = on_press
pause()

Fixing It Two Ways

There are two reliable ways to handle this. The manual approach records a timestamp the first time the button fires, and ignores any further presses that happen within a short window afterward - effectively treating rapid re-triggers as noise from the same physical press. The built-in approach lets gpiozero do this for you by passing a `bounce_time` value (in seconds) when creating the `Button`, which tells it to ignore any state changes that happen within that window after a detected edge.

Manual delay debounce with a timestamp

from gpiozero import Button
from time import monotonic
from signal import pause

button = Button(2)
count = 0
last_press = 0
MIN_INTERVAL = 0.2  # seconds - ignore repeats faster than this

def on_press():
    global count, last_press
    now = monotonic()
    if now - last_press > MIN_INTERVAL:
        count += 1
        last_press = now
        print(f"Press #{count}")

button.when_pressed = on_press
pause()

Using gpiozero's built-in bounce_time

from gpiozero import Button
from signal import pause

button = Button(2, bounce_time=0.1)  # ignore glitches within 0.1s of a detected edge
presses = 0

def count_press():
    global presses
    presses += 1
    print(f"Press #{presses}")

button.when_pressed = count_press
pause()
ApproachHow it worksWhen to use it
Manual timestamp debounceYour code tracks the time of the last accepted press and ignores anything faster than a thresholdWhen you need custom logic per press, or want debounce behavior that varies during the program
`bounce_time` parametergpiozero ignores any pin transitions within the given window after each detected edge, before your callback ever runsThe default choice for most projects - simpler, and handled at the library level
Note: There's no single correct `bounce_time` - values between 0.02 and 0.1 seconds (20-100ms) work well for most tactile buttons. Set it too low and bounce slips through; set it too high and you might miss genuinely fast, deliberate presses. Test with your actual hardware and adjust.

Exercise: Raspberry Pi Digital Input

What happens to a digital input pin's reading if it is left "floating" with no pull resistor connected?