Raspberry Pi Push Button

Learn to read the state of a physical push button with gpiozero's Button class, understand why the internal pull-up resistor means you don't need an extra resistor for a simple button, and compare polling with event-driven callbacks.

Input vs. Output

So far, the Pi has been driving pins - deciding whether each one outputs 3.3V or 0V. A button reverses that relationship: the Pi reads a pin, and it's the physical state of the switch that decides what voltage the Pi sees, high when the button's circuit is open and low when it's closed, or vice versa depending on the wiring. Reading input correctly requires understanding not just the switch, but how the pin is 'biased' in between presses.

Wiring a Push Button

A typical push button wiring is simple: connect one leg of the button to a GPIO pin, and the other leg to GND. When the button is pressed, it closes the circuit between the pin and ground. Unlike an LED circuit, you don't need an external resistor here - gpiozero enables the GPIO's internal pull-up resistor for you by default, which gently holds the pin at logic HIGH when the button is not pressed, so it reads a clean LOW only when you actually press it and complete the connection to ground.

Note: gpiozero's `Button` class defaults to `pull_up=True`. The class abstracts the underlying wiring so that `is_pressed` is always `True` when the button is physically pressed, regardless of whether you're using the internal pull-up (button wired to GND) or pull-down (button wired to 3.3V) configuration - you don't need to remember which way the voltage logic runs.

The Button Class

Reading a button with a polling loop

from gpiozero import Button
from time import sleep

button = Button(2)  # GPIO2 to one leg, GND to the other; internal pull-up handles the rest

while True:
    if button.is_pressed:
        print("Button is being pressed")
    else:
        print("Button is not pressed")
    sleep(0.2)
  • `button.is_pressed` - a read-only property that's True exactly while the button is held down
  • `button.wait_for_press()` - blocks execution until the button is pressed
  • `button.wait_for_release()` - blocks execution until the button is released
  • `button.when_pressed` - assign a function here to run automatically on every press
  • `button.when_released` - assign a function here to run automatically on every release

Event-Driven Handling

Polling in a loop works, but it wastes CPU time checking the button dozens of times a second whether or not anything changed, and it can miss very short presses if `sleep()` is too long. Assigning a function to `when_pressed`/`when_released` lets gpiozero notify your code only when the state actually changes, which is both more efficient and more responsive.

Combining a button and an LED with events

from gpiozero import LED, Button
from signal import pause

led = LED(17)
button = Button(2)

button.when_pressed = led.on
button.when_released = led.off

pause()  # keeps the program alive so the callbacks can keep firing
ApproachProsCons
Polling (`is_pressed` in a loop)Simple to understand; easy to combine with other periodic checksWastes CPU cycles; can miss very brief presses if the loop is too slow
Event-driven (`when_pressed`/`when_released`)Efficient; responds immediately to state changesRequires understanding callbacks; program must stay alive (e.g. with `pause()`)