raspberry-pi beginner 30 min

Raspberry Pi: GPIO with Python and gpiozero, the right way

Wire an LED and a button to a Raspberry Pi's GPIO and control them from Python using gpiozero. The simplest way to get started with physical computing on the Pi.

Code available for: Python
Published Aug 26, 2026

A Raspberry Pi is a Linux computer with a bunch of pins on the header that you can read and write from Python. That is the whole “physical computing” pitch. You wire an LED to a pin, you write a Python script, the LED turns on.

The trick is doing it without frying the Pi. GPIO pins run at 3.3V and have a current limit of about 16 mA each. A short circuit will not immediately stop working, but it will slowly cook the pin, and on a bad day, the SoC. I have killed a Pi this way. (I will tell you about it in the cautionary tale section.)

This tutorial uses gpiozero, which is the right library for almost every Pi GPIO project.

What you need

  • Raspberry Pi (any model with the 40-pin header, i.e. Pi 2 and newer)
  • An LED (any color)
  • A 220 ohm or 330 ohm resistor
  • A momentary pushbutton
  • A small buzzer (the active 3-pin type)
  • Three jumper wires

RPi.GPIO vs gpiozero: pick gpiozero

There are two main Python GPIO libraries. The older one, RPi.GPIO, is the one most tutorials reference. It works, but it requires you to set up pins, configure pull-up resistors, and clean up at exit. It feels like programming a microcontroller from the 1990s.

gpiozero is the modern one. It does the right thing by default: the LED knows it is an LED (so it handles on/off correctly), the button knows it is a button (so it handles pull-ups correctly). You write led.on() instead of led.write(1).

The rule: use gpiozero unless you have a specific reason to need RPi.GPIO (e.g. you are porting old code, or you need software PWM that gpiozero does not support). Most projects are simpler with gpiozero.

from gpiozero import LED, Button

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

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

That is a working button-controlled LED in 5 lines.

Install gpiozero

gpiozero is part of Raspberry Pi OS, so on a fresh install you already have it. If you are on a stripped-down image:

sudo apt install -y python3-gpiozero

That is it. No pip install (the apt version is the one to use; it matches the system’s libgpiod).

The “hello world” of Pi GPIO: LED + button

Wire this:

Wire key: GPIOGND
ComponentPin (header)GPIO
LED anode (long leg)Pin 11GPIO 17
LED cathode (short leg)through 220R to GNDPin 6 (GND)
Button leg 1Pin 6 (GND)GND
Button leg 2Pin 3GPIO 2
GPIO 17 ----[ LED ]----[ 220R ]---- GND
GPIO 2  ----[ BUTTON ]---- GND

Now the script:

from gpiozero import LED, Button
from signal import pause

led = LED(17)
button = Button(2, pull_up=True)

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

pause()

pause() blocks forever, but in a way that lets gpiozero’s internal thread handle the button events. This is the right way to write a long-running Pi GPIO script.

Press the button: LED on. Release: LED off.

Pin numbering: gpiozero uses BCM numbering, not physical pin numbers. GPIO 17 is physical pin 11. The pinout at https://pinout.xyz is the canonical reference.

Internal pull-ups

A button has two states: pressed and not pressed. From the Pi’s point of view, those should be two different voltages. Without help, the “not pressed” state is a floating input, which reads as random noise (sometimes high, sometimes low, occasionally flickering as you wave your hand near the wire).

A pull-up resistor ties the input to 3.3V by default, so “not pressed” reads as high (1), and “pressed” pulls it to GND (0). gpiozero turns on the internal pull-up by default with Button(pin, pull_up=True), which is what you want for a button wired between GPIO and GND.

If you wire the button between GPIO and 3.3V instead, use pull_up=False (or equivalently, pull_down=True).

The internal pull-ups are about 50k ohms. For long wires or noisy environments, add an external 10k pull-up. For typical breadboard projects, the internal one is enough.

The LED + buzzer + button combo

Adding a buzzer to the previous example. Wire the buzzer’s signal pin to GPIO 27 (pin 13 on the header), and the other two to GND and 3.3V.

from gpiozero import LED, Button, Buzzer
from signal import pause

