The Raspberry Pi 4 Model B features a 40-pin header with 26 usable general-purpose I/O (GPIO) pins operating strictly at 3.3V logic. Unlike the 5V-tolerant pins on many Arduino boards, pushing 5V into a Pi 4 GPIO pin will permanently destroy the BCM2711 SoC. For general-purpose digital outputs and inputs, your safest default pins are BCM 5, 6, 12, 13, 16, 17, 19, 20, 21, 22, 23, 24, 25, 26, and 27. This guide walks through a decision framework for pin selection, a robust opto-isolated relay project, and exact debugging paths for the most common GPIO runtime errors.
The GPIO Pin Selection Decision Tree
Not all GPIO pins on the Raspberry Pi 4 are created equal. Many are hardcoded for specific communication protocols or have pull-up resistors active during boot, which can cause connected relays to chatter or motors to twitch on startup. Use this decision matrix to select the right pins for your circuit.
| If your circuit needs... | Use these BCM Pins | Avoid these BCM Pins | Why? |
|---|---|---|---|
| General Digital I/O (Relays, LEDs, Buttons) | 5, 6, 12, 13, 16, 17, 19, 20, 21, 22, 23, 24, 25, 26, 27 | 2, 3, 4, 7, 8, 9, 10, 11, 14, 15 | Safe defaults with no boot-state interference or reserved bus conflicts. |
| I2C Sensors (BME280, OLEDs) | 2 (SDA), 3 (SCL) | All others | Hardware I2C bus 1 includes onboard 1.8kΩ pull-up resistors. |
| SPI Devices (RFID, ADCs) | 7 (CE1), 8 (CE0), 9 (MISO), 10 (MOSI), 11 (SCLK) | 5, 6, 12, 13 | Hardware SPI0 is significantly faster and more stable than bit-banging. |
| Hardware UART (GPS, Serial Console) | 14 (TXD), 15 (RXD) | N/A | Must disable serial console in raspi-config to free these for user space. |
/boot/config.txt.
Project Build: Opto-Isolated 4-Channel Relay Controller
Driving inductive loads (like relay coils or solenoids) directly from Pi GPIO pins is a fast track to frying your board due to back-EMF voltage spikes and current overdraw. The Pi 4 GPIO pins can source/sink a maximum of 16mA per pin, with a strict 50mA total limit across all GPIO banks. We use an opto-isolated relay module to keep the 5V relay coil power entirely separate from the Pi’s 3.3V logic.
Parts List
- Microcontroller: Raspberry Pi 4 Model B (4GB RAM variant) – ~$55
- Relay Module: 4-Channel 3.3V Trigger Opto-Isolated Relay Module (Songle SRD-03VDC-SL-C coils) – ~$8
- Wiring: 22 AWG Silicone stranded wire (Red, Black, Yellow, Green) – ~$5
- Power Supply: Official Raspberry Pi 27W USB-C Power Supply (5.1V / 5.3A) – Included with Pi or ~$12
Pin Mapping Table
We are using BCM numbering in the software, mapped to the physical header pins for wiring. Reference the official Raspberry Pi GPIO documentation or Pinout.xyz for physical header orientation.
| Relay Channel | BCM GPIO | Physical Pin | Wire Color | Function |
|---|---|---|---|---|
| CH1 (IN1) | 17 | 11 | Yellow | Logic Trigger 1 |
| CH2 (IN2) | 27 | 13 | Green | Logic Trigger 2 |
| CH3 (IN3) | 22 | 15 | Blue | Logic Trigger 3 |
| CH4 (IN4) | 23 | 16 | Purple | Logic Trigger 4 |
| VCC | N/A | 2 (5V) | Red | Relay Coil Power |
| GND | N/A | 6 (GND) | Black | Common Ground |
Wiring Steps
- De-energize the Pi: Unplug the USB-C power supply. Never wire GPIO headers while the Pi is powered.
- Connect Power: Run the Red wire from Physical Pin 2 (5V) to the relay module’s VCC terminal. Run the Black wire from Physical Pin 6 (GND) to the relay module’s GND terminal.
- Connect Logic Triggers: Connect BCM 17, 27, 22, and 23 to IN1, IN2, IN3, and IN4 respectively.
- Verify Jumper Settings: Ensure the relay module has the VCC-JD jumper removed if it supports separate logic and coil power (most 4-channel 3.3V modules have this hardwired, but verify the datasheet).
- Load Connection: Wire your AC/DC load through the relay’s COM (Common) and NO (Normally Open) screw terminals. Ensure mains voltage wiring is enclosed in a junction box.
Python Control Code with Error Handling
This code targets the Raspberry Pi 4 Model B running Raspberry Pi OS (Bookworm or Bullseye). It uses the RPi.GPIO library. While gpiozero is the modern recommendation, RPi.GPIO provides explicit runtime error strings that are critical for debugging hardware conflicts, which we will cover in the next section.
import RPi.GPIO as GPIO
import time
import sys
# --- Pin Definitions (BCM Mode) ---
RELAY_PINS = [17, 27, 22, 23]
# Most opto-isolated relay modules are Active-LOW.
# This means GPIO.LOW turns the relay ON, and GPIO.HIGH turns it OFF.
RELAY_ON = GPIO.LOW
RELAY_OFF = GPIO.HIGH
def setup_gpio():
"""Initialize GPIO pins with safe defaults."""
GPIO.setmode(GPIO.BCM)
# Suppress warnings if pins were left dirty from a previous crash
GPIO.setwarnings(True)
for pin in RELAY_PINS:
# Set as output, default to HIGH (Relay OFF) to prevent boot-twitching
GPIO.setup(pin, GPIO.OUT, initial=RELAY_OFF)
def sequence_relays():
"""Cycle through relays with a 1-second delay."""
try:
while True:
for pin in RELAY_PINS:
print(f"Energizing Relay on BCM {pin}")
GPIO.output(pin, RELAY_ON)
time.sleep(1.0)
GPIO.output(pin, RELAY_OFF)
time.sleep(0.5)
except KeyboardInterrupt:
print("\nUser interrupted. Cleaning up...")
except Exception as e:
print(f"\nUnexpected error: {e}")
sys.exit(1)
finally:
# CRITICAL: Always clean up to release hardware locks
GPIO.cleanup(RELAY_PINS)
print("GPIO cleanup complete. Safe to exit.")
if __name__ == "__main__":
setup_gpio()
sequence_relays()
Debugging: Exact Error Strings and Ranked Causes
When working directly with the Pi 4’s memory-mapped GPIO registers, the OS will throw specific errors if state or permissions are violated. Here is how to diagnose the two most common roadblocks.
Error 1: "RuntimeError: This channel is already in use"
Exact Error String: RuntimeError: This channel is already in use, continuing anyway. Use GPIO.setwarnings(False) to disable warnings. (Or, in strict mode, it will halt execution entirely).
Ranked Causes:
- Unclean Exit (90% of cases): Your previous script crashed, was killed via
kill -9, or lost power before reaching theGPIO.cleanup()function. The OS still thinks the pin is locked by a zombie process. - Concurrent Scripts (8%): You have a background service (like a systemd daemon or Home Assistant) currently polling or writing to that specific BCM pin.
- Device Tree Overlay Conflict (2%): The pin is claimed by an active overlay in
/boot/config.txt(e.g., you are trying to use BCM 18 while the PWM audio overlay is active).
- Run
sudo killall python3in the terminal to ensure no ghost Python scripts are holding the GPIO memory map. - Check for active overlays: Run
dtoverlay -lto see if the kernel has claimed your target pins for hardware functions like I2S or SPI. - Verify physical wiring: Ensure a 5V wire hasn’t accidentally shorted against your BCM signal wire, which can cause the SoC to internally flag the pin as faulted.
Error 2: "RuntimeError: No access to /dev/mem"
Exact Error String: RuntimeError: No access to /dev/mem. Try running as root!
The Fix: The RPi.GPIO library requires direct memory access to the BCM2711 peripheral registers. You must execute your script with elevated privileges. Run your script using sudo python3 your_script.py, or add your user to the gpio group via sudo usermod -aG gpio $USER and reboot.
Scaling the Build: When to Abandon Direct GPIO
The Raspberry Pi 4 only has 26 usable GPIO pins. If your project requires more than 4 or 5 relays, or you need to read an array of limit switches, you will quickly run out of safe pins. Do not attempt to use the I2C or SPI pins as standard digital I/O just to squeeze in one more relay; the boot-sequence logic spikes will trigger your relays randomly during reboot.
Use this decision path to scale your build correctly:
| Project Requirement | Recommended Hardware | Concrete Part Number | Why this wins |
|---|---|---|---|
| Control 1 to 4 Relays | Direct Pi 4 GPIO | BCM 17, 27, 22, 23 | Zero extra cost, simple Python logic, sufficient current for opto-isolator LEDs. |
| Control 5 to 8 Relays | Darlington Transistor Array | ULN2003A IC | Allows you to safely drive 5V relay coils from 3.3V logic without complex level shifters. |
| Control 9 to 16 Relays/Sensors | I2C Port Expander | MCP23017 (Adafruit #732) | Uses only 2 Pi pins (SDA/SCL) to give you 16 additional, fully configurable digital I/O pins. |
| Control 17+ High-Power Loads | External PLC or Modbus Relay Bank | Waveshare Modbus RTU Relay | Completely removes high-current switching from the Pi. Pi only sends serial RS485 commands. |
Final Recommendation: For any permanent installation requiring more than 4 relay channels, bypass direct GPIO wiring entirely and purchase an MCP23017 I2C Port Expander breakout board. It costs roughly $6, wires directly to BCM 2 and 3, and is supported natively by the gpiozero library via the MCP23017 class, giving you 16 rock-solid, boot-safe I/O pins without risking the Pi’s primary SoC.






