The Raspberry Pi 4 Model B utilizes a standardized 40-pin GPIO header that provides access to power, ground, and the BCM2711 SoC's multiplexed communication interfaces. If you are looking for the definitive Raspberry Pi 4 pin diagram, the short answer is that Pin 1 is 3.3V power, Pin 2 is 5V power, Pin 6 is Ground, and the primary I2C1 bus lives on Physical Pins 3 (SDA) and 5 (SCL). However, treating the Pi's GPIO like a microcontroller's bare pins is a fast track to frying your board. The Pi 4 operates strictly at 3.3V logic, and its pins are not 5V tolerant.
This guide breaks down the physical and BCM (Broadcom) pin mappings, outlines the electrical limits of the header, and walks through a practical I2C sensor build with production-grade Python error handling.
The Raspberry Pi 4 Pin Diagram: Critical Interfaces
Unlike Arduino boards where pins are labeled by their microcontroller port numbers, the Raspberry Pi uses a dual-naming convention. Physical pins are numbered 1 through 40 sequentially, while the software (via libraries like RPi.GPIO or gpiozero) typically references the underlying Broadcom SOC channel (BCM) numbers. Below is the data-dense mapping for the most frequently used power and communication pins on the Pi 4 Model B (Rev 1.4/1.5).
| Physical Pin | BCM GPIO | Pin Name / Function | Electrical Notes & Constraints |
|---|---|---|---|
| 1 | N/A | 3V3 Power | Max total draw 50mA across all 3.3V pins. Use for sensor logic. |
| 2 | N/A | 5V Power | Direct from USB-C input. Capable of sourcing high current for peripherals. |
| 3 | 2 (SDA1) | I2C1 SDA | Includes onboard 1.8kΩ pull-up to 3.3V. Do not use for standard GPIO. |
| 5 | 3 (SCL1) | I2C1 SCL | Includes onboard 1.8kΩ pull-up to 3.3V. Do not use for standard GPIO. |
| 6 | N/A | Ground (GND) | Common ground. Must be shared with all external sensor modules. |
| 8 | 14 (TXD) | UART0 TX | Defaults to serial console. Must disable in raspi-config for raw UART. |
| 10 | 15 (RXD) | UART0 RX | 3.3V logic. Use a logic level shifter if connecting to RS-232. |
| 19 | 10 (MOSI) | SPI0 MOSI | Master Out Slave In. Hardware SPI0 shared with CE0/CE1. |
| 21 | 9 (MISO) | SPI0 MISO | Master In Slave Out. 3.3V logic level. |
| 38 | 20 (MOSI) | I2S DIN / SPI1 | Alternate function. Often used for digital audio (I2S) microphones. |
Project Build: BME280 Environmental Sensor with Status LED
To put this pin diagram into practice, we will wire a Bosch BME280 temperature, humidity, and pressure sensor via I2C, alongside a status LED to indicate successful bus communication. This build targets the Raspberry Pi 4 Model B (4GB or 8GB variant) running Raspberry Pi OS (Bookworm or later).
Parts List
- Board: Raspberry Pi 4 Model B (Rev 1.4 or 1.5)
- Sensor: Adafruit BME280 I2C/SPI Breakout (Product ID: 2652) or generic GY-BME280 module
- Indicator: Standard 5mm Red LED
- Resistor: 330Ω (1/4W) current-limiting resistor for the LED
- Wiring: Female-to-Female Dupont jumper wires (22 AWG)
- Prototyping: Half-size solderless breadboard
Wiring Steps
Before touching any wires, ensure the Pi is powered down and the USB-C cable is disconnected.
- Sensor Power: Connect the BME280
VIN(orVCC) pin to Physical Pin 1 (3.3V) on the Pi. Warning: If your specific breakout board lacks an onboard voltage regulator, feeding it 5V from Pin 2 will instantly destroy the sensor. - Sensor Ground: Connect the BME280
GNDpin to Physical Pin 6 (Ground) on the Pi. - I2C Data (SDA): Connect the BME280
SDI(orSDA) pin to Physical Pin 3 (GPIO 2 / SDA1) on the Pi. - I2C Clock (SCL): Connect the BME280
SCK(orSCL) pin to Physical Pin 5 (GPIO 3 / SCL1) on the Pi. - LED Anode: Connect the long leg (anode) of the 5mm LED to one end of the 330Ω resistor. Connect the other end of the resistor to Physical Pin 11 (GPIO 17) on the Pi.
- LED Cathode: Connect the short leg (cathode) of the LED to Physical Pin 9 (Ground) on the Pi.
Once wired, boot the Pi, open a terminal, and enable the I2C interface via sudo raspi-config (Interface Options > I2C > Enable). Verify the wiring by running i2cdetect -y 1. You should see 76 or 77 in the output grid, confirming the BME280 is acknowledged on the bus.
Complete Python Code with I2C Error Handling
High-level libraries often mask hardware failures, making debugging a nightmare. The script below uses smbus2 for raw I2C communication and gpiozero for the LED. It reads the BME280's Chip ID register (0xD0) to verify hardware-level connectivity before attempting complex temperature compensation math.
Prerequisites: Install dependencies via sudo apt install python3-smbus python3-gpiozero i2c-tools.
import smbus2
import gpiozero
import time
import sys
# ==========================================
# PIN & HARDWARE DEFINITIONS (BCM Numbering)
# ==========================================
LED_STATUS_PIN = 17 # BCM 17 (Physical Pin 11)
I2C_BUS_ID = 1 # /dev/i2c-1 (Physical Pins 3 & 5)
BME280_I2C_ADDR = 0x76 # Default address (0x77 if SDO pin is pulled high)
BME280_CHIP_ID_REG = 0xD0 # Register that holds the hardcoded chip ID
EXPECTED_CHIP_ID = 0x60 # Bosch BME280 hardcoded response
# Initialize GPIO LED (gpiozero handles BCM pin setup automatically)
status_led = gpiozero.LED(LED_STATUS_PIN)
def verify_i2c_connection(bus, address):
"""Reads the Chip ID register to verify physical I2C connectivity."""
try:
chip_id = bus.read_byte_data(address, BME280_CHIP_ID_REG)
if chip_id == EXPECTED_CHIP_ID:
print(f"[SUCCESS] BME280 acknowledged. Chip ID: 0x{chip_id:02X}")
return True
else:
print(f"[WARNING] Device at 0x{address:02X} returned unexpected ID: 0x{chip_id:02X}")
return False
except OSError as e:
# Catching the exact hardware-level I2C failure
raise e
def main():
print("Initializing I2C Bus 1...")
bus = smbus2.SMBus(I2C_BUS_ID)
try:
# Flash LED to indicate startup
status_led.blink(on_time=0.2, off_time=0.2, n=3, background=False)
if verify_i2c_connection(bus, BME280_I2C_ADDR):
status_led.on() # Solid ON = Sensor connected and verified
print("Hardware link established. Ready for data parsing.")
# Note: Full temp/humidity parsing requires reading calibration
# registers (0x88-0xA1) and applying Bosch's compensation formulas.
# For production, use the 'adafruit-circuitpython-bme280' library.
while True:
time.sleep(1)
else:
status_led.off()
print("Device responded, but ID mismatch. Check sensor model.")
except OSError as e:
# This is the exact error thrown when SDA/SCL are disconnected or address is wrong
if e.errno == 121:
print(f"CRITICAL I2C FAULT: {e}")
print("Remote I/O error. The Pi sent a clock signal but received no ACK.")
status_led.blink(on_time=0.5, off_time=0.5) # Fast blink = Error state
else:
print(f"Unexpected OS Error: {e}")
status_led.off()
sys.exit(1)
except KeyboardInterrupt:
print("\nHalting script.")
finally:
status_led.off()
bus.close()
if __name__ == "__main__":
main()
Debugging: When I2C Throws Remote I/O Errors
When working with the Raspberry Pi 4 pin diagram and I2C sensors, the most notorious roadblock is the OSError: [Errno 121] Remote I/O error. This error occurs at the kernel level when the BCM2711's I2C controller asserts the SDA line, clocks the SCL line, but fails to receive an Acknowledge (ACK) bit from the target device on the 9th clock cycle.
If your script crashes with this exact string, do not rewrite your code. The issue is almost always physical or configuration-based. Here are the first three things to check when it fails:
1. Verify Hardware Acknowledgment via Terminal
Stop your Python script and run i2cdetect -y 1 in the terminal. If the grid is entirely empty (only dashes --), the Pi cannot see the sensor. If you see UU, the device is currently reserved by a kernel driver (common with RTC modules or certain HATs). If you see 76 or 77 in the terminal but your Python script still throws Errno 121, you likely have a software bus-lock issue; reboot the Pi to clear the I2C bus state.
2. Check SDA/SCL Physical Crossover
Consult the Raspberry Pi 4 pin diagram table above. Physical Pin 3 is SDA, and Physical Pin 5 is SCL. Module manufacturers frequently label pins from the perspective of the sensor, not the host. If your module labels a pin SDO (Serial Data Out), that must connect to the Pi's SDA (which acts as Master In during reads). Swapping SDA and SCL is the #1 cause of Errno 121 on the bench. Swap the wires on the breadboard and test again.
3. Inspect Pull-Up Resistors and Voltage Levels
The I2C protocol requires pull-up resistors on both SDA and SCL lines. The Pi 4 has internal 1.8kΩ pull-ups enabled on Pins 3 and 5. However, if you have multiple devices on the bus, the combined capacitance might drag the rise-time down, causing the Pi to miss the ACK bit. Furthermore, if your sensor module has its own 4.7kΩ pull-ups tied to 5V, you are backfeeding 5V into the Pi's 3.3V GPIO pins. This can trigger the Pi's internal protection diodes, resulting in bus lockups. Always verify your breakout board's pull-up voltage with a multimeter.
Scaling the Build: Simplify or Extend
The beauty of understanding the underlying Raspberry Pi 4 pin diagram is that you can easily pivot the hardware architecture based on your project constraints.
How to Simplify the Build
If you are strictly teaching GPIO logic or testing a new batch of ribbon cables, drop the BME280 entirely. Remove the I2C dependencies from the code, delete the smbus2 import, and rewire the LED to Physical Pin 12 (GPIO 18). Pin 18 is unique on the Pi 4 because it is the only GPIO pin that supports hardware PWM (Pulse Width Modulation) via gpiozero.PWMLED. This allows you to write a simple script that fades the LED in and out without tying up the CPU with software timing loops.
How to Extend the Build
To turn this bench test into a production IoT node, extend the system in two directions:
- Add an OLED Display: Wire a 128x64 SSD1306 I2C OLED to the same SDA/SCL bus (Pins 3 and 5). Because I2C is a multi-drop bus, the OLED (typically address 0x3C) and the BME280 (address 0x76) will coexist perfectly without conflicting.
- Implement MQTT Telemetry: Install the
paho-mqttPython library. Wrap the BME280 compensation math (using the Adafruit CircuitPython BME280 library) in a cron job or systemd service that publishes the parsed JSON payload to a local Mosquitto broker every 60 seconds. This transitions the Pi from a standalone reader to a networked edge sensor.
By respecting the electrical limits of the BCM2711 and leveraging the correct physical pins for their dedicated hardware functions, you eliminate the vast majority of embedded debugging headaches before you even write your first line of Python.






