The Quick Answer: Which Pi Board and Library for GPIO in 2026?

If you are starting a new hardware project today, the Raspberry Pi 5 (4GB) running Raspberry Pi OS Bookworm (64-bit) is the default recommendation. The shift to the RP1 southbridge chip on the Pi 5 fundamentally changed how the board handles GPIO interrupts and memory mapping, rendering legacy libraries obsolete. For software, gpiozero backed by the lgpio C-library is the only modern, stable choice.

Decision Tree: Pick Your Board
Board VariantBest Use CaseGPIO QuirksVerdict
Raspberry Pi 5 (4GB/8GB)Edge AI, high-speed polling, multitasking OSUses RP1 chip; requires lgpio; 3.3V logic only.DEFAULT PICK
Raspberry Pi 4 Model BLegacy retro-gaming, standard desktop replacementBCM2711 SoC; native RPi.GPIO support; 3.3V logic.Buy only if used/discounted.
Raspberry Pi Zero 2 WHeadless, battery-powered single-sensor nodesSame BCM2710A1 as Pi 3; limited RAM for heavy logging.Pick for dedicated IoT nodes.

Raspberry Pi 5 GPIO Pin Mapping & Hardware Specs

The physical 40-pin header on the Pi 5 looks identical to the Pi 4, but the internal routing goes through the RP1 southbridge rather than the main BCM2712 SoC. This means the GPIO base memory addresses are different, which is why older code breaks. Furthermore, the Pi 5's RP1 chip provides dedicated, configurable pull-up/pull-down resistors that are significantly more robust than previous generations.

Here is the essential pin mapping for standard digital I/O, I2C, and SPI. Always use BCM (Broadcom) numbering in your code, never physical pin numbers.

Physical PinBCM GPIOPrimary FunctionHardware Notes & Warnings
1-3.3V PowerMax draw ~50mA total across all 3.3V pins.
32I2C1 SDAHas physical 1.8kΩ pull-up resistors on the board.
53I2C1 SCLDo not use as standard GPIO if I2C is enabled in config.
1117GPIO 17Safe for general input/output. Used in our build below.
1327GPIO 27Safe for general input/output. Used in our build below.
1910SPI0 MOSIReserved for SPI. Do not use for buttons/LEDs.
6, 9, 14, 20, 25, 30, 34, 39-Ground (GND)All GND pins are common. Use the closest one to your signal.
Safety Warning: The GPIO pins of Raspberry Pi boards operate at 3.3V logic. They are not 5V tolerant. Feeding a 5V signal directly into BCM 17 will permanently destroy the RP1 southbridge pin, and potentially the entire chip. Always use a logic level shifter or a voltage divider when interfacing with 5V Arduino sensors.

Project Build: Interrupt-Driven Button & LED Status Node

We are building a hardware debounced button that triggers an LED, utilizing hardware interrupts rather than CPU-intensive polling loops. This frees up the Pi's processor for other tasks like running a local database or MQTT broker.

Parts List

  • Board: Raspberry Pi 5 (4GB variant) with Active Cooler
  • Storage: SanDisk 64GB Extreme microSD (U3 A2 rated for OS longevity)
  • Input: Elegoo Momentary Pushbutton Switch (tactile, 4-pin)
  • Output: 5mm Red LED (forward voltage ~2.0V)
  • Current Limiting: 220Ω through-hole resistor (1/4W)
  • Wiring: Female-to-Male and Male-to-Male Dupont jumper wires
  • Prototyping: Half-size 400-point solderless breadboard

Wiring Steps

  1. De-energize: Unplug the Pi 5 USB-C power supply before touching the GPIO header.
  2. Connect the LED: Insert the 220Ω resistor into the breadboard. Connect one end to Physical Pin 13 (BCM 27) via a jumper wire. Connect the other end to the anode (long leg) of the LED. Connect the cathode (short leg) to the breadboard's ground rail.
  3. Connect the Button: Place the pushbutton across the breadboard's center trench. Connect one side of the button to Physical Pin 11 (BCM 17). Connect the opposite side of the button to the breadboard's ground rail.
  4. Ground the Rail: Connect the breadboard's ground rail to Physical Pin 6 (GND) on the Pi.
  5. Verify: Use a multimeter in continuity mode to ensure the button bridges the connection only when pressed, and that the LED is oriented correctly (anode to resistor, cathode to GND).

Complete Python Code with Error Handling

This script targets the Raspberry Pi 5 running Bookworm. It uses the gpiozero library, which automatically leverages the lgpio backend on Pi 5. It includes explicit pin definitions, software debouncing, and robust exception handling.

#!/usr/bin/env python3
"""
Raspberry Pi 5 GPIO Interrupt Button & LED Controller
Target: Raspberry Pi 5 (4GB) + Bookworm OS
Library: gpiozero (v2.0+)
"""

from gpiozero import Button, LED
from gpiozero.exc import BadPinFactory, GPIOPinMissing
from signal import pause
import sys
import logging

