The Raspberry Pi 40-pin header is the physical bridge between the BCM2711 (or BCM2837) SoC and the real world. The direct answer to navigating this header is understanding that Physical Pin 1 is 3.3V, Physical Pin 2 is 5V, and all GPIO logic is strictly 3.3V. Misidentifying these power rails or feeding 5V into a GPIO pin will instantly destroy the silicon. Whether you are wiring a simple LED or debugging a stubborn I2C bus, mastering the Raspberry Pi pin out is the prerequisite for every embedded project on the bench.
The 40-Pin Header Reality: Voltages and Tolerances
Before connecting a single jumper wire, you must internalize the voltage domains of the Raspberry Pi 4 Model B (and the identical layout on the 3B+). The header is split into three distinct electrical domains:
- 3.3V Power Rails (Pins 1, 17): These are fed by the onboard 3.3V regulator. They can supply a maximum of roughly 50mA to 300mA total across all pins, depending on your power supply headroom. Use these to power low-current logic chips or sensor breakouts.
- 5V Power Rails (Pins 2, 4): Tied directly to the USB-C power input (minus the polyfuse drop). These can supply the full current capacity of your wall adapter (e.g., 3A on a Pi 4), minus what the board itself consumes. Use these for motors, relays, and high-draw peripherals.
- 3.3V Logic GPIOs: Every BCM GPIO pin operates at 3.3V. The absolute maximum voltage tolerance on any GPIO pin is 3.6V. Feeding 5V into BCM GPIO 17 will forward-bias the internal ESD protection diodes, melting the trace or killing the SoC outright. If you need to interface with 5V logic (like an Arduino Uno or a 5V relay module), you must use a logic level shifter or an optocoupler.
Critical Interfaces: I2C, SPI, and UART Pin Mapping
While you can bit-bang protocols on almost any GPIO, the Raspberry Pi has dedicated hardware peripherals mapped to specific physical pins. Below is the data-dense mapping for the primary communication buses. Bookmark this table; it is the most referenced chart on the workbench.
| Physical Pin | BCM GPIO | Function / Protocol | Hardware Notes & Constraints |
|---|---|---|---|
| 1 | - | 3.3V Power | Max ~300mA draw. Do not use for motors. |
| 3 | 2 | I2C1 SDA | Requires 4.7kΩ pull-up to 3.3V (often on breakout). |
| 5 | 3 | I2C1 SCL | I2C bus 1. Default clock is 100kHz. |
| 8 | 14 | UART TXD | Serial console by default. Must disable in raspi-config for GPIO use. |
| 10 | 15 | UART RXD | 3.3V tolerant. Do not connect directly to RS-232. |
| 19 | 10 | SPI0 MOSI | Master Out Slave In. Data from Pi to peripheral. |
| 21 | 9 | SPI0 MISO | Master In Slave Out. Data from peripheral to Pi. |
| 23 | 11 | SPI0 SCLK | SPI Clock. Can run up to 125MHz (practically ~30MHz for reliable wiring). |
| 24 | 8 | SPI0 CE0 | Chip Enable 0. Active LOW. |
| 26 | 7 | SPI0 CE1 | Chip Enable 1. Active LOW. |
For a complete visual map of all 40 pins, including the DPI and PCM alternate functions, refer to the definitive pinout.xyz interactive diagram.
Project Build: BME280 I2C Environmental Sensor
To put this pin out into practice, we will wire a Bosch BME280 sensor via the I2C1 bus. This setup reads temperature, humidity, and barometric pressure.
Parts List
- Board: Raspberry Pi 4 Model B (4GB variant) running Raspberry Pi OS (Bookworm or later).
- Sensor: Adafruit BME280 I2C/SPI Breakout (Product ID: 2652). Note: This board includes the required 4.7kΩ pull-up resistors.
- Interface: Adafruit T-Cobbler Plus (Product ID: 1754) and half-size breadboard.
- Wiring: Female-to-male jumper wires (28 AWG).
Wiring Steps
- Seat the T-Cobbler on the breadboard, straddling the center trench.
- Connect the Pi's 40-pin ribbon cable to the Cobbler. Ensure the red stripe (Pin 1) aligns with the top-left corner of the Pi's GPIO header.
- Wire the BME280 VIN pin to the Cobbler's 3.3V rail (Physical Pin 1).
- Wire the BME280 GND pin to the Cobbler's GND rail (Physical Pin 6).
- Wire the BME280 SCK (SCL) pin to the Cobbler's SCL (BCM 3, Physical Pin 5). Use a yellow wire.
- Wire the BME280 SDI (SDA) pin to the Cobbler's SDA (BCM 2, Physical Pin 3). Use a blue wire.
- Leave the CS and SDO pins unconnected for default I2C operation (Address 0x77). If your specific breakout defaults to 0x76, tie SDO to GND.
Python I2C Implementation with Error Handling
The following Python 3 script targets the Raspberry Pi 4 Model B. It uses the smbus2 library to communicate directly with the BME280 over I2C bus 1. Instead of pulling in a massive abstraction library, this script reads the sensor's Chip ID register (0xD0) to verify that the physical pin out and I2C addressing are correct.
Prerequisite: Install the library via terminal using sudo pip3 install smbus2.
import smbus2
import time
import sys
# --- PIN & BUS DEFINITIONS ---
# I2C Bus 1 maps to Physical Pins 3 (SDA) and 5 (SCL)
I2C_BUS = 1
# BME280 default I2C address.
# Note: Adafruit breakouts often default to 0x77, while generic Amazon/eBay boards default to 0x76.
BME280_ADDR = 0x77
CHIP_ID_REG = 0xD0
EXPECTED_CHIP_ID = 0x60
def verify_i2c_pinout_and_sensor():
try:
# Initialize the SMBus on I2C Bus 1
bus = smbus2.SMBus(I2C_BUS)
# Read the Chip ID register (1 byte)
chip_id = bus.read_byte_data(BME280_ADDR, CHIP_ID_REG)
if chip_id == EXPECTED_CHIP_ID:
print(f"[SUCCESS] I2C Pinout verified. BME280 detected at 0x{BME280_ADDR:02X}.")
print(f"[DATA] Chip ID Register returned: 0x{chip_id:02X}")
else:
print(f"[WARNING] Device found at 0x{BME280_ADDR:02X}, but Chip ID is 0x{chip_id:02X} (Expected 0x60).")
print("You may be talking to the wrong sensor or a different IC on the same bus.")
except FileNotFoundError as e:
print(f"[FATAL] {e}")
print("FIX: I2C interface is disabled. Run 'sudo raspi-config', go to Interface Options -> I2C, and enable it.")
sys.exit(1)
except OSError as e:
print(f"[FATAL] {e}")
print("FIX: Remote I/O error. This means the Pi sent a clock pulse but got no ACK.")
print("1. Check if SDA (Pin 3) and SCL (Pin 5) are physically swapped.")
print("2. Verify the sensor address (try 0x76 instead of 0x77).")
print("3. Ensure the breakout board has 4.7k pull-up resistors to 3.3V.")
sys.exit(1)
if __name__ == "__main__":
print("Initializing I2C bus on physical pins 3 and 5...")
verify_i2c_pinout_and_sensor()
Debugging: First Three Things to Check When I2C Fails
When working with the Raspberry Pi pin out, I2C is the most common source of bench frustration. If your script crashes, do not rewrite your code. Check the hardware and OS configuration in this exact order.
1. The "No such file or directory" Error
Exact Error String: FileNotFoundError: [Errno 2] No such file or directory: '/dev/i2c-1'
Cause: The I2C kernel module is not loaded because the interface is disabled at the OS level.
Fix: Open the terminal and run sudo raspi-config. Navigate to Interface Options > I2C and select Yes to enable it. Reboot the Pi. You can verify it loaded by running ls /dev/i2c*; you should see /dev/i2c-1.
2. The "Remote I/O Error" (The Pinout Killer)
Exact Error String: OSError: [Errno 121] Remote I/O error
Cause: The Pi's I2C controller sent the address byte on the SDA line, toggled the SCL clock, but the sensor never pulled the SDA line low to send an ACKnowledge (ACK) bit. This is almost always a physical pin out or wiring fault.
Fix:
First, check for swapped wires. SDA must go to Physical Pin 3, and SCL to Physical Pin 5.
Second, run i2cdetect -y 1 in the terminal. If you see a grid of dashes with no numbers, your wiring is open, or your sensor lacks pull-up resistors. The Pi's internal pull-ups (usually ~50kΩ) are too weak for reliable I2C; your breakout board must have 4.7kΩ resistors pulling SDA and SCL up to 3.3V.
3. The "Address Not Found" Silent Failure
Symptom: i2cdetect -y 1 shows a device at 0x76, but your Python script throws an I/O error because it's hardcoded to 0x77.
Cause: Manufacturer address variance. Bosch designed the BME280 to be selectable between 0x76 and 0x77 via the SDO pin. Adafruit routes this high (0x77), while cheaper clone boards route it low (0x76).
Fix: Change the BME280_ADDR variable in your Python script to match the hex value shown in the i2cdetect grid.
Scaling the Build: Simplify or Extend
Once you have mastered the physical pin out and verified I2C communication, you will eventually need to scale your project. Here is how to adapt your hardware architecture based on your production goals.
Simplify: Use a HAT for Production
If you are moving from a breadboard prototype to a permanent installation, jumper wires are a liability. Vibration and thermal cycling will cause female-to-male Dupont connectors to loosen, resulting in intermittent I2C bus drops. Simplify the build by switching to a HAT (Hardware Attached on Top), such as the Pimoroni Enviro+. HATs plug directly into the 40-pin header, mechanically securing the connection, and they include an EEPROM on pins 27 and 28 that automatically configures the Pi's device tree on boot.
Extend: Add SPI for High-Speed ADC
I2C is excellent for low-bandwidth environmental sensors, but it bottlenecks at around 400kHz (Fast Mode) or 1MHz (Fast Mode Plus). If you need to sample audio or read multiple analog voltages at high speed, extend your build by adding an SPI device like the MCP3008 10-bit ADC.
Map the MCP3008 to the dedicated SPI0 pins outlined in the table above (MOSI on 19, MISO on 21, SCLK on 23, CE0 on 24). SPI operates on a push-pull architecture rather than I2C's open-drain, allowing clock speeds up to 30MHz on the Pi's short breadboard traces. Just remember to enable the SPI interface in raspi-config and install the spidev Python library.
For deeper technical specifications on the BCM2711 peripheral multiplexing and alternate pin functions, consult the official Raspberry Pi Hardware Documentation.