led = LED(17)
buzzer = Buzzer(27)
button = Button(2, pull_up=True)

def alert():
    led.on()
    buzzer.on()

def quiet():
    led.off()
    buzzer.off()

button.when_pressed = alert
button.when_released = quiet

pause()

Press the button: LED on, buzzer on. Release: both off. This is the pattern for a “doorbell” or “alarm” project.

The pin factory (Pi 5 vs Pi 4)

gpiozero has a “pin factory” abstraction so you can switch between the underlying GPIO library without changing your code. The default is lgpio on a Pi 5, and RPi.GPIO on a Pi 4. Most users never notice the difference.

If you are on a Pi 5 and your code raises ImportError: No module named 'RPi.GPIO', install the new pin factory:

sudo apt install -y python3-lgpio python3-rpi-lgpio

The Pi 5 also has a different pinout for the PoE header, but the main 40-pin GPIO header is the same as the Pi 4. Your code does not need to change.

The “I broke my Pi” cautionary tale

I shorted 3.3V to GND through a misplaced jumper wire while the Pi was running. The Pi did not shut off. It kept running. Two days later, I noticed one of the GPIO pins was not reading correctly. A week later, the entire Pi froze and never came back.

The lesson: a short circuit is not dramatic. It is a slow death. The Pi does not have the same protection as a microcontroller.

Things that protect you:

  • A 220 ohm resistor in series with every LED. Not optional. The LED will draw too much current otherwise.
  • A 1k resistor in series with inputs that could see more than 3.3V.
  • Never drive a GPIO output to a motor or relay coil directly. Use a transistor or a driver board. The GPIO can source 16 mA, the motor wants 200 mA, the GPIO dies.
  • Wire the circuit before powering the Pi. Easier to debug, easier to undo a mistake.

Things that will not protect you:

  • The Pi’s polyfuse (only protects the 5V rail, not the GPIO).
  • Linux. Linux is not in the loop for GPIO current limits.

When to use the Pi vs a microcontroller

The Pi is overkill for blinking an LED. A Pico or ESP32 will do it for $4, with no OS, no SD card to corrupt, and instant boot.

The Pi is the right choice when:

  • You need a real OS (file system, network stack, video, audio).
  • You are running services (Home Assistant, MQTT broker, Pi-hole).
  • You need to talk to a peripheral that needs drivers (a camera, a USB device, an HDMI display).
  • You are already running a Pi and the GPIO is a small add-on.

The microcontroller is the right choice when:

  • The project is one sensor and one actuator.
  • Boot time matters.
  • You do not want a full Linux install to maintain.

Rule of thumb: if the project’s “main thing” is the GPIO (a robot, a sensor node, a light controller), use a microcontroller. If the GPIO is an add-on to a service the Pi is already running, use the Pi’s GPIO.

What you learned

  • gpiozero is the right library for almost every Pi GPIO project.
  • Pull-up resistors make button inputs stable.
  • Current limits matter: 16 mA per pin, 3.3V logic.
  • The Pi is overkill for blinking LEDs. A microcontroller is cheaper and more reliable for that.

When something breaks

RuntimeError: No access to /dev/gpiochip0. You forgot sudo, or the user is not in the gpio group. Add the user: sudo usermod -aG gpio $USER and log out and back in.

The button reads as “always pressed”. Wiring issue. The button is between GPIO and 3.3V instead of GPIO and GND, so the pull-up is fighting the input. Swap the wiring, or change pull_up=True to pull_up=False.

The LED is very dim. The resistor is too big. 220 ohm is the standard; 1k or higher is for low-current indicator LEDs.

The Pi is unresponsive after wiring a motor. You drew too much current from a GPIO. The Pi is probably still alive, but that pin is dead. Use a transistor or a motor driver board next time.

ImportError: No module named 'gpiozero'. The package is not installed. Run sudo apt install -y python3-gpiozero.

What to build next

  • A traffic light (3 LEDs, timed sequence).
  • A doorbell (button + buzzer, no LEDs).
  • A simple motion alarm (PIR sensor + buzzer + LED).
  • A relay-controlled lamp (with a 5V relay module, not a bare relay).

The relay module project is the one most people want to do next. The traffic light is the one most fun to build first.