The Raspberry Pi 5 retains the familiar 40-pin physical GPIO header layout, but the underlying architecture has fundamentally changed. Driven by the new RP1 southbridge chip, the Raspberry Pi 5 pins are no longer controlled directly by the main BCM2712 SoC. This shift means legacy libraries like RPi.GPIO are officially dead on the Pi 5; you must use gpiozero or lgpio for hardware control. Furthermore, the Pi 5 enforces strict 3.3V logic levels on all GPIO pins, meaning a 5V signal will permanently damage the RP1 chip.
This guide provides a data-dense pin mapping table, a complete I2C sensor build, and a targeted debugging framework for the most common Pi 5 hardware errors.
The Raspberry Pi 5 40-Pin Header: RP1 Changes and Pin Mapping
While the physical footprint of the header is identical to the Pi 4, the RP1 chip handles all peripheral routing. This results in lower latency for GPIO toggling and native support for higher-speed interfaces, but it also changes how the OS maps the /dev/gpiochip devices. Below is the critical pin mapping table for the most frequently used interfaces on the Pi 5.
| Physical Pin | BCM GPIO | Primary Function | Pi 5 / RP1 Specific Notes |
|---|---|---|---|
| 1 | N/A | 3V3 Power | Max draw increased to 1.2A on Pi 5 (up from 0.8A on Pi 4). |
| 2 | N/A | 5V Power | Direct from USB-C PD input. Use for high-current sensors only. |
| 3 | GPIO 2 | I2C1 SDA | Requires 4.7kΩ pull-up to 3V3 if breakout lacks internal resistors. |
| 5 | GPIO 3 | I2C1 SCL | Default I2C bus. Maps to /dev/i2c-1 in Bookworm OS. |
| 7 | GPIO 4 | GPCLK0 / GPIO | Useful for hardware PWM fallback or generic digital input. |
| 8 | GPIO 14 | UART0 TX | Console serial output. Disable in raspi-config for sensor use. |
| 10 | GPIO 15 | UART0 RX | Strict 3.3V. Never connect a 5V USB-Serial adapter directly. |
| 12 | GPIO 18 | PWM0 / GPIO | Hardware PWM0. Ideal for driving MOSFETs for LED dimming. |
| 19 | GPIO 10 | SPI0 MOSI | RP1 SPI0 bus. Maps to /dev/spidev0.0. |
| 21 | GPIO 9 | SPI0 MISO | RP1 SPI0 bus. Ensure CS (Pin 24) is driven low before clocking. |
Project Build: I2C Environment Monitor with Hardware Interrupt
To demonstrate the Pi 5 pins in action, we will build a desktop environment monitor. This project reads temperature and humidity via I2C and uses a hardware button interrupt to toggle an alert LED. This code specifically targets the Raspberry Pi 5 8GB (or 4GB) running Raspberry Pi OS Bookworm (64-bit).
Parts List & Pricing (2026 Estimates)
- Raspberry Pi 5 8GB (~$80) with Active Cooler (~$5)
- BME280 I2C Breakout (Adafruit 2652 or generic 3.3V variant, ~$10-$15)
- 12mm Tactile Switch (~$0.50)
- 5mm Red LED with 330Ω current-limiting resistor (~$0.20)
- Dupont Wires (Female-to-Female and Male-to-Female, ~$6)
Wiring Steps
- Power the Sensor: Connect BME280
VINto Pi 5 Pin 1 (3V3). Connect BME280GNDto Pi 5 Pin 6 (GND). - I2C Data Lines: Connect BME280
SDAto Pi 5 Pin 3 (GPIO 2). Connect BME280SCLto Pi 5 Pin 5 (GPIO 3). - Button Input: Connect one leg of the tactile switch to Pi 5 Pin 11 (GPIO 17). Connect the other leg to Pi 5 Pin 9 (GND). Note: We will enable the internal pull-up resistor in software.
- LED Output: Connect the LED anode (long leg) to Pi 5 Pin 12 (GPIO 18) via the 330Ω resistor. Connect the cathode (short leg) to Pi 5 Pin 14 (GND).
Python Code: Reading Sensors and Handling Button Presses
Before running the code, install the required dependencies in your virtual environment. The Pi 5 Bookworm OS strongly discourages global pip installs. Run these commands in your terminal:
python3 -m venv ~/env_monitor
source ~/env_monitor/bin/activate
pip install gpiozero smbus2 bme280 lgpio
Save the following script as monitor.py. This script includes explicit pin definitions, hardware debouncing, and robust I2C error handling.
#!/usr/bin/env python3
import time
import sys
from gpiozero import Button, LED
from smbus2 import SMBus
import bme280
# --- PIN DEFINITIONS (BCM Numbering) ---
BUTTON_PIN = 17 # Physical Pin 11
LED_PIN = 18 # Physical Pin 12
I2C_BUS = 1 # Physical Pins 3 (SDA) and 5 (SCL)
BME280_ADDR = 0x76 # Default for Adafruit/generic; some are 0x77
def main():
# Initialize GPIO with internal pull-up for the button
alert_button = Button(BUTTON_PIN, pull_up=True, bounce_time=0.05)
alert_led = LED(LED_PIN)
alert_active = False
def toggle_alert():
nonlocal alert_active
alert_active = not alert_active
if alert_active:
alert_led.on()
print('[INTERRUPT] Alert activated!')
else:
alert_led.off()
print('[INTERRUPT] Alert deactivated.')
# Attach hardware interrupt for button press
alert_button.when_pressed = toggle_alert
# Initialize I2C and BME280
try:
bus = SMBus(I2C_BUS)
calibration_params = bme280.load_calibration_params(bus, BME280_ADDR)
print(f'Successfully connected to BME280 at I2C address {hex(BME280_ADDR)}')
except FileNotFoundError:
print(f'FATAL: I2C bus {I2C_BUS} not found. Enable I2C in raspi-config.')
sys.exit(1)
except OSError as e:
print(f'FATAL I2C Error: {e}')
print('Check wiring, pull-up resistors, and I2C address.')
sys.exit(1)
print('Monitoring environment. Press the button to toggle alert LED. Ctrl+C to exit.')
try:
while True:
try:
data = bme280.sample(bus, BME280_ADDR, calibration_params)
temp_c = data.temperature
humidity = data.humidity
status = '🚨 ALERT' if alert_active else '✅ NORMAL'
print(f'[{status}] Temp: {temp_c:.2f}°C | Humidity: {humidity:.1f}%')
# Auto-trigger alert if temp exceeds 30C
if temp_c > 30.0 and not alert_active:
toggle_alert()
except OSError as e:
print(f'Read Error: {e}. Retrying in 5s...')
time.sleep(2)
except KeyboardInterrupt:
print('\nShutting down gracefully...')
finally:
alert_led.off()
bus.close()
if __name__ == '__main__':
main()
Debugging: Fixing 'OSError: [Errno 121] Remote I/O error'
The most notorious error when working with Raspberry Pi 5 pins and I2C sensors is the OSError: [Errno 121] Remote I/O error. This is a kernel-level rejection indicating the Pi sent a clock pulse on the SCL line, but the sensor failed to acknowledge (ACK) by pulling the SDA line low.
Ranked Causes and Fixes
- Wrong I2C Address (Most Common): The BME280 can be configured to 0x76 or 0x77 depending on the breakout board manufacturer. If your board uses 0x77 and your code specifies 0x76, the Pi will throw Errno 121. Fix: Run
i2cdetect -y 1in the terminal and update theBME280_ADDRvariable in the code. - Missing Pull-Up Resistors: I2C is an open-drain protocol. The lines must be pulled high to 3.3V. While the Pi 5 has internal pull-ups, they are often too weak (~50kΩ) for reliable I2C communication at standard speeds. Fix: Ensure your breakout board has 4.7kΩ or 10kΩ physical resistors on the SDA and SCL lines. If not, solder them between the 3V3 and SDA/SCL pins.
- SDA and SCL Swapped: Unlike UART, swapping I2C lines won't just result in garbage data; it will cause a hard bus lockup or Errno 121. Fix: Verify Pin 3 is SDA and Pin 5 is SCL using a multimeter continuity test against the breakout board silkscreen.
- Run the bus scan: Execute
i2cdetect -y 1. If you see a grid of dashes and no hex addresses (like 76 or 77), you have a physical wiring or power issue, not a code issue. - Verify VCC voltage: Use a multimeter to measure the voltage between the sensor's VCC and GND pins. It must read exactly 3.2V to 3.4V. If it reads 0V, your jumper wire is broken or you plugged it into a 5V pin by mistake.
- Check OS configuration: Run
sudo raspi-config-> Interface Options -> I2C. Ensure it is explicitly enabled. On a fresh Pi 5 Bookworm install, I2C is disabled by default.
Extending and Simplifying the Build
Depending on your project goals, you may need to scale this hardware setup up or down. Here is how to adapt the Raspberry Pi 5 pins for different use cases.
How to Extend the Build
- Add an OLED Display: Wire an SSD1306 128x64 I2C OLED to the exact same SDA (Pin 3) and SCL (Pin 5) lines. I2C supports multiple devices on the same bus as long as their addresses don't clash (the SSD1306 uses 0x3C, which is safe alongside the BME280's 0x76).
- Network the Data via MQTT: Install
paho-mqttvia pip. Add a function inside thewhile Trueloop to publishtemp_candhumidityto a local Mosquitto broker. This turns your Pi 5 into a smart-home environmental node for Home Assistant. - Add a 5V Relay for Active Cooling: Since the Pi 5 GPIO pins cannot source enough current to drive a relay coil (and are strictly 3.3V), use Pin 12 (GPIO 18) to drive a 3.3V logic-level MOSFET (like the IRLZ44N), which in turn switches the 5V relay.
How to Simplify the Build
- Drop the Hardware Interrupt: If you don't need the physical button, remove the
gpiozeroButton initialization and thetoggle_alertcallback. Rely entirely on the software threshold (if temp_c > 30.0) to trigger the LED. - Use Polling over I2C Libraries: If installing
pippackages in a restricted environment is problematic, you can strip the script down to use rawsmbus2to read just the uncompensated temperature registers, bypassing the need for thebme280calibration wrapper entirely (though accuracy will drop by roughly ±1°C).
Understanding the transition from the legacy BCM controller to the RP1 southbridge is the key to mastering the Raspberry Pi 5 pins. By respecting the 3.3V logic limits, utilizing modern libraries like gpiozero, and systematically debugging I2C bus errors, you can build highly reliable embedded systems on the Pi 5 platform.






