The Raspberry Pi 3 Model B features a 40-pin GPIO header that serves as the physical bridge between your Python code and the real world. While the official Raspberry Pi documentation provides the baseline schematic, knowing which physical pin maps to which Broadcom (BCM) GPIO channel—and understanding the strict 3.3V logic limits—is the difference between a working prototype and a fried board.
This guide targets the Raspberry Pi 3 Model B V1.2 (Broadcom BCM2837 chip). We will walk through a practical environmental monitoring build, provide a production-ready Python script with error handling, and debug the most notorious I2C failure mode you will encounter on this specific board.
Raspberry Pi 3 Model B Pin Diagram & Spec Sheet
The 40-pin header on the Pi 3 Model B is identical in layout to the Pi 2, Pi 4, and Pi 5. However, the underlying power delivery and specific peripheral multiplexing differ. Below is the spec sheet for the pins utilized in our project build.
| Physical Pin | BCM GPIO | Function / Label | Project Connection | Electrical Notes |
|---|---|---|---|---|
| 1 | N/A | 3V3 Power | BME280 VCC | Max 50mA total draw across all 3.3V pins. |
| 3 | 2 | SDA1 (I2C) | BME280 SDA | Requires 1.8kΩ - 10kΩ pull-up to 3.3V. |
| 5 | 3 | SCL1 (I2C) | BME280 SCL | I2C bus 1 default clock line. |
| 6 | N/A | Ground | Common GND | Tied to system ground plane. |
| 11 | 17 | GPIO 17 | Status LED (via 330Ω) | Standard digital output, 3.3V logic high. |
| 13 | 27 | GPIO 27 | Tactile Switch | Configured with internal pull-up resistor. |
Project Build: I2C Environmental Monitor with GPIO Fallback
We are building a temperature and pressure monitor that reads a BME280 sensor over I2C. If the sensor fails or disconnects, a physical LED on GPIO 17 flashes to indicate a hardware fault, and a button on GPIO 27 allows you to manually trigger a retry sequence.
Parts List
- Board: Raspberry Pi 3 Model B V1.2 (1GB RAM)
- Sensor: Adafruit BME280 I2C Breakout (Product ID: 2652) or generic 3.3V BME280
- Indicators: 5mm Red LED, 330Ω through-hole resistor
- Input: 6x6mm momentary tactile switch
- Wiring: Female-to-female jumper wires, half-size breadboard
Wiring Steps
- Power the Sensor: Connect Physical Pin 1 (3.3V) to the BME280 VIN/VCC. Connect Physical Pin 6 (GND) to the BME280 GND.
- I2C Data Lines: Connect Physical Pin 3 (SDA) to BME280 SDI/SDA. Connect Physical Pin 5 (SCL) to BME280 SCK/SCL.
- LED Circuit: Connect Physical Pin 11 (GPIO 17) to the 330Ω resistor, then to the LED anode (long leg). Connect the LED cathode to Physical Pin 9 (GND).
- Button Input: Connect one leg of the tactile switch to Physical Pin 13 (GPIO 27). Connect the opposite leg to Physical Pin 14 (GND).
Complete Python Code
This script targets the Pi 3 Model B using the RPi.GPIO library for digital I/O and smbus2 alongside pimoroni-bme280 for I2C communication. It includes robust error handling for bus failures.
import RPi.GPIO as GPIO
import smbus2
import bme280
import time
import sys
# --- PIN DEFINITIONS (BCM Numbering) ---
LED_PIN = 17 # Physical Pin 11
BUTTON_PIN = 27 # Physical Pin 13
I2C_BUS = 1 # I2C1 on Pi 3 (Physical Pins 3 & 5)
BME280_ADDR = 0x76 # Default for generic; Adafruit is often 0x77
def setup_gpio():
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(LED_PIN, GPIO.OUT, initial=GPIO.LOW)
# Use internal pull-up so button reads HIGH when open, LOW when pressed
GPIO.setup(BUTTON_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
def blink_error_pattern():
"""Flashes LED to indicate I2C hardware failure."""
for _ in range(3):
GPIO.output(LED_PIN, GPIO.HIGH)
time.sleep(0.15)
GPIO.output(LED_PIN, GPIO.LOW)
time.sleep(0.15)
def main():
setup_gpio()
port = I2C_BUS
address = BME280_ADDR
try:
bus = smbus2.SMBus(port)
calibration_params = bme280.load_calibration_params(bus, address)
print("BME280 initialized successfully.")
except Exception as e:
print(f"Fatal Init Error: {e}")
blink_error_pattern()
sys.exit(1)
try:
while True:
# Check if button is pressed (Active LOW)
if GPIO.input(BUTTON_PIN) == GPIO.LOW:
print("Button pressed! Forcing sensor read...")
GPIO.output(LED_PIN, GPIO.HIGH)
try:
data = bme280.sample(bus, address, calibration_params)
temp_c = data.temperature
pressure_hpa = data.pressure
humidity = data.humidity
print(f"Temp: {temp_c:.2f}C | Pressure: {pressure_hpa:.1f}hPa | Humidity: {humidity:.1f}%")
GPIO.output(LED_PIN, GPIO.LOW) # Solid off means good read
except OSError as e:
# Catching the exact I2C bus error
if "[Errno 121]" in str(e):
print(f"I2C Bus Error: {e} - Check wiring!")
blink_error_pattern()
else:
raise e
time.sleep(2.0)
except KeyboardInterrupt:
print("\nExiting gracefully...")
finally:
GPIO.cleanup()
if 'bus' in locals():
bus.close()
if __name__ == '__main__':
main()
Debugging: Fixing 'OSError: [Errno 121] Remote I/O error'
If you run the script above and immediately see OSError: [Errno 121] Remote I/O error, do not panic. This is the most common failure mode on the Pi 3 Model B when working with I2C sensors. This exact error string means the Broadcom SoC sent a clock pulse on the SCL line, but the sensor failed to pull the SDA line low to acknowledge (NACK).
The First 3 Things to Check
- Verify the I2C Interface is Enabled: The Pi 3 disables I2C by default. Run
sudo raspi-config, navigate to Interface Options > I2C, and enable it. Reboot the Pi. - Confirm SDA and SCL are Not Swapped: Physical Pin 3 is SDA. Physical Pin 5 is SCL. Swapping these will not damage the Pi, but it will guarantee an Errno 121 error because the sensor will not recognize the clock signal.
- Check for Missing Pull-Up Resistors: The I2C specification requires pull-up resistors on both SDA and SCL. The Pi 3 has onboard 1.8kΩ pull-ups for I2C1, but if you are using long wires or a sensor breakout with heavy capacitance, the signal edges degrade. Add external 4.7kΩ pull-ups to the 3.3V rail.
Ranked Causes for Errno 121
| Rank | Cause | Verification Method | Fix |
|---|---|---|---|
| 1 | Wrong I2C Address in Code | Run i2cdetect -y 1 in terminal. |
Update BME280_ADDR to match the hex output (e.g., 0x77). |
| 2 | Sensor Powered by 5V instead of 3.3V | Measure voltage at sensor VCC pin with a multimeter. | Move VCC wire to Physical Pin 1 (3.3V). |
| 3 | Loose Dupont Jumper Connection | Wiggle wires while running i2cdetect. |
Replace cheap jumper wires; use crimped connectors. |
| 4 | Bus Locked by Previous Crash | SDA line reads constant 0V on multimeter. | Reboot the Pi to reset the I2C peripheral state. |
i2cdetect -y 1 shows dashes (--) across the entire grid, your I2C bus is disabled or the SDA line is being held low by a malfunctioning slave device. Disconnect the sensor and run the command again; if the Pi's onboard addresses appear, the sensor is defective.
Extending and Simplifying the Build
Once you have the baseline environmental monitor running, you can scale the project up or down depending on your deployment needs.
How to Simplify (Headless Logging)
If you do not need the physical LED or button, strip the RPi.GPIO dependencies entirely. Rely solely on the smbus2 library and pipe the output to a local CSV file or an MQTT broker. This reduces CPU overhead on the Pi 3's older quad-core Cortex-A53, freeing up resources for other Docker containers.
How to Extend (Multi-Drop I2C Bus)
The Pi 3 Model B I2C1 bus supports up to 127 theoretical addresses, but practically, you can daisy-chain 4 to 6 sensors before capacitance degrades the signal. To add an OLED display (SSD1306) and a light sensor (TSL2591) to the same Physical Pins 3 and 5:
- Ensure no two devices share the same hardcoded I2C address.
- Keep total wire length under 30cm (1 foot) to prevent signal reflection.
- Lower the I2C baud rate in
/boot/config.txtby addingdtparam=i2c_baudrate=50000if you experience intermittent Errno 121 errors with multiple devices attached.
Frequently Asked Questions
What is the difference between BCM and BOARD numbering on the Pi 3 Model B?
BOARD numbering refers to the physical pin position on the 40-pin header (e.g., Pin 11). BCM numbering refers to the internal Broadcom SoC GPIO channel mapped to that pin (e.g., GPIO 17). In Python, RPi.GPIO.setmode(GPIO.BOARD) uses physical pins, while GPIO.setmode(GPIO.BCM) uses the chip's logical channels. The code provided in this guide uses BCM, as it is the standard for modern Raspberry Pi development and aligns with the gpiozero library.
Can I power the Raspberry Pi 3 Model B directly through the 5V GPIO pins?
Yes, you can backpower the Pi 3 Model B by injecting 5.1V into Physical Pin 2 or 4 (5V) and Physical Pin 6 (GND). However, doing so bypasses the onboard polyfuse and the USB power management circuitry. If your external power supply spikes above 5.25V or lacks over-current protection, you risk permanently damaging the board. It is highly recommended to use the micro-USB port or a dedicated UPS HAT for power injection.
Which pins on the Pi 3 Model B are strictly 3.3V tolerant?
All general-purpose GPIO pins (BCM 2 through 27) operate at 3.3V logic levels and are strictly 3.3V tolerant. Applying 5V to these pins will destroy the internal ESD protection diodes and fry the BCM2837 SoC. The only 5V tolerant pins on the header are the dedicated 5V power pins (Physical 2 and 4) and the 5V input on the USB/Ethernet controller, which are not exposed as standard GPIOs.
How do I find the I2C address if my sensor isn't showing up on the pinout?
If your sensor is wired correctly to Physical Pins 3 and 5 but isn't responding, open the terminal and run sudo i2cdetect -y 1. This command scans the I2C bus and outputs a grid. If your sensor is found, its hexadecimal address (e.g., 76 or 77) will appear in the grid. If the grid is entirely empty, verify that the sensor requires 3.3V power and that the I2C interface is enabled in raspi-config.






