The "Raspberry Pines" Autocorrect: Getting Started with Pi 5 GPIO

If voice-to-text or a search engine autocorrect just sent you looking for "raspberry pines," you are actually looking for the GPIO (General Purpose Input/Output) pins on a Raspberry Pi. It is a common typo, but the hardware behind it is anything but trivial. With the release of the Raspberry Pi 5, the GPIO architecture underwent a massive shift. The legacy BCM2711 SoC was replaced by the RP1 southbridge chip, changing how the operating system addresses the pins at the kernel level.

This guide targets the Raspberry Pi 5 (8GB variant) running Raspberry Pi OS (Bookworm or later). We will build a hardware PWM fan controller, write production-ready Python code, and debug the most common GPIO lock errors that plague Pi 5 builders.

Difficulty Rating: Intermediate | Time: 45 mins | Target Board: Raspberry Pi 5 (8GB)

Parts List

  • Board: Raspberry Pi 5 (8GB) with active cooler
  • Fan: Noctua NF-A4x10 5V PWM
  • Transistor: 2N2222 NPN (or 2N3904 equivalent)
  • Resistor: 10kΩ (Base current limiter)
  • Switch: Momentary tactile pushbutton
  • Wiring: 22 AWG solid core jumper wires, breadboard

Pi 5 Pin Mapping and Hardware Specs

The Pi 5 maintains the standard 40-pin header footprint, but the internal routing goes through the RP1 chip. The logic level remains strictly 3.3V. Feeding 5V back into these pins will instantly destroy the RP1 silicon. Below is the spec sheet for the pins used in this build.

Physical Pin BCM / GPIO Name Function in Build Voltage / Limits
Pin 2 5V Power Fan VCC (Power) 5.0V nominal (Max 2A total draw)
Pin 6 GND Common Ground 0V Reference
Pin 12 GPIO 18 PWM Output (Fan Control) 3.3V Logic (Max 8mA default)
Pin 18 GPIO 24 Input (Button Toggle) 3.3V Logic (Internal Pull-Up)

For a complete visual map, always cross-reference with the official Pinout.xyz database, which has been updated for the Pi 5 RP1 routing.

Step-by-Step: Wiring and Coding a PWM Fan Controller

We are building a circuit where GPIO 18 sends a 3.3V PWM signal to a transistor, which switches the 5V PWM line of the Noctua fan. GPIO 24 reads a button to toggle the fan manually.

  1. Wire the Transistor: Connect the 2N2222 Base to GPIO 18 through the 10kΩ resistor. Connect the Emitter to Pin 6 (GND). Connect the Collector to the Fan's PWM wire (usually blue).
  2. Wire Fan Power: Connect the Fan's VCC (yellow) to Pin 2 (5V). Connect the Fan's GND (black) to Pin 6 (GND).
  3. Wire the Button: Connect one leg of the tactile switch to GPIO 24, and the other leg to Pin 6 (GND). We will use the Pi's internal pull-up resistor in software.
  4. Install Dependencies: Open your terminal and install the Pi 5-compatible GPIO libraries. The legacy RPi.GPIO is deprecated on Bookworm; you must use rpi-lgpio.
    sudo apt update
    sudo apt install python3-gpiozero python3-rpi-lgpio

Complete Python Control Script

Save the following code as fan_controller.py. It includes explicit pin definitions, hardware PWM initialization, and robust error handling to ensure the GPIO chip is released if the script crashes.

import time
import signal
import sys
from gpiozero import PWMOutputDevice, Button

# --- PIN DEFINITIONS ---
FAN_PWM_PIN = 18
BUTTON_PIN = 24

# --- HARDWARE SETUP ---
# Hardware PWM on GPIO 18 (hardware PWM channel 0)
fan = PWMOutputDevice(FAN_PWM_PIN, frequency=25000) 
# Button with internal pull-up (active_low=True means pressing connects to GND)
toggle_btn = Button(BUTTON_PIN, pull_up=True, bounce_time=0.05)

fan_is_running = False

def toggle_fan():
    global fan_is_running
    fan_is_running = not fan_is_running
    if fan_is_running:
        fan.value = 0.75  # 75% duty cycle
        print("Fan ON at 75%")
    else:
        fan.value = 0.0
        print("Fan OFF")

toggle_btn.when_pressed = toggle_fan

def graceful_exit(signum, frame):
    """Ensure GPIO chip is released on Ctrl+C or kill signal."""
    print("\nShutting down and releasing GPIO pins...")
    fan.close()
    toggle_btn.close()
    sys.exit(0)

signal.signal(signal.SIGINT, graceful_exit)
signal.signal(signal.SIGTERM, graceful_exit)

