To install Raspberry Pi 5 for embedded hardware projects, flash Raspberry Pi OS Lite (64-bit) via the official Imager with SSH and WiFi pre-configured, enable the I2C interface via raspi-config, and wire your sensors to the 40-pin header. Unlike older models, the Pi 5 requires a 27W USB-C PD power supply to deliver full 1.6A USB current, and its RP1 southbridge chip handles GPIO routing differently, making proper headless setup and bare-metal I2C verification critical before deploying production code.

Parts List and Hardware Specifications

Before you install Raspberry Pi 5 into an enclosure or breadboard, verify your bill of materials. Using a 5V-tolerant sensor on the Pi 5's 3.3V logic GPIOs without a level shifter will destroy the RP1 southbridge. The BME280 breakout used here is strictly 3.3V.

  • Board: Raspberry Pi 5 (8GB RAM variant) - Target board for all code in this guide.
  • Power Supply: Official 27W USB-C PD Power Supply (5V/5A). Standard 5V/3A phone chargers will throttle USB ports to 600mA.
  • Thermal: Raspberry Pi Active Cooler (PWM controlled via header).
  • Sensor: BME280 Breakout Board (3.3V I2C variant, Adafruit 2652 or generic).
  • Wiring: 4x Female-to-Female Dupont jumper wires (24 AWG stranded).
  • Storage: 32GB MicroSD Card (A2 rating minimum for OS Lite).

The shift from the BCM2837 (Pi 4) to the BCM2712 and RP1 architecture (Pi 5) changed several embedded baselines. Review the spec comparison below before designing your PCB or wiring harness.

Raspberry Pi 5 vs Pi 4 Embedded Hardware Specs
Specification Raspberry Pi 4 Model B Raspberry Pi 5
GPIO Controller Integrated in BCM2711 SoC RP1 Southbridge Chip (PCIe connected)
Default I2C Baudrate 100 kHz 100 kHz (Configurable via dtparam)
Max GPIO Current (Total) 50 mA across all pins 50 mA (Strictly enforced by RP1)
USB Power Limit (Standard PSU) 1.2A (Total across 4 ports) 600mA (with 15W PSU) / 1.6A (with 27W PD PSU)
Logic Level Voltage 3.3V 3.3V

Step-by-Step Headless Installation

Running a desktop environment wastes RAM and CPU cycles on embedded nodes. We use Raspberry Pi OS Lite (Bookworm) and configure it headlessly.

  1. Flash the OS: Download the Raspberry Pi Imager. Select Raspberry Pi 5 as the device, Raspberry Pi OS Lite (64-bit) as the OS, and your MicroSD card as storage.
  2. Pre-configure OS Customization: Click the gear icon (or "Edit Settings"). Set hostname to sensor-node-01, enable SSH (use password authentication for initial setup), and enter your 2.4GHz WiFi SSID and password. Note: The Pi 5 WiFi chip struggles with some 5GHz WPA3 enterprise networks; stick to 2.4GHz WPA2 for initial provisioning.
  3. Boot and Connect: Insert the SD card, connect the 27W PSU, and wait 60 seconds. Find the Pi's IP address via your router's DHCP table or by pinging sensor-node-01.local.
  4. SSH and Enable I2C: Connect via SSH (ssh username@sensor-node-01.local). Run sudo raspi-config, navigate to Interface Options > I2C, and select Yes to enable the ARM I2C interface.
  5. Install Python Dependencies: Bookworm uses PEP 668, which blocks global pip install to prevent breaking system packages. Create a virtual environment:
    python3 -m venv ~/sensor_env
    source ~/sensor_env/bin/activate
    pip install smbus2

Pin Mapping and Breadboard Wiring

The Pi 5 maintains the standard 40-pin header layout, but the underlying routing goes through the RP1 chip. I2C Bus 1 is the default user-accessible bus. Ensure your BME280 breakout has its I2C pull-up resistors enabled (most Adafruit/SparkFun boards do by default via 10kΩ SMD resistors).

Raspberry Pi 5 to BME280 I2C Pin Mapping
Pi 5 Pin (Physical) BCM / Function Wire Color BME280 Breakout Pin
Pin 1 3.3V Power Red VIN / VCC
Pin 6 Ground Black GND
Pin 3 GPIO 2 (SDA1) Yellow SDA
Pin 5 GPIO 3 (SCL1) Orange SCL
Warning: Never wire the BME280 VIN pin to Pi 5 Pin 2 or 4 (5V). The Bosch BME280 datasheet specifies an absolute maximum supply voltage of 3.6V. Feeding it 5V will instantly vent the magic smoke and permanently short the sensor's internal voltage regulator.

Bare-Metal Python Diagnostic Code

Before importing massive libraries like Adafruit CircuitPython, verify the I2C bus is physically communicating. This script reads the BME280's Chip ID register (0xD0) and raw temperature ADC data. If this runs successfully, your wiring and OS installation are flawless.

Target Board: Raspberry Pi 5 (8GB) | OS: Bookworm Lite | Python 3.11+

import smbus2
import time
import sys

