The Raspberry Pi GPIO (General Purpose Input/Output) header is a 40-pin interface that bridges the Linux environment with physical hardware. On the Raspberry Pi 5, this is managed by the dedicated RP1 southbridge chip, operating strictly at 3.3V logic. If you are wiring sensors or relays in 2026, your default stack should be a Pi 5 running Bookworm OS with the gpiozero Python library. This guide provides the exact pinout, a safe transistor-driven relay circuit, and the complete Python code to read a BME280 environmental sensor and trigger a 5V load.
The Decision Path: Which Board and GPIO Library?
Before wiring anything, you must select the right hardware and software stack. The shift from the BCM2711 chip (Pi 4) to the RP1 chip (Pi 5) changed how GPIO libraries interact with the kernel. Here is the decision framework to lock in your build:
| Use Case Scenario | Recommended Board | GPIO Library | Verdict / Concrete Pick |
|---|---|---|---|
| High-performance edge node, local dashboard, or vision AI | Raspberry Pi 5 (8GB) | gpiozero | Default Pick: Pi 5 8GB + gpiozero |
| Battery-powered, remote, or headless sensor node | Raspberry Pi Zero 2 W | gpiozero | Pick: Zero 2 W + gpiozero (disable HDMI for power savings) |
| Legacy industrial replacement (existing codebase) | Raspberry Pi 4 Model B (4GB) | RPi.GPIO | Pick: Pi 4 + RPi.GPIO (only if porting legacy code) |
RPi.GPIO library on a Raspberry Pi 5. The RP1 chip architecture causes RPi.GPIO to throw memory access errors on modern Bookworm OS. Always default to gpiozero or libgpiod for new builds.
Hardware Spec Sheet & Parts List
This build reads temperature/humidity and switches a 5V load (like a fan or solenoid). Because the Pi 5 GPIO pins can only safely source about 8mA continuously, we will use a 2N2222 NPN transistor to drive the 5V relay module, protecting the RP1 chip from back-EMF and overcurrent.
| Component | Exact Variant / Model | Approx Cost | Engineering Notes |
|---|---|---|---|
| Microcontroller | Raspberry Pi 5 (8GB) | $80.00 | Active cooling required for sustained loads. |
| Sensor | Adafruit BME280 I2C (PID 2652) | $15.00 | 3.3V native, includes onboard pull-ups. |
| Switching Module | 5V Relay Module (Optocoupler) | $6.00 | Must have JD-VCC jumper removed for true isolation. |
| Driver Transistor | 2N2222 NPN (TO-92) | $0.50 | Handles up to 800mA; perfect for relay coils. |
| Base Resistor | 1kΩ Carbon Film (1/4W) | $0.10 | Limits base current to ~2.6mA from the Pi GPIO. |
| Wiring | 22 AWG Dupont / Solid Core | $5.00 | Keep I2C lines under 6 inches to avoid capacitance issues. |
Raspberry Pi GPIO Pin Mapping & Wiring Rules
The Pi 5 pinout remains physically identical to the Pi 4 (40-pin header), but the electrical tolerances are stricter. The RP1 chip is highly sensitive to overvoltage. Never feed 5V into any GPIO pin; doing so will instantly destroy the southbridge.
| Physical Pin | BCM GPIO | Function | Connection Target |
|---|---|---|---|
| 1 | 3V3 | Power | BME280 VIN |
| 3 | GPIO 2 | I2C SDA | BME280 SDA |
| 5 | GPIO 3 | I2C SCL | BME280 SCL |
| 6 | GND | Ground | BME280 GND |
| 16 | GPIO 23 | Digital Out | 1kΩ Resistor -> 2N2222 Base |
| 2 | 5V | Power | Relay Module VCC (JD-VCC) |
| 14 | GND | Ground | Relay GND & 2N2222 Emitter |
Step-by-Step Wiring & Assembly
- De-energize the system: Unplug the Raspberry Pi 5 power supply before touching the GPIO header. Verify the power LED is off.
- Wire the BME280 Sensor: Connect Pin 1 (3.3V) to VIN, Pin 3 to SDA, Pin 5 to SCL, and Pin 6 to GND. Keep these wires short and twisted if possible to reduce I2C noise.
- Prepare the Relay Isolation: On your 5V relay module, locate the JD-VCC jumper. Remove it. This separates the relay coil power from the optocoupler logic, preventing 5V noise from backfeeding into the Pi.
- Build the Transistor Driver: Connect GPIO 23 (Pin 16) to one leg of the 1kΩ resistor. Connect the other leg to the Base (middle pin) of the 2N2222 transistor. Connect the Emitter (right pin, facing flat side) to Pin 14 (GND).
- Wire the Relay Logic Side: Connect the relay module's IN pin to the Collector (left pin) of the 2N2222. Connect the relay module's GND to Pin 14 (GND).
- Wire the Relay Power Side: Connect the relay module's JD-VCC pin to Pin 2 (5V) on the Pi. (Ensure this 5V line does not touch any GPIO pins).
- Verify with a Multimeter: Before applying power, use your multimeter in continuity mode to ensure there is no short between the 5V rail and any GPIO pins.
Complete Python Control Code
This code targets the Raspberry Pi 5 (8GB) running Raspberry Pi OS Bookworm (64-bit). It uses gpiozero for safe GPIO manipulation and smbus2 with the bme280 library for I2C sensor reading. Install dependencies via terminal: sudo apt install python3-gpiozero python3-smbus i2c-tools and pip3 install RPi.bme280.
import time
import smbus2
import bme280
from gpiozero import OutputDevice
from signal import pause
# --- PIN & ADDRESS DEFINITIONS ---
RELAY_PIN = 23 # BCM GPIO 23 (Physical Pin 16)
I2C_PORT = 1 # Default I2C bus on Pi 4 and Pi 5
BME280_ADDRESS = 0x77 # Adafruit BME280 default (check with i2cdetect)
# Initialize GPIO (Active High for NPN transistor base)
relay = OutputDevice(RELAY_PIN, active_high=True, initial_value=False)
# Initialize I2C Bus and Sensor Calibration
bus = smbus2.SMBus(I2C_PORT)
try:
calibration_params = bme280.load_calibration_params(bus, BME280_ADDRESS)
print('BME280 sensor calibrated successfully.')
except Exception as e:
print(f'FATAL: Could not initialize BME280 at 0x{BME280_ADDRESS:X}. Check wiring.')
print(f'Error details: {e}')
exit(1)
def monitor_and_control():
print('Starting environmental monitor... Press Ctrl+C to stop.')
try:
while True:
# Read sensor data
data = bme280.sample(bus, BME280_ADDRESS, calibration_params)
temp_c = data.temperature
temp_f = (temp_c * 9/5) + 32
humidity = data.humidity
print(f'Temp: {temp_f:.1f}°F | Humidity: {humidity:.1f}%')
# Decision Logic: Trigger relay if temp exceeds 80°F
if temp_f > 80.0:
if not relay.value:
print('WARNING: Temp high. Engaging relay (Fan ON).')
relay.on()
else:
if relay.value:
print('Temp nominal. Disengaging relay (Fan OFF).')
relay.off()
time.sleep(5) # 5-second polling interval
except KeyboardInterrupt:
print('\nShutdown requested by user.')
except OSError as e:
print(f'\nI2C Bus Error: {e}. Check physical connections.')
finally:
# Safe shutdown state
relay.off()
print('Relay forced OFF. GPIO cleaned up.')
if __name__ == '__main__':
monitor_and_control()
Debugging: Exact Error Strings and Ranked Causes
When working with the Raspberry Pi GPIO and I2C buses, you will inevitably hit kernel or hardware faults. Here is how to resolve the two most common blockers.
Error 1: RuntimeError: No access to /dev/mem. Try running as root!
Context: This occurs when you try to run legacy RPi.GPIO code on Raspberry Pi OS Bookworm, or when running gpiozero without proper user permissions on older OS builds.
- Cause 1 (Most Likely): Using deprecated
RPi.GPIOon a Pi 5. Fix: Rewrite the script usinggpiozeroas shown above. - Cause 2: Your user is not in the
gpiogroup. Fix: Runsudo usermod -aG gpio $USERand reboot. - Cause 3: Another process is hogging the GPIO chip. Fix: Run
sudo lsof | grep gpiochipand kill the offending PID.
Error 2: OSError: [Errno 121] Remote I/O error
Context: The Python script crashes on the bme280.sample() line. The kernel cannot complete the I2C transaction.
- Cause 1 (Most Likely): I2C address mismatch. The Adafruit BME280 defaults to
0x77, but clone boards often use0x76. Fix: Runi2cdetect -y 1in the terminal and update theBME280_ADDRESSvariable in the code. - Cause 2: Missing pull-up resistors. Fix: Ensure your BME280 breakout board has onboard pull-ups (Adafruit/SparkFun boards do; cheap generic clones often do not). Add 4.7kΩ resistors to SDA/SCL if needed.
- Cause 3: I2C bus disabled in firmware. Fix: Run
sudo raspi-config, navigate to Interface Options -> I2C, and enable it.
- Verify 3.3V Power: Put your multimeter on Pin 1 and Pin 6. You must read exactly 3.25V to 3.35V. If it reads 0V, your Pi's polyfuse is tripped or the board is dead.
- Verify I2C is Enabled: Run
ls /dev/i2c*. If/dev/i2c-1does not appear, I2C is disabled inconfig.txtorraspi-config. - Check the Transistor Pinout: 2N2222 pinouts vary by manufacturer (E-B-C vs C-B-E). Look at the flat side of your specific TO-92 package and verify Base, Collector, and Emitter against the physical datasheet.
Extending and Simplifying the Build
Depending on your project scope, you may need to scale this circuit up or strip it down.
How to Simplify:
If you only need data logging and do not need to control a physical load, delete the gpiozero relay logic entirely. Replace the relay.on() calls with CSV file appends or SQLite database inserts. This reduces the hardware to just the Pi and the BME280, eliminating the transistor and relay wiring completely.
How to Extend:
To integrate this into a smart home ecosystem, add the paho-mqtt Python library. Inside the while True loop, publish the temp_f and humidity variables to an MQTT broker (like Mosquitto running on a Home Assistant server). For remote deployments where running a full Pi 5 is overkill, port this exact Python code and wiring schematic to a Raspberry Pi Zero 2 W. The Zero 2 W shares the identical 40-pin GPIO layout and runs the same Bookworm OS, making it a drop-in replacement that draws less than 1.5W at idle.
For deeper reading on the RP1 chip architecture and official pinout specifications, refer to the Raspberry Pi Hardware Documentation. For advanced Python GPIO patterns, the gpiozero official documentation remains the definitive reference. If you are using the Adafruit BME280 breakout, consult the Adafruit BME280 Learning Guide for specific I2C addressing jumpers.






