When mapping out the RPI GPIO on a Raspberry Pi 5, the first thing you need to know is that the underlying architecture has fundamentally changed. Unlike the Pi 4, where the main BCM2711 SoC handled peripheral routing, the Pi 5 offloads all GPIO, I2C, SPI, and UART duties to a dedicated RP1 southbridge chip. This hardware shift, combined with the move to Debian Bookworm, completely broke the legacy RPi.GPIO Python library. If you are still copying and pasting tutorials from 2021, your code will fail.

This guide gives you the exact pinout, the modern gpiozero implementation, and the specific error strings you will hit on the bench—and how to fix them in under two minutes.

Hardware Spec Sheet & Parts List

Difficulty: Beginner-Intermediate | Time: 20 Minutes | Board Target: Raspberry Pi 5 (8GB RAM) running Raspberry Pi OS (64-bit, Bookworm)

Before we wire anything, verify you have the exact components listed below. The Pi 5's 3.3V rail was upgraded to supply up to 3A (a massive leap from the Pi 4's 50mA limit), but the individual GPIO pins still strictly cap at 16mA. We are building a PWM-controlled dimmable LED circuit with a tactile button to demonstrate safe, repeatable logic.

  • Microcontroller: Raspberry Pi 5 (8GB variant recommended for headless + desktop multitasking)
  • OS: Raspberry Pi OS (64-bit, Bookworm release)
  • Output: 5mm Diffused Red LED (Forward voltage ~2.0V)
  • Current Limiting: 330Ω through-hole resistor (1/4W)
  • Input: 12x12mm tactile pushbutton switch
  • Wiring: 28 AWG Dupont jumper wires (Female-to-Female and Male-to-Female)
  • Prototyping: 830 tie-point solderless breadboard

The Raspberry Pi 5 GPIO Pinout & Electrical Limits

The physical 40-pin header layout remains backward-compatible with Pi 4 HATs, but the electrical characteristics and internal routing are managed by the RP1 chip. Below is the data-dense reference table for the pins we use in this project, alongside the critical power rails. Bookmark this row data; it saves you from frying a trace.

Physical Pin BCM GPIO Function / Alt Default Pull Max Continuous Current Notes & Warnings
1 N/A 3V3 Power Rail N/A 3000mA (Total Rail) Massive upgrade on Pi 5. Safe for powering multiple sensors.
6 N/A Ground (GND) N/A N/A Common return path. Always verify continuity to Pi chassis.
11 17 GPIO17 / UART0_RTS Pull-Down 16mA Used for Button input. Internal pull-down prevents floating.
12 18 GPIO18 / PWM0 Pull-Down 16mA Hardware PWM capable. Used for LED dimming.
17 N/A 3V3 Power Rail N/A 3000mA (Total Rail) Secondary 3.3V feed point for breadboard power rails.
2 N/A 5V Power Rail N/A 5A (via USB-C) Direct from PSU. Do NOT backfeed 5V into GPIO logic pins.
Bench Tip: Never source 5V directly from a GPIO pin. The Pi 5 logic level is strictly 3.3V. Feeding 5V into BCM 17 or 18 will instantly destroy the RP1 southbridge pad, and the board will require micro-soldering to repair.

Wiring the Circuit: Step-by-Step

