The 40-pin GPIO header on the Raspberry Pi is the gateway to physical computing, but it is also the most common point of failure for embedded projects. The physical pin diagram of Raspberry Pi boards (from the Pi 3B+ through the current Pi 4 Model B and Pi 5) shares an identical layout, but confusing physical pin numbers with Broadcom (BCM) GPIO numbers will result in dead sensors, unresponsive code, or a permanently fried SoC. This guide targets the Raspberry Pi 4 Model B (4GB) and Raspberry Pi 5 (4GB), mapping the physical header to BCM logic, walking through a robust I2C sensor build, and debugging the exact errors that halt development.

The 40-Pin Header: Physical vs. BCM Mapping

When you look at the pinout.xyz reference or the official Raspberry Pi GPIO documentation, you will see two numbering schemes. Physical (BOARD) numbering simply counts pins 1 through 40, starting from the top-left (closest to the USB-C power port) and zig-zagging down. BCM (Broadcom) numbering refers to the internal SoC GPIO channel numbers. Python libraries like RPi.GPIO and gpiozero default to BCM. If you wire a sensor to Physical Pin 11 but tell your code to read BCM 11, you are reading the wrong silicon trace.

Critical Pin Mapping Reference (Top 10 Rows)
Physical Pin BCM GPIO Function / Name Bench Notes & Warnings
1N/A3.3V PowerMax draw ~50mA total across all 3.3V pins. Use for logic power.
2N/A5V PowerTied directly to USB-C input. Can backpower the board if fed externally.
32 (SDA1)I2C DataHas onboard 1.8kΩ pull-up to 3.3V. Strictly 3.3V logic.
4N/A5V PowerSame as Pin 2.
53 (SCL1)I2C ClockHas onboard 1.8kΩ pull-up to 3.3V. Strictly 3.3V logic.
6N/AGround (GND)Common ground for all sensors and power supplies.
74 (GPCLK0)GPIO 4Often used for 1-Wire (DS18B20) by default in raspi-config.
814 (TXD)UART TXPrimary serial console. Disable serial console in raspi-config to use for hardware UART.
9N/AGround (GND)Ground.
1015 (RXD)UART RXPrimary serial console receive.
1117GPIO 17Standard GPIO. Safe for PWM and digital I/O.
⚠️ Critical Warning: Never feed 5V into any BCM GPIO pin (like BCM 17 on Physical Pin 11). The Pi SoC is strictly 3.3V tolerant. Forcing 5V into a GPIO pin will instantly destroy the internal clamping diodes and likely kill the CPU. Always use a logic level shifter if interfacing with 5V Arduino modules.

Project Build: I2C Environmental Monitor with Status LED

To put the pin diagram into practice, we will build an environmental monitor that reads temperature and pressure via I2C, and toggles a status LED via standard GPIO. This forces you to use the power rails, the dedicated I2C pins, and a standard BCM GPIO pin simultaneously.

Parts List

  • Board: Raspberry Pi 4 Model B (4GB) or Raspberry Pi 5 (4GB)
  • Sensor: Adafruit BME280 I2C/SPI Breakout (Product ID: 2652) or equivalent generic BME280 module
  • Indicator: Standard 5mm Red LED
  • Resistor: 330Ω (to limit LED current to ~10mA from the 3.3V rail)
  • Wiring: Female-to-Female and Male-to-Female jumper wires, half-size breadboard

Wiring Pin Mapping

Component PinRaspberry Pi Physical PinBCM GPIO / Rail
BME280 VIN13.3V
BME280 GND6GND
BME280 SCK (SCL)5BCM 3 (SCL1)
BME280 SDI (SDA)3BCM 2 (SDA1)
LED Anode (+)11BCM 17
LED Cathode (-)9GND (via 330Ω resistor)

