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
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. |
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.
- 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.
- 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).
- 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.
- 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.
- 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
- Pin Factory Backend: Is
rpi-lgpioactually installed? Bookworm does not always bundle it by default in headless Lite images. - BCM vs BOARD Numbering:
gpiozerostrictly uses Broadcom (BCM) pin numbering. If you pass11expecting Physical Pin 11, you are actually targeting BCM 11 (which is Physical Pin 23). Always map BCM numbers. - 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
RuntimeError: Cannot determine SOC peripheral base addressCause: 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.
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
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.






