The Raspberry Pi 3 Model B pin layout utilizes the standard 40-pin (2x20) GPIO header. While it shares the same physical footprint as newer boards, the Pi 3 Model B has specific hardware quirks—most notably its 1.8kΩ onboard I2C pull-up resistors and a strict 3.3V logic tolerance on all GPIO pins—that dictate how you wire external modules. If you apply 5V to a Pi 3 GPIO pin, you will permanently damage the SoC. This guide moves past generic pinout charts and shows you how to actually use the layout to build, code, and debug a reliable I2C environmental logger.

Bench Note: All pin references in this guide use BCM (Broadcom) numbering, which is the default for modern Python libraries like gpiozero. Physical pin numbers (1-40) are only used when discussing physical wiring and power rails.

The Raspberry Pi 3 Model B Pin Layout: Project Mapping

Rather than memorizing all 40 pins, focus on the specific rails and communication buses your project requires. For our BME280 environmental logger with a status LED and hardware interrupt button, we need 3.3V power, ground, the primary I2C bus, and two standard GPIOs.

Physical Pin BCM GPIO Function Project Wiring Target
1 N/A 3.3V Power BME280 VCC
3 2 (SDA1) I2C Data BME280 SDI/SDA
5 3 (SCL1) I2C Clock BME280 SCK/SCL
6 N/A Ground BME280 GND, Button GND
11 17 GPIO (Output) Status LED (via 330Ω)
13 27 GPIO (Input) Tactile Button

Project Build: I2C BME280 Logger with GPIO Interrupts

This build targets the Raspberry Pi 3 Model B (V1.2, 40-pin header) running Raspberry Pi OS (Bookworm or later). We are using the smbus2 library for raw I2C communication to expose hardware-level errors, and gpiozero for GPIO management, as it seamlessly handles the libgpiod backend required by modern Pi OS releases.

Parts List

  • Board: Raspberry Pi 3 Model B (V1.2)
  • Sensor: Adafruit BME280 I2C/SPI Breakout (Product ID: 2652) or equivalent generic module with onboard 3.3V LDO.
  • Indicator: 5mm Red LED + 330Ω through-hole resistor.
  • Input: 6x6mm momentary tactile switch.
  • Wiring: Female-to-female and male-to-female 24 AWG jumper wires.

