If you are building raspberry pi smart home projects in 2026, the default architecture has shifted. The days of running a Pi 3B+ headless with a messy web server are over. Modern smart home hubs require hardware acceleration, robust I/O isolation, and native MQTT integration to communicate with platforms like Home Assistant. The direct answer for a new build: use a Raspberry Pi 5 (4GB) running Raspberry Pi OS Bookworm, driving an optocoupler-isolated relay bank via MQTT, and reading environmental data over I2C.
This guide walks through building a 4-channel MQTT relay hub with a BME280 environmental sensor. We will cover the hardware decision matrix, the critical optocoupler wiring trap that destroys Pi logic boards, and the exact Python code required to navigate the Pi 5's new libgpiod backend.
The Decision Path: Which Pi for Your Smart Home Hub?
Do not default to the cheapest board on your workbench. Smart home hubs run 24/7, manage local network traffic, and often host Docker containers (like Zigbee2MQTT or Frigate). Use this decision matrix to select your board.
| Board Variant | RAM | I/O & Network | Verdict & Use Case |
|---|---|---|---|
| Raspberry Pi 3B+ | 1GB | USB 2.0, 2.4GHz WiFi only | Reject. 1GB RAM chokes on modern Home Assistant OS. SD card corruption is highly likely under continuous logging. |
| Raspberry Pi 4 Model B | 2GB / 4GB | USB 3.0, Gigabit Ethernet | Acceptable for legacy. Good for basic MQTT nodes, but lacks the PCIe lane for NVMe booting and runs hot without massive heatsinks. |
| Raspberry Pi 5 | 4GB / 8GB | PCIe 2.0, Dual 4K, RTC | DEFAULT PICK. The 4GB variant handles HAOS, MQTT brokers, and local LLMs effortlessly. Built-in RTC maintains time during network outages. |
Parts List & Spec Sheet
Here is the exact bill of materials for this build. Prices reflect typical 2026 market rates for genuine components.
| Component | Exact Variant / Model | Est. Cost | Why This Specific Part? |
|---|---|---|---|
| Microcontroller | Raspberry Pi 5 (4GB) | $60 | RPi silicon RP1 I/O chip handles GPIO interrupts natively without CPU spiking. |
| Power Supply | Official 27W USB-C PD (5V/5A) | $12 | Prevents brownouts when 4 relay coils energize simultaneously. |
| Cooling | Pi 5 Active Cooler | $5 | PWM-controlled. Keeps SoC under 60°C in enclosed smart home panels. |
| Relay Module | 4-Channel 5V Relay with Optocoupler (LC-04A) | $8 | Optocouplers physically isolate mains AC noise from the Pi's 3.3V logic. |
| Sensor | Adafruit BME280 I2C (Part 2652) | $15 | Measures temp, humidity, and pressure. I2C avoids the SPI pin conflicts on Pi 5. |
Pin Mapping & The Optocoupler Jumper Trap
Wiring a 5V relay module to a 3.3V logic board (like any Raspberry Pi) is where most hobbyists fry their GPIO pins. Standard relay boards feature a JDVCC jumper. If you leave this jumper in place and power the board from the Pi's 5V pin, the 5V logic from the optocoupler output can backfeed into the Pi's 3.3V GPIO header when the pin is pulled low.
The Fix: Remove the JDVCC jumper. Feed the relay coils (JDVCC and GND) from the Pi's 5V and GND pins. Feed the optocoupler LEDs (VCC and GND) from the Pi's 3.3V and GND pins. This ensures complete electrical isolation.
| Pi 5 GPIO (Physical Pin) | Function | Relay / Sensor Target | Voltage Level |
|---|---|---|---|
| GPIO 5 (Pin 29) | Relay 1 Control | IN1 (Optocoupler LED) | 3.3V Logic |
| GPIO 6 (Pin 31) | Relay 2 Control | IN2 (Optocoupler LED) | 3.3V Logic |
| GPIO 13 (Pin 33) | Relay 3 Control | IN3 (Optocoupler LED) | 3.3V Logic |
| GPIO 19 (Pin 35) | Relay 4 Control | IN4 (Optocoupler LED) | 3.3V Logic |
| GPIO 2 (Pin 3) / GPIO 3 (Pin 5) | I2C SDA / SCL | BME280 Sensor | 3.3V Logic |
| 5V (Pin 2) / GND (Pin 6) | Coil Power | JDVCC / GND (Jumper Removed) | 5V Power |
| 3.3V (Pin 1) / GND (Pin 9) | Opto LED Power | VCC / GND (Optocoupler side) | 3.3V Power |
Python Control Code (MQTT & GPIO)
This script targets the Raspberry Pi 5 running Bookworm (64-bit). It uses gpiozero (which automatically leverages the rpi-lgpio backend on Bookworm) and Paho MQTT v2.0.
Install dependencies first:
sudo apt install python3-rpi-lgpio python3-smbus2
pip3 install paho-mqtt adafruit-circuitpython-bme280
import time
import json
import board
import adafruit_bme280
import paho.mqtt.client as mqtt
from gpiozero import OutputDevice
from paho.mqtt.enums import CallbackAPIVersion
# --- PIN DEFINITIONS ---
RELAY_PINS = {
"relay_1": OutputDevice(5, active_high=False), # Active low for most optocouplers
"relay_2": OutputDevice(6, active_high=False),
"relay_3": OutputDevice(13, active_high=False),
"relay_4": OutputDevice(19, active_high=False)
}
# --- MQTT CONFIGURATION ---
MQTT_BROKER = "homeassistant.local"
MQTT_PORT = 1883
MQTT_USER = "pi_hub"
MQTT_PASS = "your_secure_password"
TOPIC_CMD = "smarthome/relays/+/set"
TOPIC_TELE = "smarthome/telemetry/environment"
def on_connect(client, userdata, flags, reason_code, properties):
"""Paho MQTT v2.0 connection callback."""
if reason_code == 0:
print("Connected to MQTT Broker successfully.")
client.subscribe(TOPIC_CMD)
else:
print(f"MQTT Connection failed with code: {reason_code}")
def on_message(client, userdata, msg):
"""Handle incoming relay commands."""
try:
topic_parts = msg.topic.split('/')
relay_name = topic_parts[2] # e.g., 'relay_1'
payload = msg.payload.decode().upper()
if relay_name in RELAY_PINS:
if payload == "ON":
RELAY_PINS[relay_name].on()
print(f"{relay_name} ENGAGED")
elif payload == "OFF":
RELAY_PINS[relay_name].off()
print(f"{relay_name} DISENGAGED")
except Exception as e:
print(f"Error processing MQTT message: {e}")
def main():
# Initialize I2C Sensor
try:
i2c = board.I2C()
bme280 = adafruit_bme280.basic.Adafruit_BME280_I2C(i2c, address=0x77)
print("BME280 Sensor initialized on I2C.")
except ValueError as e:
print(f"BME280 Init Error: {e}. Check I2C address (0x76 vs 0x77).")
bme280 = None
# Initialize MQTT Client (Using v2 API)
client = mqtt.Client(CallbackAPIVersion.VERSION2, client_id="Pi5_RelayHub")
client.username_pw_set(MQTT_USER, MQTT_PASS)
client.on_connect = on_connect
client.on_message = on_message
try:
client.connect(MQTT_BROKER, MQTT_PORT, 60)
except ConnectionRefusedError:
print("MQTT Broker unreachable. Check network and Mosquitto service.")
return
client.loop_start()
# Main Telemetry Loop
try:
while True:
if bme280:
telemetry = {
"temp_c": round(bme280.temperature, 2),
"humidity": round(bme280.relative_humidity, 2),
"pressure_hpa": round(bme280.pressure, 2)
}
client.publish(TOPIC_TELE, json.dumps(telemetry))
# Sleep 60s between telemetry pushes to avoid broker spam
time.sleep(60)
except KeyboardInterrupt:
print("Shutting down safely...")
finally:
client.loop_stop()
client.disconnect()
for relay in RELAY_PINS.values():
relay.off()
relay.close()
if __name__ == "__main__":
main()
Debugging: "lgpio.error" and Pi 5 GPIO Failures
When migrating older smart home scripts to the Pi 5, you will inevitably hit the new GPIO subsystem. Bookworm replaced the legacy sysfs interface with libgpiod. If your code throws the following error, do not attempt to downgrade your OS; fix the backend.
lgpio.error: 'gpiochip4' is not a valid chip
This exact error string occurs because the Pi 5's RP1 I/O chip maps to gpiochip4, but legacy libraries (like RPi.GPIO) are hardcoded to look for gpiochip0 on the BCM2711 SoC.
Ranked Causes and Fixes
- Missing
rpi-lgpiobackend (Most Likely):gpiozerorequires therpi-lgpiopackage to translate commands to the Pi 5's hardware. Fix: Runsudo apt install python3-rpi-lgpio. - Using Legacy
RPi.GPIO: TheRPi.GPIOlibrary is effectively dead on Bookworm. Fix: Refactor your code to usegpiozeroor directlgpiobindings as shown in the script above. - I2C Bus Mapping Collision: The Pi 5 uses I2C0 for the HAT EEPROM and I2C1 for the GPIO header. If you are using a HAT that hardcodes I2C0, the BME280 will fail to initialize. Fix: Wire raw sensors directly to Physical Pins 3 and 5 (I2C1), bypassing HAT headers.
The First Three Things to Check When It Fails
If your relays aren't clicking or Home Assistant isn't seeing the MQTT payloads, run this triage sequence:
- Verify OS Version: Run
cat /etc/os-release. If it says "Bullseye", yourlibgpiodassumptions are wrong. You must be on Bookworm or newer for Pi 5 stability. - Check I2C Interface Toggle: Bookworm moved I2C configuration. Run
sudo raspi-config, navigate to Interface Options > I2C, and ensure it is explicitly enabled. Reboot after changing. - Inspect MQTT ACLs: If the script connects but publishes nothing, your Mosquitto broker's Access Control List is blocking the
smarthome/topic. Check/etc/mosquitto/aclon your broker.
Extending and Simplifying the Build
A centralized Pi 5 hub is excellent for logic, but running 20-foot I2C or GPIO wires to remote rooms violates signal integrity limits and invites EMI noise. Here is how to scale the architecture correctly.
How to Simplify (Downscale)
If you only need to control a single HVAC damper or read one room's temperature, drop the Pi entirely. Use an ESP32-C3 SuperMini ($4). Flash it with ESPHome, which natively compiles YAML into C++ and pushes data directly to Home Assistant via the ESPHome API, eliminating the need for a standalone MQTT broker and Python scripts.
How to Extend (Upscale)
To expand this Pi 5 hub into a whole-home nerve center without running miles of copper wire:
- Add a Zigbee Coordinator: Plug a Home Assistant SkyConnect (or Sonoff ZBDongle-P) into the Pi 5's USB 2.0 port. Use a 1-meter USB extension cable to move the dongle away from the Pi 5's USB 3.0 controller, which generates severe 2.4GHz RF interference that will cripple Zigbee mesh networks.
- Remote Nodes via PoE: For remote relay control (e.g., garden lighting, garage doors), deploy Waveshare ESP32-S3 Ethernet boards powered via PoE. They communicate back to this Pi 5's MQTT broker over your existing CAT6 network, keeping mains voltage and logic completely localized to the remote enclosure.
By anchoring your smart home on the Pi 5's isolated I/O and modern MQTT standards, you eliminate the fragile web-scraping integrations of the past. Lock in your optocoupler wiring, respect the libgpiod backend, and let the broker handle the routing.






