To use GPIO pins on a Raspberry Pi in 2026, you wire your components to the 40-pin header using BCM (Broadcom) channel numbering, and control them via the gpiozero Python library backed by the lgpio pin factory. The transition to the Raspberry Pi 5 and its RP1 southbridge chip deprecated the legacy RPi.GPIO library, making lgpio the mandatory standard for reliable hardware control.

This guide walks through a complete, interrupt-driven button and LED circuit. We will cover the exact hardware required, the pin mapping, compilable Python code with robust error handling, and a decision-tree approach to debugging when your pins refuse to toggle.

The 2026 Standard: Choosing Your Pi Board and GPIO Library

Before touching a jumper wire, you must select the correct software stack. The Raspberry Pi 5 introduced the RP1 I/O controller, which fundamentally changed how the CPU talks to the GPIO header. If you are following tutorials from 2022 or earlier, they will likely fail on modern hardware.

Decision Path: Which GPIO Library Should You Use?
  • If you are on a Pi 4 or older AND maintaining legacy code: Use RPi.GPIO. (Not recommended for new projects).
  • If you need bare-metal C/C++ performance or custom PWM: Use pigpio via the lgpio C library bindings.
  • If you are building a new project on a Pi 4 or Pi 5 (Default Pick): Use gpiozero with the lgpio pin factory. It abstracts the hardware changes, handles cleanup automatically, and is officially supported by Raspberry Pi Ltd.

For this build, we are targeting the Raspberry Pi 5 (8GB variant) running Raspberry Pi OS (Bookworm 64-bit). The code and wiring are backward-compatible with the Pi 4B, provided you install the correct backend.

Hardware Build: Parts List and Pin Mapping

Do not rely on generic "starter kit" components without verifying their specs. The Pi 5 GPIO pins operate strictly at 3.3V logic levels and can source a maximum of 8mA per pin (with a total bank limit of 50mA). Exceeding this will permanently damage the RP1 chip.

Exact Parts List

ComponentSpecification / VariantEstimated Cost
MicrocontrollerRaspberry Pi 5 (8GB RAM)$80.00
LED5mm Diffused Red (Forward Voltage ~2.0V, 20mA max)$0.10
Current Limiting Resistor330Ω, 1/4W, 5% tolerance (Carbon or Metal Film)$0.02
Switch6x6mm Tactile Pushbutton (4-pin, SPST-NO)$0.05
WiringFemale-to-Female Dupont Jumper Wires (24 AWG)$3.00 / pack
PrototypingHalf-size solderless breadboard (400 tie points)$4.50

Pin Mapping Table

We use BCM (Broadcom) numbering in software, but you must count physical pins on the board. Pin 1 is the 3.3V pin closest to the SD card slot / USB-C power connector.

FunctionBCM Pin (Code)Physical Pin (Header)Wire Color Recommendation
LED Anode (+)GPIO 17Pin 11Green
LED Cathode (-)N/A (via Resistor)Pin 9 (GND)Black
Button SignalGPIO 27Pin 13Yellow
Button GroundN/APin 14 (GND)Black

Step-by-Step Wiring and Safety Verification

Always wire your circuits with the Pi powered down. The 5V pins on the Pi 5 header are directly tied to the USB-C power input and lack polyfuse protection on the GPIO bank.

  1. Place the LED: Insert the 5mm LED into the breadboard. Note the flat edge on the LED plastic lens—this is the cathode (negative). Insert the anode (long leg, positive) into row 10, cathode into row 11.
  2. Install the Resistor: Insert one leg of the 330Ω resistor into row 10 (sharing the anode hole). Insert the other leg into the negative (blue/black) ground rail. Note: Resistors are non-polarized, orientation does not matter.
  3. Wire the LED to Pi: Connect a female-to-female jumper from Physical Pin 11 (BCM 17) to row 10 (LED anode). Connect a jumper from Physical Pin 9 (GND) to the ground rail where the resistor terminates.
  4. Place the Button: Straddle the tactile button across the breadboard's center trench. Pins should be in rows 20 and 22.
  5. Wire the Button to Pi: Connect a jumper from Physical Pin 13 (BCM 27) to one side of the button (row 20). Connect a jumper from Physical Pin 14 (GND) to the opposite side of the button (row 22).
  6. Verify Continuity: Before applying power, use a multimeter in continuity mode. Place one probe on the Pi's GND pin and the other on the LED cathode. You should read a low resistance (approx. 330Ω). Press the button and verify continuity between BCM 27 and GND.

The Code: Compilable Python with Error Handling

This script targets Raspberry Pi OS Bookworm (64-bit). It uses gpiozero to set up an interrupt-driven button reader and an LED. It includes explicit error handling for the most common library and permission failures.

#!/usr/bin/env python3
"""
Raspberry Pi 5 GPIO Button & LED Controller
Target: Raspberry Pi 5 (8GB) / Pi 4B
OS: Raspberry Pi OS Bookworm 64-bit
Dependencies: gpiozero, rpi-lgpio
"""

import sys
import time

# 1. Attempt to import gpiozero and handle missing backend errors
try:
    from gpiozero import LED, Button
    from gpiozero.exc import BadPinFactory
    from signal import pause
except ImportError:
    print('FATAL: gpiozero is not installed. Run: sudo apt install python3-gpiozero')
    sys.exit(1)

