The best default configuration for a 24/7 Raspberry Pi 5 home lab telemetry node is the 8GB variant (SC1112) paired with the Official Active Cooler (SC1115), running a lightweight Python MQTT publisher over the hardware I2C bus. The Pi 5’s RP1 southbridge chip drastically improves I/O throughput compared to the Pi 4, but it also changes how I2C clock stretching and pull-ups behave under the newer Bookworm kernel. If you are building a rack-mounted sensor node to monitor your server cabinet's ambient temperature, humidity, and DC power draw, this guide gives you the exact hardware, pinout, and fault-tolerant code to get it running.

The Verdict: Sizing Your Raspberry Pi 5 Home Lab Node

Before ordering parts, you need to match the Pi 5 variant and thermal solution to your actual workload. The Pi 5 runs significantly hotter at idle than the Pi 4, and the RP1 chip adds its own thermal mass. Use this decision table to lock in your hardware.

Workload Profile RAM Pick Cooling Pick Why This Works
Pi-hole + MQTT Broker + Python Scripts 4GB Official Active Cooler Headless daemons rarely exceed 1GB RAM; Active Cooler keeps SoC under 60°C at 2W idle.
Docker + Home Assistant + Frigate NVR 8GB Argon NEO 5 / Tower Cooler Frigate and HA easily consume 3-4GB. Tower coolers handle sustained 8W+ loads in enclosed racks.
K3s Cluster Node + Ceph Storage 8GB Official Active Cooler + Case Fan Kubernetes agents are CPU-bursty. Active cooler handles bursts; case fan clears rack ambient heat.
DEFAULT PICK (Telemetry Node) 8GB Official Active Cooler 8GB future-proofs for local SQLite logging; Active Cooler is $5 and perfectly adequate for sensor polling.

Hardware Spec Sheet and Pin Mapping

For this build, we are reading DC current/voltage from a 12V/5V lab power rail and ambient cabinet conditions. We are using Adafruit’s breakout boards because they include the necessary 3.3V logic level shifting and I2C pull-ups, which is critical since the Pi 5 RP1 chip operates its GPIO bank at 3.3V but has different internal pull-up characteristics than the BCM2711.

Bench Note: The Pi 5’s I2C bus is routed through the RP1 southbridge, not the main BCM2712 SoC. This means I2C addresses and standard speeds (100kHz/400kHz) remain identical, but the RP1 handles clock stretching in hardware. You do not need the i2c-gpio bit-banging overlay anymore.

Bill of Materials

  • Compute: Raspberry Pi 5 8GB (Model: SC1112) — ~$80
  • Thermal: Raspberry Pi 5 Active Cooler (Model: SC1115) — ~$5
  • Power: 27W USB-C PD Power Supply (Official) — ~$12
  • Current Sensor: Adafruit INA219 I2C Breakout (Product ID: 3650) — ~$10
  • Env Sensor: Adafruit BME280 I2C Breakout (Product ID: 2652) — ~$15
  • Wiring: 22 AWG silicone jumper wires (pre-crimped Dupont)

Pin Mapping Table

Both sensors share the same I2C bus. The INA219 defaults to address 0x40 and the BME280 defaults to 0x77 (or 0x76 depending on the jumper pad).

Pi 5 GPIO Pin Physical Pin # Function INA219 Pad BME280 Pad
3V3 Power 1 VCC (3.3V Logic) VIN VIN
Ground 6 GND GND GND
GPIO 2 (SDA1) 3 I2C Data SDA SDA
GPIO 3 (SCL1) 5 I2C Clock SCL SCL

Assembly and I2C Bus Configuration

  1. Apply Thermal Paste and Cooler: The Pi 5 Active Cooler comes with pre-applied thermal pads. Peel the film, align the standoffs with the PCB holes, and secure with the provided screws. Plug the 4-pin PWM fan cable into the FAN header on the Pi 5.
  2. Wire the I2C Bus: Connect the 3.3V, GND, SDA, and SCL pins from the Pi 5 to a common breadboard rail, then branch out to both the INA219 and BME280. Keep I2C wire runs under 30cm (12 inches) to avoid bus capacitance issues.
  3. Configure Pi OS Bookworm: Flash Raspberry Pi OS Bookworm 64-bit (Lite version). Boot the Pi, SSH in, and run sudo raspi-config. Navigate to Interface Options -> I2C and enable it.
  4. Verify the Bus: Install the I2C tools and scan the bus:
    sudo apt update && sudo apt install i2c-tools -y
    i2cdetect -y 1
    You should see 40 and 77 (or 76) in the output grid. If you see empty dashes, check your crimps.

