The Decision Tree: Picking the Right Board
When planning raspberry pi projects smart home builders often default to whatever board is in their parts bin. That is a mistake. The introduction of the RP1 I/O controller chip in the Raspberry Pi 5 fundamentally changed GPIO performance and I2C bus stability, making board selection a critical architectural decision for a 24/7 smart home hub.
| Criteria | Pi Zero 2 W | Pi 4 Model B (4GB) | Pi 5 (4GB) |
|---|---|---|---|
| Base Price (approx.) | $15 (often marked up to $35) | $55 | $60 |
| I2C Bus Stability | Poor under CPU load | Good (BCM2711) | Excellent (RP1 dedicated chip) |
| Home Assistant OS Support | Sluggish UI, high latency | Smooth, but runs hot | Native, fast, PCIe NVMe capable |
| Power Draw (Idle) | ~0.7W | ~2.5W | ~2.8W |
Hardware Spec Sheet and Pin Mapping
This build uses the Raspberry Pi 5 to poll a BME280 environmental sensor via I2C and toggle a 4-channel optocoupler relay via GPIO, publishing and subscribing to an MQTT broker (like Mosquitto running on your Home Assistant server).
Parts List
- Compute: Raspberry Pi 5 (4GB) with official 27W USB-C PD power supply and Active Cooler.
- Sensor: BME280 Breakout Board (ensure it has a 3.3V voltage regulator on the back, not a raw 1.8V die).
- Actuator: 5V 4-Channel Relay Module with Optocoupler isolation (e.g., Songle SRD-05VDC-SL-C).
- Wiring: 22 AWG solid core hookup wire, 4.7kΩ pull-up resistors (if BME280 breakout lacks them).
Pin Mapping Table (BCM Numbering)
| Component | Component Pin | Pi 5 GPIO (BCM) | Pi 5 Physical Pin | Notes |
|---|---|---|---|---|
| BME280 | VIN | 3V3 | 1 | Do not use 5V; risk of logic level damage. |
| BME280 | GND | GND | 6 | Common ground with Pi and Relay. |
| BME280 | SCL | GPIO 3 (SCL1) | 5 | Requires 4.7kΩ pull-up to 3.3V. |
| BME280 | SDA | GPIO 2 (SDA1) | 3 | Requires 4.7kΩ pull-up to 3.3V. |
| Relay Ch 1 | IN1 | GPIO 17 | 11 | Controls HVAC Fan. |
| Relay Ch 2 | IN2 | GPIO 27 | 13 | Controls Damper. |
| Relay VCC | VCC | 5V | 2 | Pi 5 5V rail can source up to 1.5A total. |
Step-by-Step Wiring and Assembly
- Prep the I2C Bus: Cheap BME280 breakouts from online marketplaces often omit the 4.7kΩ pull-up resistors on SDA and SCL. Solder a 4.7kΩ resistor between the SDA and 3.3V pads, and another between SCL and 3.3V pads on the breakout board. Without these, the RP1 chip's I2C controller will fail to register the rising edges.
- Wire the Sensor: Connect the BME280 to the Pi's physical pins 1, 3, 5, and 6 as per the table above.
- Wire the Relay Optocouplers: Connect the Relay VCC to Physical Pin 2 (5V) and Relay GND to Physical Pin 9. Connect IN1 to Physical Pin 11 (GPIO 17).
- Isolate the Logic (Optional but Recommended): If your relay module has a JD-VCC jumper, remove it. Feed the JD-VCC side from a separate 5V buck converter, and only connect the optocoupler ground to the Pi. This prevents relay coil flyback from resetting the Pi 5.
- Verify Connections: Use a multimeter in continuity mode to check for shorts between 3.3V and GND before applying power.
The Python MQTT Control Script
The following script targets the Raspberry Pi 5 (4GB) running Raspberry Pi OS (Bookworm 64-bit). It uses gpiozero (which natively supports the Pi 5's RP1 chip via the lgpio backend) and smbus2 for I2C communication.
First, install the dependencies via your terminal:
sudo apt update
sudo apt install python3-gpiozero python3-pip i2c-tools
pip3 install paho-mqtt smbus2 pimoroni-bme280 --break-system-packages
Create smart_hub.py:
import time
import json
import paho.mqtt.client as mqtt
from gpiozero import OutputDevice
import smbus2
import bme280
# --- PIN & CONFIG DEFINITIONS ---
RELAY_PIN_HVAC = 17 # BCM 17
RELAY_PIN_DAMPER = 27 # BCM 27
I2C_BUS = 1 # /dev/i2c-1
BME280_ADDR = 0x76 # Check with i2cdetect; some are 0x77
MQTT_BROKER = "192.168.1.50"
MQTT_PORT = 1883
MQTT_TOPIC_SENSOR = "homeassistant/sensor/bme280/state"
MQTT_TOPIC_RELAY = "homeassistant/switch/hvac/set"
# --- HARDWARE INIT ---
hvac_relay = OutputDevice(RELAY_PIN_HVAC, active_high=False, initial_value=False)
damper_relay = OutputDevice(RELAY_PIN_DAMPER, active_high=False, initial_value=False)
bus = smbus2.SMBus(I2C_BUS)
try:
calibration_params = bme280.load_calibration_params(bus, BME280_ADDR)
print("BME280 initialized successfully.")
except OSError as e:
print(f"Fatal I2C Error: {e}. Check wiring and pull-ups.")
exit(1)
# --- MQTT CALLBACKS ---
def on_connect(client, userdata, flags, reason_code, properties):
if reason_code == 0:
print("Connected to MQTT Broker")
client.subscribe(MQTT_TOPIC_RELAY)
else:
print(f"MQTT Connection failed with code {reason_code}")
def on_message(client, userdata, msg):
payload = msg.payload.decode('utf-8').strip().upper()
print(f"Received command: {payload}")
if payload == "ON":
hvac_relay.on() # active_high=False means .on() pulls pin LOW to trigger optocoupler
elif payload == "OFF":
hvac_relay.off()
# --- MAIN LOOP ---
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="pi5_smart_hub")
client.on_connect = on_connect
client.on_message = on_message
try:
client.connect(MQTT_BROKER, MQTT_PORT, 60)
client.loop_start()
except Exception as e:
print(f"MQTT Broker unreachable: {e}")
exit(1)
try:
while True:
data = bme280.sample(bus, BME280_ADDR, calibration_params)
payload = json.dumps({
"temperature": round(data.temperature, 2),
"humidity": round(data.humidity, 2),
"pressure": round(data.pressure, 1)
})
client.publish(MQTT_TOPIC_SENSOR, payload)
time.sleep(30) # Poll every 30 seconds
except KeyboardInterrupt:
print("Shutting down...")
finally:
client.loop_stop()
hvac_relay.close()
damper_relay.close()
Debugging: Fixing the I2C Remote I/O Error
When working with I2C sensors on the Pi 5, you will inevitably encounter this exact error string when running the script or i2cdetect:
OSError: [Errno 121] Remote I/O error
This error means the Pi's I2C controller sent the address byte (0x76), but the BME280 NACKed (did not acknowledge) it. Here is the ranked decision path to fix it, ordered from most to least likely:
- I2C Interface is Disabled (80% of cases): Raspberry Pi OS Bookworm ships with I2C disabled by default. Run
sudo raspi-config, navigate to Interface Options -> I2C, and enable it. Reboot. - Missing Pull-Up Resistors (15% of cases): The RP1 chip has weak internal pull-ups that are insufficient for the capacitance of standard jumper wires. Measure the SDA and SCL lines with a multimeter; they should read ~3.3V when idle. If they read near 0V or float, solder 4.7kΩ external pull-up resistors to the 3.3V rail.
- Wrong I2C Address (4% of cases): Some BME280 breakouts tie the SDO pin high, shifting the address to
0x77. Runi2cdetect -y 1. If you see a77in the grid instead of76, update theBME280_ADDRvariable in the Python script. - Thermal Throttling / Voltage Sag (1% of cases): If the Pi 5 power supply is inadequate (e.g., using a standard 5V/3A phone charger instead of the 27W PD supply), the 3.3V rail will sag under CPU load, causing the sensor to brownout and drop off the bus. Check for the lightning bolt icon on the display or run
vcgencmd get_throttled.
Scaling the Hub: Extend or Simplify
Once your baseline MQTT relay and sensor hub is stable, you must decide how to scale the system based on your home's topology.
How to Extend the Build
If you need to integrate wireless sensors (like door contacts or smart bulbs) without relying on WiFi, add a Zigbee coordinator. Plug a Sonoff ZBDongle-E (EFR32MG21 chip, ~$25) into the Pi 5's USB 2.0 port (do not use the USB 3.0 port; 2.4GHz Zigbee interference from USB 3.0 data lines will destroy your mesh network range). Pass the dongle through to Home Assistant using the ZHA (Zigbee Home Automation) integration. This turns your Pi into a unified wired/wireless bridge.
How to Simplify the Build
If running a dedicated MQTT broker and writing Python scripts feels like overkill for a single room, drop the MQTT layer entirely. Install Flask on the Pi and expose a local REST API. You can then use the native "RESTful Switch" integration in Home Assistant to send simple HTTP GET requests (http://pi-ip:5000/relay/on) to toggle the GPIO pins. This eliminates the broker dependency and reduces the software stack to a single Python file.
For a dedicated, hardwired smart home node, the Raspberry Pi 5 (4GB) remains the definitive choice. Its RP1 I/O controller resolves the legacy I2C jitter issues of the Pi 4, and its PCIe lane allows you to boot from a reliable NVMe SSD, completely eliminating the SD card corruption failures that plague older smart home builds.






