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 ScenarioRecommended BoardGPIO LibraryVerdict / Concrete Pick
High-performance edge node, local dashboard, or vision AIRaspberry Pi 5 (8GB)gpiozeroDefault Pick: Pi 5 8GB + gpiozero
Battery-powered, remote, or headless sensor nodeRaspberry Pi Zero 2 WgpiozeroPick: Zero 2 W + gpiozero (disable HDMI for power savings)
Legacy industrial replacement (existing codebase)Raspberry Pi 4 Model B (4GB)RPi.GPIOPick: Pi 4 + RPi.GPIO (only if porting legacy code)
Bench Tip: Never use the legacy 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.

ComponentExact Variant / ModelApprox CostEngineering Notes
MicrocontrollerRaspberry Pi 5 (8GB)$80.00Active cooling required for sustained loads.
SensorAdafruit BME280 I2C (PID 2652)$15.003.3V native, includes onboard pull-ups.
Switching Module5V Relay Module (Optocoupler)$6.00Must have JD-VCC jumper removed for true isolation.
Driver Transistor2N2222 NPN (TO-92)$0.50Handles up to 800mA; perfect for relay coils.
Base Resistor1kΩ Carbon Film (1/4W)$0.10Limits base current to ~2.6mA from the Pi GPIO.
Wiring22 AWG Dupont / Solid Core$5.00Keep 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 PinBCM GPIOFunctionConnection Target
13V3PowerBME280 VIN
3GPIO 2I2C SDABME280 SDA
5GPIO 3I2C SCLBME280 SCL
6GNDGroundBME280 GND
16GPIO 23Digital Out1kΩ Resistor -> 2N2222 Base
25VPowerRelay Module VCC (JD-VCC)
14GNDGroundRelay GND & 2N2222 Emitter

Step-by-Step Wiring & Assembly

  1. De-energize the system: Unplug the Raspberry Pi 5 power supply before touching the GPIO header. Verify the power LED is off.
  2. 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.
  3. 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.
  4. 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).
  5. 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).
  6. 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).
  7. 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.GPIO on a Pi 5. Fix: Rewrite the script using gpiozero as shown above.
  • Cause 2: Your user is not in the gpio group. Fix: Run sudo usermod -aG gpio $USER and reboot.
  • Cause 3: Another process is hogging the GPIO chip. Fix: Run sudo lsof | grep gpiochip and 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 use 0x76. Fix: Run i2cdetect -y 1 in the terminal and update the BME280_ADDRESS variable 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.
The First 3 Things to Check When It Fails:
  1. 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.
  2. Verify I2C is Enabled: Run ls /dev/i2c*. If /dev/i2c-1 does not appear, I2C is disabled in config.txt or raspi-config.
  3. 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.