Decision Tree: Which Pi and OS for Your Build?

Before flashing an SD card, you need to lock in your hardware and OS combination. The Raspberry Pi ecosystem has fragmented with the release of the Pi 5 and the Bookworm OS update. Use this decision matrix to select the exact baseline for your project.

Use Case Board Pick OS Pick Why This Combo Wins
Always-on IoT / Sensor Hub Raspberry Pi 5 (8GB) Pi OS Lite (64-bit, Bookworm) Max RAM for Docker containers; no GUI overhead eating CPU cycles. (Default Pick)
Desktop Replacement / Media Center Raspberry Pi 5 (8GB) Pi OS Desktop (64-bit) Dual 4K@60Hz micro-HDMI; hardware video decoding requires the desktop stack.
Battery-Powered / Remote Weather Station Raspberry Pi Zero 2 W Pi OS Lite (64-bit) Draws ~1.2W idle vs Pi 5's ~2.5W. Essential for solar/18650 pack deployments.
Legacy Hardware Interfacing (Pre-2023) Raspberry Pi 4 Model B (4GB) Pi OS Bullseye (Legacy) Retains legacy RPi.GPIO and wiringpi support without lgpio migration.
The Default Recommendation: If you are starting a new embedded project today, buy the Raspberry Pi 5 (8GB) and install Raspberry Pi OS Lite (64-bit, Bookworm). The 8GB variant costs roughly $80 and prevents out-of-memory crashes when running Python data-logging scripts alongside an MQTT broker.

Parts List and Spec Sheet

This build targets a headless environmental monitoring node. Do not substitute the power supply; the Pi 5 negotiates USB-C Power Delivery (PD) and will throttle downstream USB current to 600mA if it does not detect a 27W PD handshake.

  • Compute: Raspberry Pi 5 (8GB variant) — $80.00
  • Power: Official Raspberry Pi 27W USB-C PD Power Supply (White/Black) — $12.00
  • Storage: 64GB SanDisk Extreme microSD (A2 app performance class) — $15.00
  • Sensor: Adafruit BME280 I2C/SPI Breakout (Product ID: 2652) — $19.95
  • Wiring: 4x Female-to-Female Dupont jumper wires (20cm)
  • Indicator: 5mm Red LED with 330Ω current-limiting resistor

Step-by-Step: Installing Raspberry Pi Headless

Headless means running the board without a monitor, keyboard, or mouse. We configure SSH and WiFi before the first boot using the official imager.

  1. Download Raspberry Pi Imager: Get the latest version from the official Raspberry Pi software page. Install it on your host PC/Mac.
  2. Select Device and OS: Choose 'Raspberry Pi 5' as the device. Select Raspberry Pi OS (other) -> Raspberry Pi OS Lite (64-bit).
  3. Select Storage: Insert your 64GB A2 microSD card and select it in the Imager.
  4. Edit OS Customisation (Crucial Step): Click 'Next', then click 'Edit Settings' on the prompt.
    • Hostname: Set to pi-sensor-node.
    • Username/Password: Create a secure local user (e.g., maker).
    • Wireless LAN: Enter your 2.4GHz SSID and password. (Note: The Pi 5 supports 5GHz, but 2.4GHz penetrates enclosures better for IoT nodes).
    • Services Tab: Check 'Enable SSH' and select 'Use password authentication'.
  5. Flash and Verify: Click 'Save', then 'Yes' to apply OS customisation. Wait for the verify step to complete.
  6. First Boot: Insert the SD card into the Pi 5, connect the 27W USB-C PD supply, and wait 45 seconds. Ping the board from your host machine: ping pi-sensor-node.local. Once it replies, SSH in: ssh maker@pi-sensor-node.local.

GPIO Pin Mapping and Sensor Wiring

The Pi 5 uses the BCM2712 SoC. While the physical header remains 40 pins, the underlying GPIO chip architecture has changed. We are using the default I2C1 bus for the BME280 sensor and BCM 17 for a status LED.

Pi 5 Physical Pin BCM GPIO Function BME280 / LED Connection
Pin 1 3V3 Power (3.3V) BME280 VIN
Pin 3 GPIO 2 I2C1 SDA BME280 SDI
Pin 5 GPIO 3 I2C1 SCL BME280 SCK
Pin 6 GND Ground BME280 GND
Pin 11 GPIO 17 Digital Output 330Ω Resistor -> LED Anode
Pin 9 GND Ground LED Cathode
Bench Tip: The Pi 5 has 1.5kΩ pull-up resistors on the I2C1 lines. If you are running wires longer than 30cm to your sensor, signal integrity will degrade. Add external 4.7kΩ pull-ups to 3.3V on both SDA and SCL lines at the sensor breakout board to guarantee clean edges.

Python Control Code with Error Handling

This code targets the Raspberry Pi 5 (8GB) running Bookworm 64-bit. Bookworm deprecated the legacy RPi.GPIO library. You must use gpiozero (which leverages the lgpio backend on Pi 5) and smbus2 for I2C communication.

First, install the required dependencies via SSH:

