The most reliable method for setting up Raspberry Pi hardware for embedded sensor work is a headless configuration using the Raspberry Pi Imager, pre-enabling SSH and the I2C interface before the first boot. This bypasses the desktop environment overhead, saves RAM, and eliminates the need for a dedicated monitor during jobsite or bench deployment. Below is the exact procedure, wiring schematic, and Python implementation for deploying an I2C temperature sensor on the latest hardware revision.
Spec Sheet & Parts List
When setting up Raspberry Pi 5 boards, you must account for the new RP1 southbridge chip, which changes how GPIO and I2C are handled at the kernel level compared to the Pi 4. Ensure your components match these exact variants to avoid compatibility headaches.
| Component | Exact Variant / Model | Estimated Cost | Notes |
|---|---|---|---|
| Microcontroller | Raspberry Pi 5 (8GB RAM) | $80.00 | Requires active cooling for sustained loads |
| Power Supply | Official 27W USB-C PD Power Supply | $12.00 | Required to negotiate 5V/5A; standard 5V/3A triggers USB current limits |
| I2C Sensor | Adafruit MCP9808 Breakout (PID: 1782) | $14.95 | High accuracy (±0.25°C), 5V tolerant logic, onboard pull-ups |
| Wiring | 26 AWG Silicone Jumper Wires | $8.00 | Silicone insulation prevents melting near headers |
| Status LED | Standard 5mm Red LED + 330Ω Resistor | $0.50 | For visual heartbeat confirmation |
Headless Configuration & Physical Wiring
Do not boot the board with default settings. Use the Raspberry Pi Imager on your host PC to flash Raspberry Pi OS Lite (64-bit, Debian 12 "Bookworm"). Click the gear icon (OS Customization) and apply these exact settings:
- Hostname: Set to
sensor-node-01.local - Enable SSH: Select "Use password authentication" (generate a strong password)
- Set Username/Password: Create a non-default user (e.g.,
maker) - Configure Wireless LAN: Enter your SSID and WPA2/WPA3 passphrase
- Set Locale: Match your timezone and keyboard layout
/boot/firmware/ instead of the legacy /boot/. If you need to manually edit config.txt later to force I2C baud rates, use the new path.
Pin Mapping Table
The Raspberry Pi 5 retains the standard 40-pin header layout for basic I2C, but the RP1 chip handles the actual routing. Wire the MCP9808 and status LED exactly as follows:
| RPi 5 Pin (Physical) | GPIO / Function | Destination |
|---|---|---|
| Pin 1 | 3.3V Power | MCP9808 VDD |
| Pin 3 | GPIO 2 (SDA1) | MCP9808 SDA |
| Pin 5 | GPIO 3 (SCL1) | MCP9808 SCL |
| Pin 6 | Ground (GND) | MCP9808 GND |
| Pin 11 | GPIO 17 | 330Ω Resistor -> LED Anode |
| Pin 9 | Ground (GND) | LED Cathode |
Complete Python Implementation
Target Board Variant: Raspberry Pi 5 (8GB) running Raspberry Pi OS Lite (64-bit, Debian 12 Bookworm).
Environment Note: Bookworm enforces PEP 668, meaning you cannot use pip install globally without breaking system packages. You must use a Python virtual environment.
SSH into your Pi and run these setup commands:
sudo apt update && sudo apt install -y i2c-tools python3-venv python3-gpiozero
sudo raspi-config nonint do_i2c 0
python3 -m venv ~/sensor_env
source ~/sensor_env/bin/activate
pip install smbus2
Save the following code as read_temp.py. This script includes explicit pin definitions, I2C register reading, and robust error handling for jobsite deployments.
import smbus2
import time
import sys
from gpiozero import LED
# --- PIN & ADDRESS DEFINITIONS ---
I2C_BUS_ID = 1 # /dev/i2c-1 is the default user-accessible bus
MCP9808_ADDR = 0x18 # Default I2C address for Adafruit MCP9808
TEMP_REGISTER = 0x05 # Ambient Temperature Register
STATUS_LED_PIN = 17 # Physical Pin 11 (GPIO 17)
READ_INTERVAL = 2.0 # Seconds between reads
# Initialize GPIO and I2C bus
status_led = LED(STATUS_LED_PIN)
bus = smbus2.SMBus(I2C_BUS_ID)
def read_temperature_c():
"""Reads 16-bit temperature data from MCP9808 and converts to Celsius."""
raw_data = bus.read_word_data(MCP9808_ADDR, TEMP_REGISTER)
# MCP9808 returns data with LSB first, we must swap bytes
raw_swapped = ((raw_data & 0xFF) << 8) | (raw_data >> 8)
# Clear flag bits (bits 13-15)
raw_swapped &= 0x1FFF
# Calculate temperature
temp_c = raw_swapped / 16.0
if raw_swapped & 0x1000:
temp_c -= 256.0
return temp_c
def main():
print(f"Starting sensor node on I2C bus {I2C_BUS_ID}, address {hex(MCP9808_ADDR)}")
try:
while True:
status_led.on()
temp_c = read_temperature_c()
temp_f = (temp_c * 9/5) + 32
print(f"[{time.strftime('%Y-%m-%d %H:%M:%S')}] Temp: {temp_c:.2f}°C | {temp_f:.2f}°F")
status_led.off()
time.sleep(READ_INTERVAL)
except OSError as e:
print(f"CRITICAL I2C ERROR: {e}. Check wiring and pull-ups.", file=sys.stderr)
status_led.blink(on_time=0.1, off_time=0.1) # Fast blink indicates I2C fault
sys.exit(1)
except KeyboardInterrupt:
print("\nShutdown requested by user. Exiting safely.")
status_led.off()
bus.close()
sys.exit(0)
if __name__ == "__main__":
main()
Debugging: First Three Things to Check When It Fails
When setting up Raspberry Pi hardware for I2C, silent failures are rare; the kernel will usually throw a specific exception. If the script crashes immediately, perform these three checks in order:
- Verify I2C is actually enabled in the firmware: Run
cat /boot/firmware/config.txt | grep i2c. You must seedtparam=i2c_arm=on. If it is commented out with a#, edit the file and reboot. - Check physical continuity and swap state: Use a multimeter in continuity mode to verify Pin 3 connects to SDA and Pin 5 connects to SCL. Swapping SDA and SCL is the most common breadboard mistake.
- Measure pull-up voltage: Set your multimeter to DC Voltage. Probe Pin 3 (SDA) and Pin 5 (SCL) relative to GND. You must read ~3.3V. If you read 0V, your breakout board lacks pull-up resistors, and the I2C bus will float, causing timeouts.
Common Error Strings and Ranked Causes
Error 1: OSError: [Errno 121] Remote I/O error
- Cause A (80% likely): The I2C address is wrong. Run
i2cdetect -y 1. If the grid is empty, the sensor is unpowered or wired incorrectly. - Cause B (15% likely): I2C bus capacitance is too high (wires too long). Keep I2C traces under 30cm.
- Cause C (5% likely): The sensor is in a low-power sleep state and missed the start condition.
Error 2: ModuleNotFoundError: No module named 'smbus2' or 'gpiozero'
- Cause A (95% likely): You forgot to activate the virtual environment. Run
source ~/sensor_env/bin/activatebefore executing the script. - Cause B (5% likely): You installed the packages globally using
sudo pip3which is blocked by PEP 668 on Bookworm.
Extending or Simplifying the Build
To simplify: If you are tired of stripping wires and debugging breadboard continuity issues, switch to the Adafruit STEMMA QT / Qwiic ecosystem. The Raspberry Pi 5 does not have a native Qwiic port, but you can buy a "SparkFun Qwiic SHIM for Raspberry Pi" (approx. $12). This plugs directly onto the GPIO header and provides a keyed I2C connector, eliminating reversed-wire faults entirely.
To extend: For a production IoT node, add MQTT telemetry. Install paho-mqtt in your virtual environment and publish the temp_c variable to a Mosquitto broker. For long-term data retention, pair the Pi with a local InfluxDB instance running on a separate NAS, using the Pi strictly as an edge-collection node. For detailed network configuration protocols, refer to the official Raspberry Pi configuration documentation.
Frequently Asked Questions
How do I enable I2C when setting up Raspberry Pi headless?
If you forgot to enable I2C in the Raspberry Pi Imager OS customization menu, you do not need to reflash the SD card. SSH into the Pi and run sudo raspi-config, navigate to Interface Options > I2C, and enable it. Alternatively, for a purely script-based headless fix, append dtparam=i2c_arm=on to the bottom of /boot/firmware/config.txt and reboot. See the Bookworm release notes for more on the new file paths.
Why does RPi.GPIO fail when setting up Raspberry Pi 5 on Bookworm?
The legacy RPi.GPIO library relies on direct memory access to the BCM283x SoC registers. The Raspberry Pi 5 uses the new RP1 southbridge chip for GPIO, which breaks this direct memory mapping. Furthermore, Debian 12 (Bookworm) restricts these low-level accesses. You must use gpiozero (which uses the lgpio backend under the hood on Pi 5) or the rpi-gpio-2 fork if you absolutely need legacy syntax.
What is the best way to find the I2C address when setting up Raspberry Pi sensors?
Install the I2C tools via sudo apt install i2c-tools. Then, run the command i2cdetect -y 1. This will output a grid of hexadecimal addresses. If your sensor is wired correctly and powered, its address will appear in the grid (e.g., 18 for the MCP9808). If you see UU, the kernel has already claimed the device via an overlay, which is normal for RTC modules but rare for raw sensors.