def main():
    # Pin definitions (BCM numbering)
    LED_PIN = 17
    BUTTON_PIN = 27

    try:
        # Initialize hardware
        # pull_up=True uses the internal 50k pull-up resistor; button connects to GND
        led = LED(LED_PIN)
        button = Button(BUTTON_PIN, pull_up=True, bounce_time=0.05)
        
        print(f'Successfully initialized BCM {LED_PIN} (LED) and BCM {BUTTON_PIN} (Button).')
        print('Press the button to toggle the LED. Press Ctrl+C to exit.')

        # Define interrupt callbacks
        def on_button_pressed():
            led.toggle()
            state = 'ON' if led.is_lit else 'OFF'
            print(f'Button pressed! LED is now {state}.')

        # Bind callbacks
        button.when_pressed = on_button_pressed

        # Keep the script running to listen for interrupts
        pause()

    except BadPinFactory:
        print('FATAL ERROR: BadPinFactory detected.')
        print('The lgpio backend is missing. Fix this by running:')
        print('  sudo apt update && sudo apt install python3-rpi-lgpio')
        sys.exit(1)
    except RuntimeError as e:
        print(f'FATAL ERROR: Hardware access denied. {e}')
        print('Ensure you are not running conflicting GPIO scripts in the background.')
        sys.exit(1)
    except KeyboardInterrupt:
        print('\nInterrupt received. Cleaning up GPIO states...')
        # gpiozero handles cleanup automatically on exit, but we catch Ctrl+C gracefully
        sys.exit(0)

if __name__ == '__main__':
    main()

To run this script, save it as gpio_test.py and execute it via the terminal:

python3 gpio_test.py

Debugging: When Your GPIO Refuses to Toggle

When a circuit fails, do not immediately rewrite the code. 90% of GPIO failures on the Pi are physical or environment-level. Here is the first three things to check when your script runs but the hardware does nothing:

  1. Breadboard Rail Splits: Many half-size breadboards have a physical gap in the middle of the long power/ground rails (often indicated by a break in the red/blue printed lines). If your GND wire is on one side of the gap and your component is on the other, the circuit is open.
  2. LED Polarity and Resistor Seating: Ensure the flat edge of the LED is facing the GND rail. Push the resistor and LED legs firmly into the breadboard; loose Dupont connections on the Pi header can easily back out by 1mm, breaking contact.
  3. Library Backend Conflict: Run pip list | grep lgpio. If you have both RPi.GPIO and rpi-lgpio installed in the same virtual environment, gpiozero may attempt to load the legacy factory first and fail silently or throw an exception.

Exact Error Strings and Ranked Causes

Error 1: gpiozero.exc.BadPinFactory: Unable to load any default pin factory!
  • Cause A (Most Likely): You are on a Pi 5 and lack the rpi-lgpio package. The Pi 5 requires this to translate gpiozero commands to the RP1 chip.
  • Fix: Run sudo apt install python3-rpi-lgpio.
  • Cause B: You are running the script inside a Docker container without passing the --privileged flag or mapping /dev/gpiochip0.
Error 2: RuntimeError: This module can only be run on a Raspberry Pi!
  • Cause A (Most Likely): Your code or a dependency is explicitly importing the legacy RPi.GPIO library, which checks the CPU hardware ID and fails on the Pi 5's BCM2712 SoC.
  • Fix: Refactor the code to use gpiozero or lgpio directly. Uninstall the legacy package: pip uninstall RPi.GPIO.
Error 3: RuntimeError: No access to /dev/mem. Try running as root!
  • Cause: You are using an outdated library attempting direct memory mapping, or your user is not in the gpio group.
  • Fix: Do not run with sudo. Instead, ensure your user is in the gpio group: sudo usermod -aG gpio $USER, then log out and back in.

Extending and Simplifying the Build

Once you have verified basic digital I/O, you can adapt this foundation for more complex embedded projects.

How to Simplify

If you want to eliminate the physical pull-down/pull-up wiring confusion, rely entirely on the Pi's internal resistors. In the code above, Button(BUTTON_PIN, pull_up=True) activates the internal 50kΩ pull-up resistor. This means the pin sits at 3.3V (HIGH) by default, and pressing the button shorts it to GND (LOW). You only need to wire the button between the GPIO pin and GND—no external resistors required for the switch.

How to Extend

  • Add PWM Fading: Swap the LED class for PWMLED. You can then use led.pulse(fade_in_time=1, fade_out_time=1) to create a smooth breathing effect. Note that hardware PWM on the Pi 5 is limited to specific pins (like BCM 12, 13, 18, 19), but gpiozero handles software PWM seamlessly on any pin.
  • Integrate I2C Sensors: Move beyond digital I/O by adding a BME280 temperature/humidity sensor. Wire the SDA to Physical Pin 3 (BCM 2) and SCL to Physical Pin 5 (BCM 3). Use the smbus2 or adafruit-circuitpython-bme280 library to read environmental data and adjust the LED brightness based on room temperature.
  • Opto-isolation for Inductive Loads: If you plan to switch a 12V relay or a solenoid, never wire it directly to the Pi's GPIO. Use a 2N2222 NPN transistor or a PC817 optocoupler to isolate the 3.3V logic from the higher voltage load, protecting the RP1 chip from flyback voltage spikes.

For deeper technical specifications on the RP1 southbridge and official pinout diagrams, refer to the Raspberry Pi Hardware Documentation. For advanced API usage, including custom pin factories and SPI/I2C abstractions, consult the gpiozero official documentation.