The Core Raspberry Pi 5 GPIO & Interface Cheat Sheet

When you are staring at a 40-pin header with a handful of jumper wires, you don't need a 50-page manual; you need a reference that tells you exactly which physical pin maps to which BCM (Broadcom) GPIO, what the default function is, and what the hardware limits are. This raspberry pi cheat sheet is engineered for the Raspberry Pi 5 Model B (8GB RAM) running Raspberry Pi OS (64-bit, Bookworm or later). While the physical pinout remains backward-compatible with the Pi 4 Model B, the Pi 5 utilizes the new RP1 southbridge chip, which changes kernel-level GPIO mapping and slightly alters power delivery characteristics on the 5V rail.

Board Variant Target: Raspberry Pi 5 Model B (8GB). Note: If using a Pi 4, the pinout is identical, but the 5V rail can supply less total current before triggering the polyfuse (typically 1.2A on Pi 4 vs 2A+ on Pi 5 with a 27W PD PSU).

40-Pin Header Quick Reference (Most Used Pins)

Physical Pin BCM GPIO Default Function Alt0 / Special Max Continuous Current
1N/A3.3V PowerN/A50mA (Total 3.3V limit)
2N/A5V PowerN/ADepends on PSU (up to 2A)
32SDA1 (I2C)GPIO216mA (per pin)
53SCL1 (I2C)GPIO316mA (per pin)
6N/AGroundN/AN/A
814TXD (UART)GPIO1416mA (per pin)
1015RXD (UART)GPIO1516mA (per pin)
1910MOSI (SPI0)GPIO1016mA (per pin)
219MISO (SPI0)GPIO916mA (per pin)
2311SCLK (SPI0)GPIO1116mA (per pin)

Source: Raspberry Pi Official Hardware Documentation. Always assume a safe limit of 16mA per GPIO pin and 50mA total from the 3.3V rail unless using an external LDO.

Benchmark Build: I2C Environmental Monitor (BME280 + SSD1306)

To put this cheat sheet into practice, we will wire a classic I2C sensor and display combo. This build reads temperature, humidity, and pressure, then renders it on a local OLED. It is the perfect testbed for verifying I2C bus integrity and pull-up resistor behavior.

Parts List & Exact Variants

Component Exact Model / Part Number Est. 2026 Price Hardware Notes
MicrocontrollerRaspberry Pi 5 Model B (8GB)$80.00Requires 27W USB-C PD PSU for full peripheral support.
SensorAdafruit BME280 (PID: 2652)$19.50Includes onboard 3.3V LDO and 10k pull-ups. I2C addr: 0x77 (default) or 0x76.
DisplayAdafruit SSD1306 128x64 I2C (PID: 326)$19.95Monochrome OLED. I2C addr: 0x3C.
Wiring28 AWG Silicone Jumper Wires$8.00Use silicone, not PVC, to prevent melting near the Pi 5 CPU.

Pin Mapping Table

The Pi 5's I2C1 bus is the default hardware I2C interface. Wire the modules exactly as follows:

Pi 5 Physical Pin BCM / Function Wire Color Target Module Pin
13.3V PowerRedVIN / VCC (Both Modules)
6GroundBlackGND (Both Modules)
3BCM 2 (SDA1)BlueSDA (Both Modules)
5BCM 3 (SCL1)YellowSCL (Both Modules)
Warning: Never connect the VIN pin of the BME280 or SSD1306 to Physical Pin 2 (5V) unless the module specifically has a 5V-tolerant voltage regulator. The Adafruit breakouts listed above do, but generic eBay clones often route 5V directly to the 3.3V logic IC, which will instantly fry the sensor and potentially backfeed 5V into the Pi's SDA line, destroying the RP1 southbridge.

Compilable Python Code with Hardware Error Handling

Before running this code, ensure I2C is enabled via sudo raspi-config (Interface Options > I2C) and install the required Adafruit Blinka libraries: pip3 install adafruit-circuitpython-bme280 adafruit-circuitpython-ssd1306 pillow.

import board
import busio
import adafruit_bme280
import adafruit_ssd1306
from PIL import Image, ImageDraw, ImageFont
import time
import sys
import os

# --- Pin Definitions (BCM mapping via board module) ---
# Physical Pin 3 (BCM 2) -> SDA
I2C_SDA = board.SDA
# Physical Pin 5 (BCM 3) -> SCL
I2C_SCL = board.SCL

# I2C Addresses (Verify with `i2cdetect -y 1`)
BME280_ADDR = 0x76  # Adafruit BME280 default is often 0x77, check your board
OLED_ADDR = 0x3C

def initialize_hardware():
    """Initialize I2C bus and sensors with strict error handling."""
    try:
        # Create I2C bus object
        i2c = busio.I2C(I2C_SCL, I2C_SDA)
        
        # Initialize BME280
        bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c, address=BME280_ADDR)
        bme280.sea_level_pressure = 1013.25
        
        # Initialize SSD1306 OLED (128x64)
        oled = adafruit_ssd1306.SSD1306_I2C(128, 64, i2c, addr=OLED_ADDR)
        oled.fill(0)
        oled.show()
        
        return bme280, oled
        
    except ValueError as e:
        print(f"[FATAL] Hardware Config Error: {e}")
        print("Action: Verify BME280_ADDR matches your physical board (0x76 vs 0x77).")
        sys.exit(1)
    except OSError as e:
        if e.errno == 121:
            print("[FATAL] OSError: [Errno 121] Remote I/O error.")
            print("Action: I2C bus lockup or missing device. Check physical wiring.")
        else:
            print(f"[FATAL] Unexpected OS Error: {e}")
        sys.exit(1)
    except RuntimeError as e:
        print(f"[FATAL] RuntimeError: {e}")
        sys.exit(1)