sudo apt update
sudo apt install python3-smbus2 python3-gpiozero python3-rpi-lgpio -y

Create a file named sensor_node.py and paste the following complete, compilable script:

import sys
import time
import smbus2
from gpiozero import LED

# --- HARDWARE CONFIGURATION ---
I2C_BUS = 1
BME280_ADDR = 0x77  # Adafruit breakouts default to 0x77; some clones use 0x76
CHIP_ID_REG = 0xD0
EXPECTED_CHIP_ID = 0x60

# BCM 17 corresponds to Physical Pin 11
STATUS_LED = LED(17) 

def verify_i2c_sensor():
    """Checks I2C bus for BME280 Chip ID to confirm wiring."""
    try:
        bus = smbus2.SMBus(I2C_BUS)
        chip_id = bus.read_byte_data(BME280_ADDR, CHIP_ID_REG)
        bus.close()
        
        if chip_id == EXPECTED_CHIP_ID:
            print(f"[OK] BME280 found at {hex(BME280_ADDR)}. Chip ID: {hex(chip_id)}")
            return True
        else:
            print(f"[WARN] Device at {hex(BME280_ADDR)} returned unexpected ID: {hex(chip_id)}")
            return False
            
    except OSError as e:
        print(f"[FAIL] I2C Hardware Fault: {e}")
        return False

def main_loop():
    """Blinks LED to indicate system health while logging."""
    print("Starting main monitoring loop. Press Ctrl+C to exit.")
    try:
        while True:
            # Blink once per cycle to show heartbeat
            STATUS_LED.blink(on_time=0.5, off_time=0.5, n=1, background=False)
            
            # Placeholder for actual BME280 temp/humidity read logic
            print(f"[{time.strftime('%H:%M:%S')}] Sensor polling...")
            time.sleep(5)
            
    except KeyboardInterrupt:
        STATUS_LED.off()
        print("\n[INFO] Halted by user. LED off.")
        sys.exit(0)

if __name__ == '__main__':
    if not verify_i2c_sensor():
        print("Halting execution. Check wiring and I2C address.")
        sys.exit(1)
    main_loop()

Debugging: First Three Things to Check When It Fails

When working with embedded I2C and the new Pi 5 architecture, things will break on the first boot. Here is the exact decision path for the most common errors.

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

This is the universal I2C failure code. The kernel attempted to clock data out, but the sensor did not acknowledge (ACK) the address.

  1. Check I2C Address (Most Likely): Run sudo i2cdetect -y 1. If the grid is empty, your SDA/SCL wires are swapped, or the ground wire is loose. If you see 76 instead of 77, change BME280_ADDR = 0x77 to 0x76 in the Python script.
  2. Verify I2C Interface is Enabled: Bookworm handles overlays differently. Run sudo raspi-config, navigate to Interface Options -> I2C, and ensure it is enabled. Reboot.
  3. Measure Voltage: Use a multimeter to verify exactly 3.3V between Pin 1 and Pin 6. If it reads 0V or 5V, your breakout board is miswired and you may have damaged the sensor's voltage regulator.

Error 2: RuntimeError: Failed to add edge detection or GPIO Import Fails

If you copied legacy code from a Pi 4 tutorial, it likely uses import RPi.GPIO as GPIO. This will crash on a Pi 5 running Bookworm.

  1. Switch to gpiozero: Refactor your code to use gpiozero as shown in the script above. It abstracts the underlying lgpio C-bindings required by the BCM2712 chip.
  2. Check User Permissions: If gpiozero throws a permissions error, ensure your user is in the gpio group: sudo usermod -aG gpio maker, then log out and back in.

Error 3: ModuleNotFoundError: No module named 'smbus2'

  1. Virtual Environment Conflict: Bookworm enforces PEP 668, marking the system Python as externally managed. If you are using a venv, you must install the package inside it: pip install smbus2. If running system-wide, use sudo apt install python3-smbus2 instead of pip.

Extending or Simplifying the Build

Once the baseline I2C read and GPIO heartbeat are stable, you need to decide how to scale the deployment.

To Simplify (Cost/Power Reduction):
If this node is going inside a sealed outdoor enclosure running off a 12V lead-acid battery and a solar charge controller, the Pi 5 is overkill. Migrate the exact Python script above to a Raspberry Pi Zero 2 W. The pinout for I2C1 and BCM 17 is physically identical. You will drop idle power consumption from ~2.5W to ~1.2W, doubling your battery runtime. Just ensure you use the arm64 Lite OS image.

To Extend (Data Pipeline Integration):
For a production IoT deployment, polling a sensor locally is useless without telemetry. Extend the build by installing mosquitto (MQTT broker) and publishing the sensor data to a home automation hub like Home Assistant. Add the paho-mqtt library to your Python script, wrap the main_loop in a JSON payload, and publish to home/sensors/bme280. For enterprise edge deployments, containerize the Python script using Docker and manage it via Portainer, leveraging the Pi 5's 8GB RAM to run the broker, database (InfluxDB), and script simultaneously without swapping to the SD card.