The Raspberry Pi 4 GPIO Pinout: What You Actually Have to Work With
The Raspberry Pi 4 Model B features a 40-pin header, but treating it as 40 available I/O pins is a fast track to a fried board. This guide targets the Raspberry Pi 4 Model B (4GB or 8GB variant) running Raspberry Pi OS (64-bit, Bookworm or later). Before wiring anything, you must understand the hard electrical limits of the BCM2711 SoC.
• Logic Level: 3.3V. Feeding 5V into any GPIO pin will permanently destroy the SoC.
• Max Current Per Pin: 16mA (safe continuous).
• Total GPIO Current Limit: ~50mA across all pins combined. Do not use GPIO pins to power multiple LEDs or sensors directly.
| Physical Pin | BCM GPIO | Function | Notes |
|---|---|---|---|
| 1 | N/A | 3.3V Power | Max 50mA total draw |
| 2, 4 | N/A | 5V Power | Direct from USB-C input (minus polyfuse drop) |
| 6, 9, 14, 20, 25, 30, 34, 39 | N/A | Ground (GND) | Always use a common ground with external supplies |
| 3, 5 | GPIO 2, 3 | I2C SDA/SCL | Hardware 1.8kΩ pull-ups to 3.3V on the board |
| 8, 10 | GPIO 14, 15 | UART TX/RX | Default serial console; disable in raspi-config for general I/O |
Decision Tree: How to Interface Real-World Hardware to 3.3V Logic
The most common mistake makers make is connecting 5V Arduino modules or 12V industrial sensors directly to the Pi 4. Use this decision matrix to select the exact interface component you need.
| Your Hardware Scenario | Electrical Constraint | Required Interface | Concrete Part Pick |
|---|---|---|---|
| Reading a standard 3.3V tactile switch | Voltage matches, needs pull-up | Direct GPIO (Internal Pull-up) | None (Use software config) |
| Reading a 5V TTL sensor output | 5V > 3.3V limit | Unidirectional Level Shifter | 74AHCT125 or SparkFun Logic Level Converter |
| Reading a 12V/24V industrial proximity sensor | High voltage, inductive noise | Opto-Isolator Module | PC817 4-Channel Optocoupler Board |
| Switching a 5V 500mA relay or solenoid | Current > 16mA limit | Logic-Level MOSFET or Driver IC | IRLZ44N MOSFET or ULN2003 Darlington Array |
| Switching a 120V AC mains appliance | Lethal voltage, isolation needed | Opto-Isolated Relay Board | Songle SRD-05VDC-SL-C Relay Module (Active LOW) |
Default Recommendation: If you are interfacing anything outside a bare 3.3V breadboard environment, always default to an opto-isolated relay or optocoupler module. The $6 cost saves your $55 Pi from ground-loop spikes.
Project Build: Opto-Isolated Input and Relay Output Controller
We will build a robust controller that reads a 12V industrial-style switch (simulated here with a standard switch through an opto-isolator) and triggers a 5V relay to switch a higher-power load.
Parts List
- Board: Raspberry Pi 4 Model B (4GB)
- Input Isolation: PC817 4-Channel Optocoupler Isolation Module (approx. $8)
- Output Switching: 4-Channel 5V Relay Module with Songle SRD-05VDC-SL-C relays (approx. $7)
- Wiring: 22 AWG solid-core jumper wires, 2x20 female GPIO header
- Power: Official Raspberry Pi 27W USB-C Power Supply
Pin Mapping Table (BCM Numbering)
| Component | Pi 4 BCM GPIO | Physical Pin | Wire Color (Standard) |
|---|---|---|---|
| Opto-Isolator CH1 (Input) | GPIO 17 | 11 | Blue |
| Opto-Isolator CH2 (Input) | GPIO 27 | 13 | Green |
| Relay IN1 (Output) | GPIO 22 | 15 | Orange |
| Relay IN2 (Output) | GPIO 23 | 16 | Yellow |
| VCC (Opto & Relay) | 5V (Pin 2) | 2 | Red |
| GND (Opto & Relay) | GND (Pin 6) | 6 | Black |
Wiring Steps
- De-energize: Unplug the Pi 4 USB-C power supply. Never wire the 40-pin header while powered.
- Power the Modules: Connect the 5V (Red) and GND (Black) wires from Physical Pins 2 and 6 to the VCC and GND rails on both the opto-isolator and relay modules. Note: Many relay modules have a jumper linking VCC and JD-VCC. Remove this jumper and supply JD-VCC separately if driving heavy inductive loads to maintain true isolation.
- Wire Inputs: Connect GPIO 17 (Blue) to the output side (usually labeled OUT1 or DO1) of the opto-isolator. Connect your external 12V switch to the input side (DC+ and CH1) of the opto-isolator.
- Wire Outputs: Connect GPIO 22 (Orange) to IN1 on the relay module. Most Pi-compatible relay modules are Active LOW, meaning the relay triggers when the GPIO pin is pulled to 0V.
- Verify: Use a multimeter in continuity mode to verify no shorts between 5V and GND before applying power.
Python Control Code with Robust Error Handling
For Raspberry Pi OS Bookworm (64-bit), the legacy RPi.GPIO library frequently fails to compile or throws runtime warnings. The modern, officially supported standard is gpiozero. It handles pin cleanup automatically and abstracts the Active LOW logic of relay boards.
import sys
import logging
import signal
from gpiozero import Button, OutputDevice
# Configure logging for debugging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# --- PIN DEFINITIONS (BCM Numbering) ---
# Inputs (Active LOW on the opto-isolator output)
OPTO_INPUT_1 = 17
OPTO_INPUT_2 = 27
# Outputs (Active LOW for standard Songle relay modules)
RELAY_OUTPUT_1 = 22
RELAY_OUTPUT_2 = 23
def setup_hardware():
# Button class automatically configures internal pull-ups and handles debouncing
# pull_up=False because the opto-isolator pulls the line to GND when triggered
input1 = Button(OPTO_INPUT_1, pull_up=True, bounce_time=0.05, active_state=False)
input2 = Button(OPTO_INPUT_2, pull_up=True, bounce_time=0.05, active_state=False)
# OutputDevice with active_high=False handles the Active LOW relay logic automatically
relay1 = OutputDevice(RELAY_OUTPUT_1, active_high=False, initial_value=False)
relay2 = OutputDevice(RELAY_OUTPUT_2, active_high=False, initial_value=False)
return input1, input2, relay1, relay2
def main():
try:
in1, in2, out1, out2 = setup_hardware()
logging.info('GPIO initialized successfully. Waiting for inputs...')
# Bind events
in1.when_pressed = out1.on
in1.when_released = out1.off
in2.when_pressed = out2.on
in2.when_released = out2.off
# Keep the script running gracefully
signal.pause()
except Exception as e:
logging.critical(f'Fatal GPIO Error: {e}')
sys.exit(1)
if __name__ == '__main__':
# Handle Ctrl+C gracefully
signal.signal(signal.SIGINT, lambda s, f: sys.exit(0))
main()
Debugging the Big Three: When Your GPIO Script Fails
When your script crashes, do not guess. Check these exact error strings and follow the ranked causes.
gpiozero.exc.PinFactoryFallback: Falling back from rpigpio: No module named 'RPi.GPIO'Ranked Causes:
1. You are on 64-bit Raspberry Pi OS Bookworm, where
RPi.GPIO is deprecated and removed from default repos.2. You installed a virtual environment without installing the fallback
lgpio or RPi.GPIO packages.Fix: Stop using
RPi.GPIO. Rewrite your code using gpiozero (as shown above), which uses the lgpio backend natively on modern Pi OS. Install it via sudo apt install python3-gpiozero python3-lgpio.
RuntimeError: This channel is already in use, continuing anyway. Use GPIO.setwarnings(False) to disable warnings.Ranked Causes:
1. A previous run of your script crashed before executing
GPIO.cleanup(), leaving the pin locked in the kernel.2. Another background service (like a home automation daemon) is currently holding the pin.
Fix: If using legacy
RPi.GPIO, add GPIO.setwarnings(False) at the top of your script. Better yet, switch to gpiozero, which automatically releases pins when the Python garbage collector destroys the object.
PermissionError: [Errno 13] Permission denied: '/dev/gpiomem'Ranked Causes:
1. You are running the script as a user who is not in the
gpio group.2. You are trying to access hardware PWM or I2C/SPI pins which require
/dev/mem (root access) instead of /dev/gpiomem.Fix: Add your user to the gpio group:
sudo usermod -aG gpio $USER, then log out and log back in. Never run GPIO scripts with sudo as a crutch; it creates security vulnerabilities and file ownership nightmares.
The First Three Things to Check When Hardware Fails
If the code runs without errors but the physical relay doesn't click or the input doesn't register:
- Verify Permissions and Groupings: Run
ls -l /dev/gpiomem. It must showcrw-rw---- 1 root gpio. If it showsroot root, your OS image is corrupted or misconfigured. - Check Peripheral Conflicts: Open
sudo raspi-config-> Interface Options. Ensure I2C, SPI, and Serial Console are disabled if you are trying to use pins 2, 3, 7-11, 14, or 15. The kernel will silently block your Python script from toggling these pins if a hardware overlay claims them. - Measure Physical Voltages: Set your multimeter to DC Volts. Probe Physical Pin 1 (should read 3.25V - 3.35V) and Physical Pin 2 (should read 4.9V - 5.1V). If Pin 2 reads below 4.6V, your USB-C power supply is failing or the board's polyfuse has tripped due to a short circuit on your breadboard.
Extending and Simplifying the Build
Once the base opto-isolated circuit is proven, you have two paths for scaling:
Path A: Simplify for Low-Risk Prototyping
If you are moving this to a controlled indoor environment (like a desk-bound weather station) and dropping the 12V industrial sensors, strip out the opto-isolators. Connect 3.3V tactile switches directly to GPIO 17 and 27, using the Pi's internal pull-up resistors via gpiozero's Button(pin, pull_up=True) class. This reduces part count and wiring complexity by 50%.
Path B: Extend for Industrial I/O Density
If you need to read 16+ inputs or drive 8+ relays, you will exhaust the Pi 4's safe GPIO pins and hit the 50mA total current limit. Do not daisy-chain more direct GPIO modules. Instead, migrate to an I2C I/O expander. Add an Adafruit MCP23017 16-Channel I/O Expander (approx. $12). It connects to just two pins (GPIO 2 and 3 for I2C SDA/SCL) and handles all the pin-state logic internally, drawing minimal current from the Pi's 3.3V rail while providing robust 5V-tolerant I/O on the expander side.