if __name__ == "__main__":
    print(f"Fan controller active. Target Board: Raspberry Pi 5.")
    print(f"Press the button on GPIO {BUTTON_PIN} to toggle.")
    # Initial state: Fan off
    fan.value = 0.0 
    
    try:
        while True:
            # Main loop kept alive; event-driven via gpiozero callbacks
            time.sleep(0.1)
    except Exception as e:
        print(f"Unexpected error: {e}")
        graceful_exit(None, None)

Debugging: "lgpio.error: 'gpiochip4' already open"

If you are migrating from a Pi 4 to a Pi 5, you will inevitably hit a wall with the new LGPIO backend. The most common fatal error when running GPIO scripts on the Pi 5 is:

lgpio.error: 'gpiochip4' already open

This happens because the RP1 chip exposes GPIO via /dev/gpiochip4. If a previous Python script crashed or was killed without running a cleanup routine, the OS keeps the file descriptor locked. When your new script tries to claim the pins, the kernel denies access.

The First Three Things to Check When It Fails

  1. Check for Ghost Processes: A background script is likely still holding the lock. Run ps aux | grep python in the terminal. If you see your previous script running, kill it with sudo kill -9 <PID>.
  2. Verify Peripheral Conflicts: Open sudo raspi-config and check Interface Options. If you have I2C, SPI, or 1-Wire enabled, they might be reserving the pins you are trying to use in software. Disable them if they aren't needed for this specific build.
  3. Confirm Library Backend: Ensure you are not accidentally importing the old RPi.GPIO library in a sub-module. Run pip list | grep RPi. If RPi.GPIO is installed alongside rpi-lgpio, Python might grab the wrong backend. Uninstall the legacy package: pip uninstall RPi.GPIO.

Note: If the lock persists after killing all Python processes, a quick sudo reboot will flush the kernel's GPIO file descriptors.

Extending and Simplifying Your Build

Once the base circuit is stable, you have two paths depending on your project goals:

Simplify: Use a Fan HAT

If breadboard wiring feels fragile for a permanent enclosure, abandon the discrete transistor. Purchase a dedicated PWM Fan HAT (like the Argon NEO or GeeekPi tower coolers). These plug directly into the 40-pin header and handle the 5V-to-3.3V logic shifting on-board, reducing your code to a simple I2C or direct PWM call.

Extend: Closed-Loop Thermal PID

Replace the manual button with an I2C temperature sensor like the BME280. Wire SDA to Pin 3 and SCL to Pin 5. Use the smbus2 library to read the die temperature, and implement a PID control loop in Python to dynamically adjust the fan.value (duty cycle) based on thermal load. This prevents the fan from hunting (ramping up and down rapidly) at thermal thresholds.

Frequently Asked Questions

Can I use 5V sensors directly on Raspberry Pi pins?

No. The Raspberry Pi 5 GPIO pins operate strictly at 3.3V logic. Feeding a 5V signal (like from an Arduino Uno or a 5V ultrasonic sensor) directly into a Pi GPIO pin will overvoltage the RP1 silicon, likely destroying the pin or the entire chip. You must use a bidirectional logic level shifter (like the BSS138 MOSFET-based shifters from Adafruit or SparkFun) or a simple voltage divider (e.g., 2kΩ and 3.3kΩ resistors) to drop the 5V signal down to a safe 3.3V before it reaches the Pi.

Why do my Raspberry Pi pins stop working after a reboot?

This is almost always caused by the config.txt file or device tree overlays. When you enable hardware interfaces (like UART, I2C, or SPI) via raspi-config, the OS reserves those specific pins at boot. If you later try to use GPIO 14 and 15 (UART TX/RX) as standard digital I/O in Python, the kernel will block you. To fix this, run sudo raspi-config, navigate to Interface Options, and disable any serial or peripheral protocols you are no longer using, then reboot.

What is the maximum current draw per GPIO pin on the Pi 5?

By default, the Raspberry Pi 5 limits GPIO pins to 8mA per pin. Through software configuration (editing the /boot/firmware/config.txt with gpio=18=op,dh,dd drive strength commands), you can push individual pins to 16mA. However, the total combined current draw across all GPIO pins in a single bank must not exceed 50mA. Never attempt to drive a motor, relay, or high-power LED directly from a GPIO pin; always use a transistor, MOSFET, or optocoupler as a switch.

Safety & Code Caveat: While this guide covers low-voltage DC electronics, always ensure your Pi's power supply is rated for the total load. If you are switching mains AC voltage via a relay connected to these pins, the mains wiring must be performed in accordance with local electrical codes, and you should consult a licensed electrician for high-voltage terminations.