def render_display(oled, temp, hum, pres):
    """Render sensor data to the OLED screen."""
    image = Image.new('1', (oled.width, oled.height))
    draw = ImageDraw.Draw(image)
    
    # Use default font for maximum compatibility
    font = ImageFont.load_default()
    
    draw.text((0, 0), f"Temp: {temp:.1f} C", font=font, fill=255)
    draw.text((0, 20), f"Hum:  {hum:.1f} %", font=font, fill=255)
    draw.text((0, 40), f"Pres: {pres:.0f} hPa", font=font, fill=255)
    
    oled.image(image)
    oled.show()

if __name__ == "__main__":
    print("Initializing I2C peripherals...")
    bme, display = initialize_hardware()
    print("Hardware online. Logging data...")
    
    try:
        while True:
            t = bme.temperature
            h = bme.relative_humidity
            p = bme.pressure
            
            print(f"T: {t:.2f}C | H: {h:.2f}% | P: {p:.2f}hPa")
            render_display(display, t, h, p)
            
            time.sleep(2.0)
    except KeyboardInterrupt:
        print("\nGraceful shutdown. Clearing OLED.")
        display.fill(0)
        display.show()
        sys.exit(0)

Debugging Matrix: Exact Error Strings and Ranked Causes

Hardware I2C on the Raspberry Pi is notoriously unforgiving if the physical layer is compromised. When your script crashes, look at the exact traceback string and consult this matrix. Source: Adafruit BME280 Wiring & Test Guide.

Exact Error String Rank Root Cause Hardware / Software Fix
OSError: [Errno 121] Remote I/O error 1 I2C Bus Lockup (SDA line held low by slave device). Power cycle both the Pi and the sensor simultaneously. If persistent, add 4.7kΩ external pull-up resistors to SDA/SCL.
OSError: [Errno 121] Remote I/O error 2 Address mismatch (Code expects 0x76, hardware is 0x77). Run i2cdetect -y 1. Update BME280_ADDR in the script to match the hex output.
RuntimeError: No I2C device at address: 0x3C 1 OLED module is dead or wired to the wrong SDA/SCL pins. Verify continuity from Pi Pin 3 to OLED SDA with a multimeter. Check for cold solder joints on the OLED header.
ModuleNotFoundError: No module named 'adafruit_bme280' 1 Library installed in user space, but script run as root/sudo. Run sudo pip3 install adafruit-circuitpython-bme280 or use a virtual environment (venv) consistently.

The First Three Things to Check When I2C Fails

Before rewriting your Python code or blaming the RP1 chip, perform this physical-layer triage:

  1. Run the Bus Scan: Execute sudo i2cdetect -y 1 in the terminal. If the grid is entirely empty (just dashes), your issue is 100% physical (wiring, power, or dead module). If you see UU, the kernel driver has already claimed the device, and user-space Python will be blocked.
  2. Verify VCC Voltage: Put your multimeter probes on the sensor's VCC and GND pins. It must read exactly 3.3V (±0.1V). If it reads 0V, your Pi's 3.3V LDO is starved or a jumper wire is broken. If it reads 5V, you wired it to Pin 2 and likely damaged the sensor.
  3. Check Pull-Up Resistors: The Raspberry Pi has internal 1.8kΩ pull-ups on SDA/SCL, but long wires or multiple modules can drag the bus capacitance too high. If i2cdetect shows ghost addresses (e.g., 0x30 through 0x37 all lighting up), your bus is floating. Add external 4.7kΩ pull-up resistors to the 3.3V rail.

Scaling the Build: Extensions and Simplifications

Once the baseline I2C monitor is stable, you can adapt the architecture to fit your specific project constraints.

How to Simplify the Build

If you don't need real-time local visual feedback and want to reduce the BOM cost and I2C bus capacitance:

  • Drop the OLED: Remove the SSD1306 entirely. Modify the Python script to append the sensor readings to a local CSV file (/var/log/environmental.csv) with a timestamp.
  • Automate via Cron: Instead of a continuous while True loop, rewrite the script to execute a single read-and-log cycle. Add it to your crontab (crontab -e) to run every 5 minutes: */5 * * * * /usr/bin/python3 /home/pi/sensor_log.py. This frees up the CPU and allows the Pi to sleep between reads.

How to Extend the Build

To turn this bench test into a production-grade IoT node:

  • Add MQTT Telemetry: Install paho-mqtt. Wrap the sensor read loop in an MQTT publisher that pushes JSON payloads to a Mosquitto broker. This allows Home Assistant to auto-discover the Pi as an environmental sensor node without polling.
  • Implement Hardware Watchdogs: The Pi 5 features a dedicated hardware watchdog timer. Use the watchdog Python library or systemd's built-in watchdog to automatically reboot the Pi if the I2C bus locks up and the script hangs, ensuring 99.9% uptime for remote deployments.
  • Switch to SPI: If you need to add a third I2C device and the bus capacitance exceeds 400pF (causing data corruption), move the OLED to the SPI0 bus (Physical pins 19, 21, 23, 24). SPI is push-pull and immune to the pull-up capacitance limits that plague multi-drop I2C networks.