The Raspberry Pi 5 brings significant PCIe and USB 3.0 bandwidth upgrades, but its Broadcom BCM2712 SoC handles low-level GPIO and I2C communication much like its predecessors. When using a Raspberry Pi for environmental monitoring, the I2C bus remains the most reliable protocol for digital sensors, provided you respect its physical layer limitations. This guide walks through building a robust temperature, humidity, and pressure logger using the Pi 5 and a BME280 sensor, complete with production-grade error handling and I2C debugging workflows.
Project Overview & Bill of Materials
The Raspberry Pi 5 requires a strict 5V/5A USB-C PD power supply to maintain stable logic levels on the 3.3V rail, which directly feeds the I2C pull-up resistors. Using an underpowered supply will cause brownouts that manifest as random I2C bus lockups.
| Component | Exact Variant / Model | Estimated Cost (2026) |
|---|---|---|
| Single Board Computer | Raspberry Pi 5 (4GB RAM) | $60.00 |
| Power Supply | Official Raspberry Pi 27W USB-C PD (5V/5A) | $12.00 |
| Environmental Sensor | Adafruit BME280 I2C/SPI Breakout (Product ID: 2652) | $19.95 |
| Wiring | Premium Female/Female Silicone Jumper Wires (20cm) | $4.00 |
| Storage | SanDisk Extreme 32GB microSD (A1 rated) | $9.00 |
Hardware Wiring & Pin Mapping
The Raspberry Pi 5's 40-pin header maintains backward compatibility with the Pi 4's I2C pinout. Hardware I2C bus 1 is exposed on pins 3 and 5. The BME280 operates strictly at 3.3V logic; never connect it to a 5V I2C bus without a level shifter, or you will destroy the sensor's internal ASIC.
| Raspberry Pi 5 Pin | BCM GPIO | Function | BME280 Pin | Wire Color |
|---|---|---|---|---|
| Pin 1 | N/A | 3.3V Power | VIN (or 3Vo) | Red |
| Pin 6 | N/A | Ground | GND | Black |
| Pin 3 | GPIO 2 | I2C SDA | SDA | Blue |
| Pin 5 | GPIO 3 | I2C SCL | SCL | Yellow |
- De-energize the Pi: Unplug the USB-C power supply before touching the GPIO header.
- Connect Power and Ground: Attach the red wire to Pin 1 (3.3V) and black to Pin 6 (GND).
- Connect Data Lines: Attach blue to Pin 3 (SDA) and yellow to Pin 5 (SCL).
- Verify Connections: Use a multimeter in continuity mode to verify GND-to-GND before applying power.
- Boot the Pi: Plug in the 27W power supply and SSH into the Pi or open a terminal.
Python Implementation with Error Handling
Before writing code, ensure the I2C interface is enabled via sudo raspi-config (Interface Options -> I2C -> Enable) and install the required Adafruit Blinka and BME280 libraries:
sudo apt update
sudo apt install python3-pip i2c-tools
pip3 install --break-system-packages adafruit-circuitpython-bme280
The following Python script targets the Raspberry Pi 5 running Bookworm. It includes explicit pin/bus definitions and robust try/except blocks to catch physical layer disconnects and addressing errors without crashing the daemon.
import time
import board
import adafruit_bme280
# Pin/Bus Definitions for Raspberry Pi 5
# Hardware I2C bus 1 is the default on the 40-pin header (Pins 3 & 5)
I2C_BUS = board.I2C() # Uses board.SCL and board.SDA
# Adafruit BME280 default address is 0x77. Generic clones often use 0x76.
SENSOR_ADDRESS = 0x77
def main():
try:
# Initialize I2C bus and Sensor object
i2c = I2C_BUS
bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c, address=SENSOR_ADDRESS)
# Set local sea level pressure for accurate altitude calculation (hPa)
bme280.sea_level_pressure = 1013.25
print('BME280 Sensor initialized successfully on I2C Bus 1.')
while True:
temp_c = bme280.temperature
humidity = bme280.relative_humidity
pressure = bme280.pressure
print(f'Temp: {temp_c:.2f} C | Humidity: {humidity:.2f} % | Pressure: {pressure:.2f} hPa')
time.sleep(2.0)
except ValueError as e:
# Catches missing sensor, wrong address, or unpowered chip
print(f'[FATAL] Sensor not found on I2C bus. Exact error: {e}')
print('Action: Check wiring, run i2cdetect -y 1, and verify SENSOR_ADDRESS.')
except OSError as e:
# Catches bus lockups, NACKs, or physical disconnects during runtime
print(f'[ERROR] I2C Bus communication failed. Exact error: {e}')
print('Action: Check for loose Dupont wires, bus capacitance, or logic level mismatches.')
except KeyboardInterrupt:
print('\nMonitoring stopped by user.')
if __name__ == '__main__':
main()
Debugging I2C Failures: Exact Errors and Fixes
When using a Raspberry Pi for I2C, the Linux kernel abstracts the hardware, meaning Python throws generic OS-level errors when the physical layer fails. Here are the exact error strings you will encounter and how to fix them.
Error 1: ValueError: No I2C device at address: 0x77
Ranked Causes:
- Wrong I2C Address: You are using a third-party BME280 clone that defaults to
0x76instead of the Adafruit default of0x77. - Sensor is Unpowered: The 3.3V rail is not reaching the VIN pin due to a broken jumper wire.
- Counterfeit Chip: The sensor is actually a BMP280 (no humidity) masquerading as a BME280, failing the internal ID register check.
Error 2: OSError: [Errno 121] Remote I/O error
Ranked Causes:
- Bus Lockup / Capacitance: Wires are too long, causing the SDA/SCL rise times to exceed the I2C specification, resulting in a NACK (Not Acknowledged) from the sensor.
- SDA/SCL Swapped: The data and clock lines are reversed.
- Thermal Throttling: The Pi 5 has throttled its core clock, disrupting the I2C timing bit-bang (rare on hardware I2C, common on software I2C).
- Verify the I2C Matrix: Run
i2cdetect -y 1in the terminal. You should see a77or76in the grid. If the grid is entirely empty, the bus is disabled or unpowered. If you seeUU, the kernel driver has already claimed the device. - Check config.txt: Open
/boot/firmware/config.txtand ensure the linedtparam=i2c_arm=onis present and uncommented. - Multimeter Continuity Test: With the Pi powered off, set your multimeter to continuity. Probe the sensor's GND pin and the Pi's metal USB shield (which is tied to system ground). A beep confirms your ground reference is solid.
Extending and Simplifying the Build
How to Extend: To turn this into a remote IoT node, integrate the paho-mqtt Python library to publish the sensor dictionary to a local Mosquitto broker. You can also add a 0.96-inch SSD1306 OLED display on the same I2C bus (address 0x3C) for local readouts without needing a network connection.
How to Simplify: The Raspberry Pi 5 is a powerhouse, but if your only goal is headless sensor logging, it is overkill. Downgrade the compute module to a Raspberry Pi Zero 2 W. It uses the exact same 40-pin I2C mapping, costs roughly $15, and draws less than 200mA, making it viable for 18650 lithium-ion battery packs with a simple TP4056 charging module.
Frequently Asked Questions
Is using a Raspberry Pi overkill for simple sensor reading?
Yes, if you are only reading one sensor and doing nothing else. A Raspberry Pi runs a full Linux kernel, requires a complex bootloader, and draws roughly 2.5W to 5W at idle. For simple, low-power sensor polling, an ESP32-C3 or Arduino Nano is vastly superior, drawing milliamps and booting in milliseconds. However, using a Raspberry Pi is justified if you need local database storage (SQLite), a web dashboard (Flask/Dash), or complex edge-computing tasks like FFT analysis on the sensor data.
What are the best practices when using a Raspberry Pi in headless mode?
When running without a monitor, always configure SSH with key-based authentication and disable password logins. More importantly, configure a static IP via dhcpcd.conf or your router's DHCP reservation so you don't lose track of the node. Finally, use systemd to wrap your Python script into a service with Restart=always and RestartSec=10 to ensure the script recovers automatically if the I2C bus throws a fatal OSError during a power flicker.
How do I prevent SD card corruption when using a Raspberry Pi for 24/7 logging?
MicroSD cards fail when subjected to continuous write cycles, such as logging sensor data every second to a local text file. To prevent this, mount the root filesystem as read-only using the overlayfs feature in raspi-config (Performance Options -> Overlay File System). Alternatively, write your sensor logs to a RAM disk (tmpfs) and use a cron job to batch-upload the data to an external server or USB thumb drive once an hour. For production deployments, bypass the SD card entirely and boot the Pi 5 from an NVMe SSD via the PCIe HAT.