Numbered Wiring Steps

  1. De-energize: Unplug the Raspberry Pi USB-C power supply. Never wire the GPIO header while the board is live.
  2. Enable I2C: Boot the Pi, open terminal, run sudo raspi-config, navigate to Interface Options > I2C, and enable it. Reboot.
  3. Wire the Sensor: Connect the BME280 VIN to Physical Pin 1 (3.3V). Connect GND to Physical Pin 6. Connect SCK to Pin 5 and SDI to Pin 3.
  4. Verify I2C Address: Power up and run i2cdetect -y 1. You should see 76 or 77 in the grid. Adafruit boards default to 0x77; generic clones often default to 0x76.
  5. Wire the LED: Place the 330Ω resistor on the breadboard. Connect Physical Pin 11 (BCM 17) to the LED anode. Connect the LED cathode through the resistor to Physical Pin 9 (GND).

Complete Python Code with Error Handling

This script uses RPi.GPIO for the LED and smbus2 paired with the bme280 library for the sensor. Install dependencies first: sudo apt install python3-smbus i2c-tools && pip3 install RPi.GPIO RPi.bme280.

import smbus2
import bme280
import RPi.GPIO as GPIO
import time
import sys

# --- PIN DEFINITIONS (BCM MODE) ---
LED_PIN = 17          # BCM 17 = Physical Pin 11
I2C_BUS_ID = 1        # /dev/i2c-1 corresponds to Physical Pins 3 & 5
BME280_ADDRESS = 0x76 # Change to 0x77 if using Adafruit official breakout

def setup_gpio():
    GPIO.setmode(GPIO.BCM)
    GPIO.setwarnings(False)
    GPIO.setup(LED_PIN, GPIO.OUT, initial=GPIO.LOW)

def read_sensor(bus, address, calibration_params):
    try:
        data = bme280.sample(bus, address, calibration_params)
        return data.temperature, data.pressure, data.humidity
    except OSError as e:
        print(f'Hardware I/O Error: {e}')
        return None, None, None

def main():
    setup_gpio()
    
    try:
        bus = smbus2.SMBus(I2C_BUS_ID)
        # Load calibration parameters for the specific sensor silicon
        calibration_params = bme280.load_calibration_params(bus, BME280_ADDRESS)
        print('Sensor initialized successfully.')
    except FileNotFoundError:
        print('Error: I2C bus not found. Did you enable I2C in raspi-config?')
        sys.exit(1)
    except OSError as e:
        print(f'Error: Cannot reach sensor at 0x{BME280_ADDRESS:02x}. Check wiring. ({e})')
        sys.exit(1)

    try:
        while True:
            temp, pres, hum = read_sensor(bus, BME280_ADDRESS, calibration_params)
            
            if temp is not None:
                print(f'Temp: {temp:.1f}C | Press: {pres:.0f}hPa | Hum: {hum:.1f}%')
                # Blink LED to indicate successful read
                GPIO.output(LED_PIN, GPIO.HIGH)
                time.sleep(0.2)
                GPIO.output(LED_PIN, GPIO.LOW)
            else:
                print('Read failed. Retrying...')
                # Solid LED to indicate error state
                GPIO.output(LED_PIN, GPIO.HIGH)
                
            time.sleep(2)
            
    except KeyboardInterrupt:
        print('\nInterrupted by user.')
    finally:
        # CRITICAL: Always clean up GPIO to prevent pin state lockups
        GPIO.cleanup()
        print('GPIO cleaned up. Exiting.')

if __name__ == '__main__':
    main()

Debugging GPIO and Pinout Failures

When your build fails, do not guess. Follow this decision path based on the exact error string thrown by the Python interpreter.

The First Three Things to Check

  1. Run i2cdetect -y 1: If the grid is empty, your I2C wiring is wrong, or you are querying the wrong bus (Pi 1 uses bus 0, Pi 4/5 use bus 1).
  2. Verify Numbering Mode: Check if your code says GPIO.setmode(GPIO.BCM) or GPIO.setmode(GPIO.BOARD). If it is BCM, your variables must be BCM numbers (e.g., 17), not physical numbers (e.g., 11).
  3. Check Power Rails: Use a multimeter to verify 3.2V-3.3V between Physical Pin 1 and Physical Pin 6. If it reads 0V, your Pi's polyfuse may have tripped or the board is unpowered.