Wiring Steps

  1. De-energize the board. Unplug the Pi 3 micro-USB power supply before touching the GPIO header.
  2. Wire the I2C Bus: Connect Physical Pin 1 (3.3V) to the sensor VCC. Connect Physical Pin 6 (GND) to the sensor GND. Connect Physical Pin 3 (SDA) to sensor SDA, and Physical Pin 5 (SCL) to sensor SCL.
  3. Wire the Status LED: Connect BCM 17 (Physical 11) to the 330Ω resistor, then to the LED anode (long leg). Connect the LED cathode to a ground rail.
  4. Wire the Button: Connect one leg of the tactile switch to BCM 27 (Physical 13). Connect the opposite diagonal leg to a ground rail. (The Pi's internal pull-up resistor will be enabled in software, so no external resistor is needed).
  5. Verify connections. Tug gently on the unshrouded Pi 3 header pins to ensure no jumpers are offset by one row—a common cause of dead shorts.

Python Code with Robust Error Handling

Before running this, ensure I2C is enabled via sudo raspi-config (Interface Options > I2C) and install the dependencies: sudo apt install python3-smbus2 python3-gpiozero.

import sys
import time
from smbus2 import SMBus, i2c_msg
from gpiozero import LED, Button
from signal import pause

# --- PIN DEFINITIONS (BCM Numbering) ---
STATUS_LED_PIN = 17
INTERRUPT_BTN_PIN = 27
I2C_BUS_ID = 1
BME280_ADDR = 0x76  # Default for Adafruit; generic modules often use 0x77

# --- HARDWARE INITIALIZATION ---
led = LED(STATUS_LED_PIN)
# pull_up=True uses the Pi's internal 50k pull-up resistor
button = Button(INTERRUPT_BTN_PIN, pull_up=True, bounce_time=0.05) 

def read_bme280_raw(bus, address):
    """Reads raw compensation and data registers from BME280 via I2C."""
    try:
        # Request 8 bytes of data starting from register 0xF7 (pressure, temp, humidity)
        msg = i2c_msg.read(address, 8)
        bus.i2c_rdwr(msg)
        data = list(msg)
        
        # Basic chip ID check to verify we are talking to the right silicon
        id_msg = i2c_msg.write(address, [0xD0])
        bus.i2c_rdwr(id_msg)
        read_msg = i2c_msg.read(address, 1)
        bus.i2c_rdwr(read_msg)
        chip_id = list(read_msg)[0]
        
        if chip_id != 0x60:
            raise ValueError(f'Chip ID 0x{chip_id:02X} does not match expected BME280 ID 0x60')
            
        return data
    except OSError as e:
        raise e

def button_pressed_handler():
    led.on()
    print('[INTERRUPT] Button pressed. Logging sensor data...')
    try:
        with SMBus(I2C_BUS_ID) as bus:
            raw_data = read_bme280_raw(bus, BME280_ADDR)
            print(f'Success: Read {len(raw_data)} bytes from sensor at 0x{BME280_ADDR:02X}')
    except Exception as e:
        print(f'Hardware Fault during read: {type(e).__name__}: {e}')
    finally:
        time.sleep(0.5)
        led.off()

if __name__ == '__main__':
    print(f'System ready. Monitoring BCM {INTERRUPT_BTN_PIN} for button press...')
    button.when_pressed = button_pressed_handler
    
    try:
        pause()  # Keeps the script running efficiently
    except KeyboardInterrupt:
        print('\nShutdown requested. Cleaning up GPIO...')
    finally:
        led.off()
        button.close()
        print('Resources released safely.')

Debugging: First Three Things to Check When It Fails

When working with the Raspberry Pi 3 Model B pin layout, hardware faults usually manifest as OS-level I/O errors. If the script crashes, check these three things in order.

1. The Exact Error: OSError: [Errno 121] Remote I/O error

This is the most common I2C failure on the Pi 3. It means the Linux kernel sent a clock pulse on the SCL line, but the sensor did not acknowledge (ACK) on the SDA line.

  • Cause A (Most Likely): Physical misalignment. The Pi 3 header is unshrouded. If your jumper block shifted up by 1mm, your SDA pin might be sitting on the 5V rail. Fix: Power down, inspect pin seating with a flashlight, and reseat.
  • Cause B: Incorrect I2C address. Generic BME280 modules often default to 0x77 instead of the Adafruit 0x76. Fix: Run i2cdetect -y 1 in the terminal. If you see 77, update the BME280_ADDR variable in the code.
  • Cause C: Missing sensor power. You wired SDA/SCL but forgot the 3.3V VCC line. The sensor has no power to pull the SDA line low for the ACK.

2. The Exact Error: FileNotFoundError: [Errno 2] No such file or directory: '/dev/i2c-1'

The Python script cannot find the I2C hardware interface in the OS.

  • Cause A: The I2C interface is disabled in the device tree. Fix: Run sudo raspi-config, navigate to Interface Options > I2C, and enable it. Reboot.
  • Cause B: You are using an extremely old or custom kernel that maps the primary bus to /dev/i2c-0. Fix: Check ls /dev/i2c*. If only i2c-0 exists, change I2C_BUS_ID = 0 in the code (though this is rare on Pi 3 V1.2).

3. The Exact Error: gpiozero.exc.GPIOPinInUse

The gpiozero library cannot claim BCM 17 or 27 because another process holds it.

  • Cause A: A previous instance of your script crashed without running the finally cleanup block, or a background service (like an MQTT daemon you were testing) is holding the pin. Fix: Run sudo killall python3 or identify the process using sudo lsof | grep gpio.
The Pi 3 Pull-Up Gotcha: The Raspberry Pi 3 Model B has 1.8kΩ pull-up resistors physically soldered to the board on the SDA and SCL lines (pins 3 and 5). If you connect an external sensor module that also has 4.7kΩ pull-ups, the parallel resistance drops to ~1.3kΩ. This creates a strong pull-up that can sometimes overwhelm sensors with weak open-drain drivers, leading to corrupted data bytes. If your I2C reads are erratic, check your sensor module and physically remove its onboard pull-up resistors.

Extending and Simplifying the Build

The Raspberry Pi 3 Model B pin layout supports up to 27 usable GPIOs and multiple devices on the same I2C bus. Here is how to scale this project based on your needs.

How to Extend (Scale Up)

  • Add an OLED Display: Wire an SSD1306 128x64 I2C OLED to the exact same SDA/SCL pins (Physical 3 and 5). Because it uses a different address (0x3C), the Pi's I2C bus will route data to both the BME280 and the display simultaneously without pin conflicts.
  • Add a 5V Relay Module: The Pi 3 GPIOs output 3.3V, which is often insufficient to trigger the optocoupler on standard 5V relay modules. Use Physical Pin 2 (5V) to power the relay VCC, and use an NPN transistor (like a 2N2222) driven by a spare GPIO (e.g., BCM 22) to switch the relay ground.

How to Simplify (Scale Down)

  • Drop the Hardware Interrupt: If you don't need the button, remove the gpiozero button logic and replace the pause() loop with a simple while True: loop containing time.sleep(60) to log data every minute.
  • Use a HAT: If breadboard wiring is causing [Errno 121] errors due to loose jumper wires, abandon the breadboard entirely and use a pre-soldered Raspberry Pi Sense HAT or Enviro HAT, which plugs directly into the 40-pin header and uses the same I2C addresses.

Frequently Asked Questions (FAQ)

Are the Raspberry Pi 3 Model B pin layout headers identical to the Pi 4?

Physically, yes. Both use the 2x20 (40-pin) header with the same power, ground, and primary I2C/SPI/UART pin assignments. However, electrically, there are differences. The Pi 4 has a higher current capacity on the 3.3V rail (up to 300mA vs the Pi 3's ~50mA limit before brownout). Furthermore, the Pi 4 supports true analog video out via the TRRS jack, while the Pi 3 Model B requires an HDMI-to-VGA adapter or composite via the unpopulated J5V header pads. Code written for the Pi 3's GPIOs will run natively on the Pi 4 without pin mapping changes.

Which pins on the Raspberry Pi 3 Model B pin layout tolerate 5V logic?

None of the GPIO pins are 5V tolerant. The Broadcom BCM2837 SoC on the Pi 3 operates strictly at 3.3V. Applying 5V to any GPIO (including SDA/SCL) will forward-bias the internal ESD protection diodes, drawing massive current and permanently destroying the pin or the entire SoC. The only pins on the header that carry 5V are the power rails (Physical Pins 2 and 4). If you must interface a 5V sensor (like an HC-SR04 ultrasonic sensor), you must use a logic level shifter or a simple resistor voltage divider (e.g., 1kΩ and 2kΩ) on the Echo pin.

Why does my I2C sensor read erratic values on physical pins 3 and 5?

Erratic I2C reads on the Pi 3 are almost always caused by bus capacitance or pull-up resistor conflicts. As mentioned in the debugging section, the Pi 3 has aggressive 1.8kΩ onboard pull-ups. If your external module adds more pull-ups, the bus voltage rise time becomes too fast, or the sensor's internal transistor cannot pull the line low enough to register a logical '0'. Additionally, if your jumper wires exceed 30cm (12 inches), the added capacitance of the wire will distort the I2C clock edges. Keep I2C wires under 15cm, and use i2cdetect -y 1 to ensure the address shows up solidly, not intermittently.

For official hardware specifications and device tree configurations, refer to the Raspberry Pi Foundation Documentation and the Adafruit BME280 Breakout Guide.