# I2C Configuration
I2C_BUS = 1
BME280_ADDR = 0x76  # Use 0x77 if SDO pin is tied high
REG_CHIP_ID = 0xD0
REG_TEMP_DATA = 0xFA

def main():
    try:
        # Initialize SMBus
        bus = smbus2.SMBus(I2C_BUS)
        
        # 1. Verify Chip ID (BME280 should return 0x60)
        chip_id = bus.read_byte_data(BME280_ADDR, REG_CHIP_ID)
        if chip_id != 0x60:
            print(f'ERROR: Invalid Chip ID: {hex(chip_id)}. Expected 0x60. Check I2C address.')
            sys.exit(1)
        print(f'SUCCESS: BME280 detected at {hex(BME280_ADDR)}. Chip ID: {hex(chip_id)}')
        
        # 2. Trigger a single forced measurement (Register 0xF4, write 0x24)
        bus.write_byte_data(BME280_ADDR, 0xF4, 0x24)
        time.sleep(0.1)  # Wait for measurement to complete
        
        # 3. Read 3 bytes of raw temperature data (0xFA, 0xFB, 0xFC)
        data = bus.read_i2c_block_data(BME280_ADDR, REG_TEMP_DATA, 3)
        adc_T = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4)
        
        print(f'Raw ADC Temperature Value: {adc_T}')
        print('I2C communication verified. Ready for full compensation library.')
        
    except FileNotFoundError as e:
        print(f'CRITICAL OS ERROR: {e}')
        print('FIX: I2C interface is not enabled. Run sudo raspi-config and enable I2C.')
        sys.exit(2)
        
    except OSError as e:
        if e.errno == 121:
            print(f'CRITICAL HARDWARE ERROR: {e}')
            print('FIX: Remote I/O error. No ACK received. Check SDA/SCL wiring and pull-ups.')
        elif e.errno == 16:
            print(f'CRITICAL BUS ERROR: {e}')
            print('FIX: Device or resource busy. Another process is holding the I2C bus.')
        else:
            print(f'Unexpected OSError: {e}')
        sys.exit(3)
        
    except Exception as e:
        print(f'Unexpected Python Error: {e}')
        sys.exit(4)
        
    finally:
        if 'bus' in locals():
            bus.close()

if __name__ == '__main__':
    main()

Debugging I2C Failures and Boot Errors

When the script above fails, do not immediately rewrite the code. Hardware and OS configuration are the culprits 95% of the time. If your terminal throws an error, here are the first three things to check:

  1. Verify I2C Kernel Modules: Run lsmod | grep i2c. If it returns empty, the kernel module isn't loaded. Run sudo raspi-config again, enable I2C, and reboot. The official Raspberry Pi configuration docs detail Bookworm's shift to using config.txt overlays for bus routing.
  2. Scan the Bus: Run sudo i2cdetect -y 1. If you see a grid of all dashes (--), the Pi is sending clocks but getting no response. If you see UU, the kernel has already claimed the device (rare on Lite OS). If you see 76 or 77, your wiring is perfect and the issue is in your Python address variable.
  3. Check Physical Pull-ups: The Pi 5 RP1 chip has internal pull-ups, but they are weak (~50kΩ). For I2C runs longer than 6 inches, you need external 4.7kΩ pull-up resistors to 3.3V on both SDA and SCL lines.

Exact Error Strings and Ranked Causes

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

  • Cause A (Most Likely): Loose Dupont wire on SDA/SCL. The breadboard contact is oxidized. Swap wires.
  • Cause B: Wrong I2C address. The BME280 SDO pin is floating or tied high, making the address 0x77 instead of 0x76.
  • Cause C: Missing pull-up resistors on a generic, bare-bones BME280 module.

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

  • Cause A (Most Likely): I2C is disabled in raspi-config. The /dev/i2c-1 device node doesn't exist because the kernel overlay isn't loaded.
  • Cause B: You are running the script outside your virtual environment, or you lack user permissions (add your user to the i2c group via sudo usermod -aG i2c $USER and log out/in).

How to Extend or Simplify the Build

Once the bare-metal diagnostic script confirms a clean I2C connection, you have two paths forward depending on your project timeline.

Extending the Build (Production Path)

To turn this into a production environmental logger, replace the raw smbus2 math with the adafruit-circuitpython-bme280 library. This handles the complex calibration register math (trimming parameters) stored in the sensor's non-volatile memory. Extend the hardware by adding a 0.96" I2C OLED display (SSD1306) on the same bus. Because the BME280 and SSD1306 use different addresses (0x76 and 0x3C), they will peacefully share SDA/SCL without a multiplexer. Add a cron job to log the compensated temperature, humidity, and barometric pressure to a local SQLite database every 60 seconds.

Simplifying the Build (Prototyping Path)

If you are just prototyping and want to eliminate breadboard wiring errors entirely, ditch the Dupont wires and install a Pimoroni Enviro pHAT or the official Raspberry Pi Sense HAT. These boards plug directly into the 40-pin header, route I2C internally, and include Python libraries pre-packaged in the Raspberry Pi OS repository. You sacrifice the raw 3.3V pin access, but you gain a plug-and-play hardware stack that boots and reads data in under three lines of Python.