Ranked Causes for Exact Error Strings

Error String: OSError: [Errno 121] Remote I/O error

  • Cause 1 (Most Likely): Incorrect I2C address. The code expects 0x76, but the sensor is strapped to 0x77. Fix: Change BME280_ADDRESS in the code.
  • Cause 2: SDA and SCL swapped. Fix: Swap the wires on Physical Pins 3 and 5.
  • Cause 3: Missing pull-up resistors on a generic clone sensor. The Pi has internal 1.8kΩ pull-ups, but long wires (>10cm) cause capacitance issues. Fix: Add external 4.7kΩ pull-ups to 3.3V.

Error String: RuntimeError: No access to /dev/mem. Try running as root!

  • Cause 1: Running an outdated version of RPi.GPIO on a newer Pi OS kernel. Fix: Update via sudo apt update && sudo apt install python3-rpi.gpio.
  • Cause 2: Permissions issue on older OS versions. Fix: Run the script with sudo python3 script.py.

Error String: ValueError: The channel sent is invalid on a Raspberry Pi

  • Cause 1: You passed a physical pin number (like 11) while the code is set to GPIO.BCM mode. BCM 11 does not exist on the Pi 4/5 header (it maps to Physical Pin 26, which is BCM 7). Fix: Change the pin variable to 17.

Extending and Simplifying the Build

To Simplify: If raw RPi.GPIO and smbus2 feel too verbose, switch to the gpiozero and adafruit-circuitpython-bme280 libraries. gpiozero abstracts away the cleanup routines and BCM/BOARD confusion by accepting both natively (e.g., LED('GPIO17') or LED('BOARD11')).

To Extend: The I2C bus supports up to 127 devices, but you will quickly run into address collisions (e.g., adding a second BME280, since both default to 0x76/0x77). To extend the build with multiple identical sensors, insert a TCA9548A I2C Multiplexer (approx. $6) between the Pi and the sensors. The multiplexer sits at 0x70 and routes the SDA/SCL lines to 8 separate downstream channels, allowing you to read eight identical sensors on one bus.

Frequently Asked Questions

Is the pin diagram of Raspberry Pi 5 different from the Pi 4?

Physically, the 40-pin header layout is identical. Pin 1 is still 3.3V, Pin 2 is 5V, and the I2C/GPIO mappings remain exactly the same. However, the Pi 5 introduced a dedicated JST connector for PCIe and a separate UART debug connector. Furthermore, the Pi 5 uses the RP1 southbridge chip to handle GPIO, which changed the underlying software drivers, though Python libraries like gpiozero and RPi.GPIO have been updated to abstract this hardware change away from the user.

Which pins are strictly 3.3V and which are 5V on the Raspberry Pi pinout?

Physical Pins 1 and 17 output 3.3V. Physical Pins 2 and 4 output 5V. Every single BCM GPIO pin (Pins 3, 5, 7, 8, 10, 11, etc.) operates at 3.3V logic levels. Never apply more than 3.3V to any GPIO pin, and never use the 5V pins as a logic HIGH signal for a sensor.

Why does my Raspberry Pi reboot when I connect a 5V sensor to a GPIO pin?

If you connect a 5V output to a 3.3V GPIO input, the excess voltage forward-biases the SoC's internal ESD protection diodes. This dumps current directly into the 3.3V rail, causing the rail voltage to spike. The Pi's power management IC (PMIC) detects this overvoltage anomaly and triggers a hard brownout reset to protect the silicon. Repeated offenses will permanently short the diode and destroy the board.

How do I read the physical pin diagram without counting manually?

Open the terminal on your Raspberry Pi and type pinout. This built-in command (part of the gpiozero package) prints a beautiful, color-coded ASCII art diagram of your specific Pi model's header directly to the console, complete with BCM numbers, physical numbers, and power rails. It is the fastest way to verify a pin before wiring it on the bench.