Python Telemetry Script with Error Handling

This script targets the Raspberry Pi 5 8GB running Bookworm 64-bit. It uses the Adafruit Blinka ecosystem to handle the complex BME280 calibration math and the INA219 shunt calculations, publishing the results to an MQTT broker. We wrap the I2C reads in strict try/except blocks because I2C buses in rack environments are prone to transient electrical noise.

Install the dependencies first:

sudo apt install python3-pip python3-venv -y
python3 -m venv ~/lab-telemetry
source ~/lab-telemetry/bin/activate
pip install adafruit-circuitpython-ina219 adafruit-circuitpython-bme280 paho-mqtt

Create telemetry.py and paste the following complete code:

#!/usr/bin/env python3
"""
Raspberry Pi 5 Home Lab Telemetry Node
Target Board: Raspberry Pi 5 8GB (SC1112) / Bookworm 64-bit
Sensors: INA219 (0x40), BME280 (0x77)
"""

import time
import sys
import board
import busio
import paho.mqtt.client as mqtt
from adafruit_ina219 import INA219, ADCResolution, BusVoltageRange
import adafruit_bme280

# --- PIN & I2C DEFINITIONS ---
# Pi 5 Hardware I2C1 (Physical Pins 3 and 5)
I2C_SDA = board.SDA
I2C_SCL = board.SCL

# --- MQTT CONFIGURATION ---
MQTT_BROKER = "192.168.1.50"
MQTT_PORT = 1883
MQTT_TOPIC_PREFIX = "homelab/rack1/telemetry"

# Initialize I2C Bus
i2c = busio.I2C(I2C_SCL, I2C_SDA)

def init_sensors():
    """Initialize sensors with error handling for boot-time I2C faults."""
    try:
        ina219 = INA219(i2c, addr=0x40)
        ina219.bus_adc_resolution = ADCResolution.ADCRES_12BIT_1S
        ina219.shunt_adc_resolution = ADCResolution.ADCRES_12BIT_1S
        ina219.bus_voltage_range = BusVoltageRange.RANGE_16V
        
        bme280 = adafruit_bme280.Adafruit_BME280_I2C(i2c, address=0x77)
        bme280.sea_level_pressure = 1013.25
        
        return ina219, bme280
    except ValueError as e:
        print(f"FATAL: Sensor not found on I2C bus. Check wiring and addresses. Error: {e}")
        sys.exit(1)

def on_connect(client, userdata, flags, rc, properties=None):
    if rc == 0:
        print("Connected to MQTT Broker")
    else:
        print(f"MQTT Connection failed with code {rc}")

def main():
    ina219, bme280 = init_sensors()
    
    client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
    client.on_connect = on_connect
    
    try:
        client.connect(MQTT_BROKER, MQTT_PORT, 60)
        client.loop_start()
    except Exception as e:
        print(f"FATAL: Could not connect to MQTT broker at {MQTT_BROKER}. Error: {e}")
        sys.exit(1)

    print("Telemetry daemon started. Polling every 10 seconds...")
    
    while True:
        try:
            # Read INA219 Power Data
            bus_voltage = ina219.bus_voltage
            current_ma = ina219.current
            power_mw = ina219.power
            
            # Read BME280 Environmental Data
            temp_c = bme280.temperature
            humidity = bme280.relative_humidity
            pressure = bme280.pressure
            
            # Publish to MQTT
            client.publish(f"{MQTT_TOPIC_PREFIX}/voltage", f"{bus_voltage:.2f}")
            client.publish(f"{MQTT_TOPIC_PREFIX}/current_ma", f"{current_ma:.1f}")
            client.publish(f"{MQTT_TOPIC_PREFIX}/temp_c", f"{temp_c:.1f}")
            client.publish(f"{MQTT_TOPIC_PREFIX}/humidity", f"{humidity:.1f}")
            
            print(f"V: {bus_voltage:.2f} | I: {current_ma:.1f}mA | T: {temp_c:.1f}C | H: {humidity:.1f}%")
            
        except OSError as e:
            # Catches I2C bus drops (Errno 121 / 110)
            print(f"WARNING: I2C Bus Error ({e}). Sensors will be re-initialized.")
            time.sleep(2)
            ina219, bme280 = init_sensors()
            
        except Exception as e:
            print(f"ERROR: Unexpected fault: {e}")
            
        time.sleep(10)