We are wiring a button to BCM 17 and a PWM LED to BCM 18. Follow these exact physical connections to match the Python code provided in the next section.

  1. Power the Breadboard: Connect Physical Pin 1 (3.3V) to the red breadboard rail using a Red Dupont wire. Connect Physical Pin 6 (GND) to the blue breadboard rail using a Black Dupont wire.
  2. Wire the Button: Place the tactile switch across the breadboard center trench. Run a Yellow wire from one leg of the button to Physical Pin 11 (BCM 17). Run a Black wire from the opposite leg to the blue GND rail. (We rely on the Pi's internal pull-up resistor in software, so no external resistor is needed here).
  3. Wire the LED Resistor: Insert the 330Ω resistor into the board. Connect one end to Physical Pin 12 (BCM 18) using a Green wire. Connect the other end to an empty row.
  4. Wire the LED: Insert the 5mm LED. The anode (long leg) connects to the same row as the resistor. The cathode (short leg, flat side) connects to the blue GND rail via a Black wire.
  5. Verify: Use a multimeter in continuity mode to ensure the GND rail does not short to the 3.3V rail before applying power to the Pi.

Complete Python Control Code (gpiozero)

The legacy RPi.GPIO library is deprecated on Bookworm. The official, supported method for controlling the RPI GPIO on a Raspberry Pi 5 is the gpiozero library, which automatically leverages the lgpio backend under the hood.

This script reads the button state and smoothly ramps the LED brightness using hardware PWM. It includes explicit error handling for the most common backend failures.

#!/usr/bin/env python3
"""
Raspberry Pi 5 GPIO Control Script
Target: Pi 5 (Bookworm) using gpiozero and lgpio backend
Author: ElectricalFlux
"""

import sys
import time

try:
    from gpiozero import PWMLED, Button
    from gpiozero.exc import BadPinFactory
    from signal import pause
except ImportError as e:
    print(f"[FATAL] Missing library: {e}")
    print("Fix: sudo apt update && sudo apt install python3-gpiozero python3-rpi-lgpio")
    sys.exit(1)

# Explicit Pin Definitions (BCM Numbering)
PIN_LED = 18
PIN_BUTTON = 17

def main():
    try:
        # Initialize hardware with explicit pin factory fallback if needed
        led = PWMLED(PIN_LED)
        button = Button(PIN_BUTTON, pull_up=False) # pull_up=False uses internal pull-down

        print(f"[INFO] GPIO initialized. LED on BCM {PIN_LED}, Button on BCM {PIN_BUTTON}.")
        print("[INFO] Press the button to ramp up LED. Press Ctrl+C to exit.")

        # Main logic loop
        while True:
            if button.is_pressed:
                # Ramp up brightness smoothly
                for brightness in range(0, 101, 5):
                    led.value = brightness / 100.0
                    time.sleep(0.05)
                print("[STATE] Button Pressed -> LED at 100%")
                
                # Hold while pressed
                while button.is_pressed:
                    time.sleep(0.1)
                    
                # Ramp down when released
                for brightness in range(100, -1, -5):
                    led.value = brightness / 100.0
                    time.sleep(0.05)
                print("[STATE] Button Released -> LED at 0%")
            
            time.sleep(0.05) # Debounce / CPU yield

    except BadPinFactory as e:
        print(f"[ERROR] Pin Factory Failed: {e}")
        print("Fix: The lgpio backend is missing. Run: sudo apt install python3-rpi-lgpio")
        sys.exit(2)
    except KeyboardInterrupt:
        print("\n[INFO] Script terminated by user. Cleaning up GPIO...")
    except Exception as e:
        print(f"[ERROR] Unexpected runtime error: {e}")
        sys.exit(3)
    finally:
        # gpiozero handles cleanup on exit automatically, but explicit close is safe
        try:
            led.close()
            button.close()
        except NameError:
            pass

if __name__ == "__main__":
    main()

Debugging: Exact Errors and the First 3 Things to Check

When working with embedded Linux, hardware abstraction layers fail in predictable ways. If your script crashes on boot, do not rewrite your logic. Check these three items first.

The First 3 Things to Check When It Fails

  1. Pin Factory Backend: Is rpi-lgpio actually installed? Bookworm does not always bundle it by default in headless Lite images.
  2. BCM vs BOARD Numbering: gpiozero strictly uses Broadcom (BCM) pin numbering. If you pass 11 expecting Physical Pin 11, you are actually targeting BCM 11 (which is Physical Pin 23). Always map BCM numbers.
  3. 3.3V vs 5V Rail Shorts: If the Pi reboots randomly when you press the button, your breadboard power rails are likely shorted, or you are pulling too much current through a damaged GPIO trace.

Ranked Error Strings and Fixes

Error 1: RuntimeError: Cannot determine SOC peripheral base address
Cause: You are trying to import the legacy RPi.GPIO library on a Raspberry Pi 5. The library looks for the BCM2711 memory map, but the Pi 5 uses the RP1 chip, so the memory address lookup fails.
Fix: Uninstall the legacy library (pip uninstall RPi.GPIO) and refactor your code to use gpiozero as shown above.

Error 2: gpiozero.exc.BadPinFactory: Unable to load any default pin factory!
Cause: gpiozero is installed, but it cannot find a valid backend to talk to the kernel. On Pi 5 Bookworm, it requires the lgpio C-extension.
Fix: Open your terminal and run:
sudo apt update && sudo apt install python3-rpi-lgpio

Error 3: RuntimeError: No access to /dev/mem. Try running as root!
Cause: You are trying to use direct memory mapping (via pigpio or raw SPI/I2C calls) as a standard user without the correct group permissions.
Fix: Add your user to the required hardware groups and reboot:
sudo usermod -aG gpio,i2c,spi $USER && sudo reboot

Extending and Simplifying the Build

Once you have the baseline circuit running, you will likely want to either strip it down for a quick test or scale it up for home automation.

Simplify: The Bash One-Liner

If you don't want to write Python just to test if a pin is alive, use the built-in raspi-gpio or the newer pinctrl utility provided by the RP1 firmware. To set BCM 18 high directly from the terminal:

pinctrl set 18 op dh

(op = output, dh = drive high). This bypasses Python entirely and is invaluable for isolating hardware faults from software bugs.

Extend: MQTT Integration for Home Assistant

To turn this desk button into a smart home trigger, extend the Python script by importing the paho-mqtt library. Instead of toggling the local LED, publish the button state to an MQTT broker:

import paho.mqtt.client as mqtt

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
client.connect("192.168.1.100", 1883, 60)

# Inside your button press loop:
client.publish("homeassistant/desk/button", "PRESSED", qos=1)

This transforms a $5 breadboard circuit into a wireless, latency-free IoT node. For deeper hardware specifications and official memory maps, always refer to the official Raspberry Pi 5 Datasheet and the gpiozero documentation.