# --- PIN DEFINITIONS (BCM Numbering) ---
BUTTON_PIN = 17  # Physical Pin 11
LED_PIN = 27     # Physical Pin 13

logging.basicConfig(level=logging.INFO, format='%(levelname)s: %(message)s')

def main():
    try:
        # Initialize hardware with explicit pull-up and 50ms software debounce
        btn = Button(BUTTON_PIN, pull_up=True, bounce_time=0.05)
        led = LED(LED_PIN)
        
        logging.info(f'GPIO initialized. Button on BCM {BUTTON_PIN}, LED on BCM {LED_PIN}.')
        logging.info('Press the button to toggle LED. Press Ctrl+C to exit.')

        # Interrupt-driven callbacks (non-blocking)
        btn.when_pressed = lambda: led.on() or logging.info('Button PRESSED -> LED ON')
        btn.when_released = lambda: led.off() or logging.info('Button RELEASED -> LED OFF')

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

    except BadPinFactory as e:
        logging.error(f'Pin Factory Error: {e}')
        logging.error('Fix: Run "sudo apt install python3-lgpio" on Pi 5 Bookworm.')
        sys.exit(1)
    except GPIOPinMissing as e:
        logging.error(f'Hardware Error: {e}')
        sys.exit(1)
    except KeyboardInterrupt:
        logging.info('Script terminated by user. Cleaning up GPIO...')
        sys.exit(0)
    except Exception as e:
        logging.error(f'Unexpected error: {e}')
        sys.exit(1)

if __name__ == '__main__':
    main()

Debugging: Fixing Pin Factory and Edge Detection Errors

The transition from the BCM2711 (Pi 4) to the RP1 (Pi 5) broke thousands of legacy tutorials. If your code fails immediately upon execution, do not guess. Follow this diagnostic path.

Error 1: gpiozero.exc.BadPinFactory: Unable to load any default pin factory!

What it means: gpiozero cannot find a compatible backend to talk to the RP1 chip. The legacy RPi.GPIO library is essentially dead on Pi 5, and gpiozero is failing to fall back to lgpio.

Ranked Causes & Fixes:

  1. Missing lgpio package (90% of cases): Bookworm does not always pre-install the Python bindings for lgpio in minimal images.
    Fix: Run sudo apt update && sudo apt install python3-lgpio.
  2. Running in an isolated virtual environment without system packages: If you created a venv with python3 -m venv myenv, it lacks access to the system-level lgpio C-bindings.
    Fix: Recreate the venv using python3 -m venv --system-site-packages myenv.

Error 2: RuntimeError: Failed to add edge detection

What it means: The OS kernel or another daemon has already claimed BCM 17 or BCM 27, preventing your script from attaching a hardware interrupt to it.

Ranked Causes & Fixes:

  1. I2C/SPI Overlay Conflict: You are trying to use a pin reserved by an active device tree overlay.
    Fix: Check /boot/firmware/config.txt and comment out overlays like dtparam=i2c_arm=on if you are using BCM 2/3, or SPI overlays if using BCM 10/11.
  2. Zombie Python Process: A previous run of your script crashed without releasing the pin.
    Fix: Run sudo killall python3 or reboot the Pi.
  3. pigpiod Daemon Conflict: The pigpiod service is running in the background and hoarding GPIO memory.
    Fix: Run sudo systemctl stop pigpiod.
The First 3 Things to Check When GPIO Fails:
  1. Verify OS Architecture: Run cat /etc/os-release. You must be on Bookworm (Debian 12) or newer for native Pi 5 lgpio support. Bullseye will fail.
  2. Inspect Physical Seating: Check the ribbon cable or Dupont wires. The red stripe (Pin 1) must align with the square pad on the Pi's PCB. Offsetting by one row fries the 3.3V regulator.
  3. Measure Voltage: Put a multimeter on Physical Pin 1 (3.3V) and Pin 6 (GND). If you read 0V or 5V, your power supply or polyfuse is tripped, and the RP1 chip is in brownout protection.

Extending and Simplifying Your GPIO Build

Once the basic interrupt node is stable, you will inevitably need to scale. Here is how to adapt the architecture without rewriting your core logic.

How to Simplify: Multi-Pin Polling

If you are building a keypad or a bank of limit switches, setting up individual Button objects creates messy code. Simplify by using gpiozero's ButtonBoard class. It treats multiple GPIO pins as a single tuple-returning object, allowing you to read the state of 8 pins in a single line of code without managing individual interrupt callbacks.

How to Extend: MQTT Integration

To turn this local button into a smart home trigger, extend the script by importing paho-mqtt. Inside the btn.when_pressed lambda function, add a client.publish('homeassistant/sensor/pi5_button', 'ON') payload. Because gpiozero callbacks run in a separate background thread, publishing to an MQTT broker inside the callback will not block the main pause() loop, ensuring zero latency on the physical LED response while the network I/O happens asynchronously.

For authoritative pinout references and ongoing library updates, always consult the official Raspberry Pi Hardware Documentation and the interactive Pinout.xyz database before wiring new sensors to the RP1 header.