if __name__ == "__main__":
    main()

Debugging I2C Faults: Errno 121 and Errno 110

When running I2C sensors in a home lab rack near switching power supplies and Wi-Fi routers, you will inevitably hit I2C bus faults. The RP1 chip is strict about protocol violations. If your script crashes or throws warnings, look for these exact error strings.

Symptom: OSError: [Errno 121] Remote I/O error
Meaning: The Pi 5 sent a clock pulse, but the sensor NAK’d (did not acknowledge) the transaction. The bus is physically connected, but the logical handshake failed.

The First Three Things to Check When It Fails

  1. Run i2cdetect -y 1: If your sensor address (e.g., 40) suddenly shows as -- or UU, the sensor has crashed or lost power. UU means a kernel driver has claimed it (common if you accidentally enabled a device tree overlay for a BME280 in config.txt while also trying to read it via Python). Remove the overlay and reboot.
  2. Verify Pull-Up Resistors: The Pi 5 RP1 chip has internal pull-ups, but they are weak (~50kΩ). Adafruit breakouts include 10kΩ pull-ups. If you are using raw sensor modules (like the $2 eBay BME280 clones), you must add external 4.7kΩ pull-up resistors between SDA/SCL and 3.3V. Without them, the signal edges are too slow, and the RP1 will read garbage data or throw Errno 121.
  3. Check Wire Capacitance and Length: I2C is not designed for long runs. If your jumper wires exceed 30cm, the bus capacitance exceeds the 400pF I2C spec. The fix is either shorten the wires, drop the bus speed to 50kHz in config.txt (dtparam=i2c_baudrate=50000), or use an I2C bus extender like the PCA9600.
Symptom: OSError: [Errno 110] Connection timed out
Meaning: The SCL (Clock) line is being held low. This is classic "clock stretching" gone wrong. The sensor is pulling the clock line low to ask for more processing time, but the Pi 5 RP1 times out waiting for the release. This almost always points to a faulty sensor module or a missing common ground between the Pi and the sensor power supply.

Scaling the Build: Extend or Simplify

A home lab node should match your maintenance tolerance. Here is how to adjust this build based on your infrastructure needs.

How to Simplify (No Network Dependency)

If you don't want to maintain an MQTT broker or Home Assistant instance, strip the paho-mqtt library out of the script. Replace the publish block with Python’s built-in sqlite3 library to log data to a local telemetry.db file on the Pi’s SD card. You can then use a simple cron job to email you a daily CSV summary, or just SSH in and query the database when you need to check historical rack temperatures.

How to Extend (Add Storage and Automation)

If you want to turn this into a true edge-compute node:

  • Add NVMe Storage: Use the official Raspberry Pi M.2 HAT+ to mount a 2230 or 2242 NVMe SSD. Boot the Pi 5 directly from the SSD to eliminate SD card corruption from frequent SQLite writes.
  • Add Load Shedding: Wire an I2C relay module (like the Adafruit 4-Channel Relay Breakout) to the same I2C bus. Modify the Python script to monitor the INA219 current draw; if the current exceeds 4000mA for more than 30 seconds, trigger the relay to cut power to non-essential lab equipment, protecting your main UPS.
  • Integrate with Home Assistant: Use the Home Assistant MQTT integration to auto-discover these topics. By formatting your MQTT payloads as JSON and using the homeassistant/sensor/ discovery topic prefix, the Pi 5 will automatically populate your HA dashboard without manual YAML configuration.

For further reading on the Pi 5's I/O architecture, refer to the official Raspberry Pi 5 documentation regarding the RP1 southbridge, and consult the Adafruit INA219 guide for deep-dives into shunt resistor calibration.