To code a Raspberry Pi for physical hardware control in 2026, you must target the Pi 5’s RP1 southbridge chip using Python with the gpiozero library backed by lgpio. Legacy libraries like RPi.GPIO are fundamentally incompatible with the Pi 5's new architecture. This guide walks you through building a closed-loop thermal cooling system—reading a DHT22 temperature sensor and driving a PWM-controlled 5V cooling fan—while navigating the strict package management rules of Raspberry Pi OS Bookworm.
Raspberry Pi 5 vs Legacy Boards: Hardware Spec Sheet
The shift from the BCM2711 SoC to the BCM2712 paired with the RP1 southbridge changed how the Pi handles GPIO memory mapping. If you are migrating code from a Pi 4, you need to understand these hardware differences before writing your first script.
| Feature | Raspberry Pi 5 (8GB) | Raspberry Pi 4 Model B (8GB) | Raspberry Pi 3 Model B+ |
|---|---|---|---|
| Primary SoC | BCM2712 (Quad-core Cortex-A76) | BCM2711 (Quad-core Cortex-A72) | BCM2837B0 (Quad-core Cortex-A53) |
| GPIO Controller | RP1 Southbridge (PCIe connected) | Integrated BCM2711 Peripheral | Integrated BCM2835 Peripheral |
| Hardware PWM Channels | 4 dedicated channels (RP1) | 2 dedicated channels | 2 dedicated channels |
| Default Python Backend | lgpio (via gpiozero) |
RPi.GPIO or lgpio |
RPi.GPIO |
| Max GPIO Current (Total) | 50mA (strictly enforced by RP1) | 50mA | 50mA |
| OS Package Manager | Bookworm (PEP 668 enforced) | Bullseye / Bookworm | Buster / Bullseye |
Parts List and GPIO Pin Mapping
This build assumes you are using a Raspberry Pi 5 (8GB variant, SKU SC1128) running the 64-bit version of Raspberry Pi OS Bookworm. We are using a 5V PWM fan to avoid needing an external 12V power supply, keeping the build bench-friendly.
Required Components
- Microcontroller: Raspberry Pi 5 (8GB)
- Sensor: DHT22 / AM2302 (3-pin or 4-pin module with onboard 10kΩ pull-up)
- Actuator: Noctua NF-A4x10 5V PWM Fan (or generic 4-pin 5V PWM PC fan)
- Resistors: 1x 10kΩ (if using raw 4-pin DHT22), 1x 1kΩ (for PWM signal protection)
- Wiring: 24 AWG solid core jumper wires
Pin Mapping Table
| Component | Component Pin | Pi 5 GPIO (BCM) | Pi 5 Physical Pin | Function |
|---|---|---|---|---|
| DHT22 Sensor | VCC | N/A | 1 (or 17) | 3.3V Power |
| DHT22 Sensor | DATA | GPIO 4 | 7 | Bit-banged 1-Wire Data |
| DHT22 Sensor | GND | N/A | 9 | Ground |
| PWM Fan | VCC (Yellow/Red) | N/A | 2 (or 4) | 5V Power |
| PWM Fan | GND (Black) | N/A | 14 | Ground |
| PWM Fan | PWM (Blue) | GPIO 18 | 12 | Hardware PWM0 (via 1kΩ) |
Environment Setup: Navigating Bookworm and PEP 668
Raspberry Pi OS Bookworm enforces PEP 668, which marks the system Python environment as 'externally managed'. If you try to run pip install adafruit-circuitpython-dht globally, the OS will block you to prevent breaking system utilities. You must use a virtual environment or the system package manager.
- Update the system:
sudo apt update && sudo apt upgrade -y - Install system-level GPIO backends:
sudo apt install python3-gpiozero python3-lgpio libgpiod2 -y - Create a virtual environment:
python3 -m venv ~/thermal_env - Activate the environment:
source ~/thermal_env/bin/activate - Install the DHT sensor library:
pip install adafruit-circuitpython-dht
The Complete Python Thermal Control Script
This script reads the DHT22 every 5 seconds. If the temperature exceeds 45°C, it ramps up the PWM fan. It includes robust error handling for the DHT22's notorious 'checksum mismatch' and 'timeout' errors, which occur when the bit-banged 1-Wire protocol is interrupted by OS background tasks.
import time
import board
import adafruit_dht
from gpiozero import PWMLED
from signal import pause
# --- PIN DEFINITIONS ---
DHT_PIN = board.D4 # Physical Pin 7
FAN_PIN = 18 # Physical Pin 12 (Hardware PWM0)
# --- CONFIGURATION ---
TEMP_THRESHOLD_LOW = 35.0 # Celsius: Fan starts spinning
TEMP_THRESHOLD_HIGH = 50.0 # Celsius: Fan at 100%
READ_INTERVAL = 5.0 # Seconds between sensor reads
# Initialize hardware
# PWMLED is used here as a generic PWM output driver in gpiozero
dht_device = adafruit_dht.DHT22(DHT_PIN)
fan_pwm = PWMLED(FAN_PIN, frequency=25000) # 25kHz is standard for PC fans
def calculate_duty_cycle(temp):
if temp < TEMP_THRESHOLD_LOW:
return 0.0
elif temp >= TEMP_THRESHOLD_HIGH:
return 1.0
else:
# Linear interpolation between low and high thresholds
return (temp - TEMP_THRESHOLD_LOW) / (TEMP_THRESHOLD_HIGH - TEMP_THRESHOLD_LOW)
def main():
print('Thermal control loop started. Press Ctrl+C to exit.')
try:
while True:
try:
temp_c = dht_device.temperature
humidity = dht_device.humidity
if temp_c is not None:
duty = calculate_duty_cycle(temp_c)
fan_pwm.value = duty
print(f'Temp: {temp_c:0.1f}C | Humidity: {humidity:0.1f}% | Fan PWM: {duty*100:0.1f}%')
else:
print('Sensor returned null. Skipping cycle.')
except RuntimeError as error:
# DHT22 frequently throws checksum/timeout errors on Linux
# We catch them and retry on the next loop instead of crashing
print(f'Sensor read error: {error.args[0]}. Retrying next cycle.')
time.sleep(2.0)
continue
except Exception as e:
print(f'Unexpected hardware error: {e}')
fan_pwm.value = 1.0 # Failsafe: 100% fan on error
time.sleep(5.0)
time.sleep(READ_INTERVAL)
except KeyboardInterrupt:
print('\nShutting down safely...')
finally:
fan_pwm.off()
dht_device.exit()
print('GPIO cleaned up. Fan stopped.')
if __name__ == '__main__':
main()
Debugging: "Cannot determine SOC peripheral base address"
If you copy-pasted a tutorial from 2021, you will likely hit this exact error string when running your script on a Pi 5:
RuntimeError: Cannot determine SOC peripheral base address
This happens because legacy code attempts to map the memory address of the BCM2711 GPIO controller, which does not exist on the Pi 5. The Pi 5 routes GPIO through the RP1 chip over a PCIe link, completely changing the memory map.
The First Three Things to Check When It Fails
- Check your imports: Are you using
import RPi.GPIO as GPIO? If so, delete it. You must refactor your code to usegpiozeroorlgpio.RPi.GPIOis effectively dead for Pi 5 hardware. - Verify the backend package: Run
apt list --installed | grep lgpio. Ifpython3-lgpiois missing,gpiozerowill silently fall back to tryingRPi.GPIOand crash. Install it viasudo apt install python3-lgpio. - Check user permissions: The RP1 GPIO memory requires specific group permissions. Ensure your user is in the
gpiogroup by runninggroups. If missing, runsudo usermod -aG gpio $USERand reboot.
--device /dev/gpiomem and --privileged flags in your docker run command, or map the specific /dev/gpiochip0 device.
How to Extend or Simplify the Build
Simplify: Swap to I2C
The DHT22 uses a bit-banged protocol that is highly susceptible to Linux kernel scheduling jitter, causing the RuntimeError checksum errors caught in our try/except block. To simplify the build and eliminate these errors, swap the DHT22 for a BME280 I2C sensor. I2C is handled by a dedicated hardware controller on the RP1, freeing the CPU and guaranteeing reliable reads. You will need to enable I2C via sudo raspi-config and use the adafruit-circuitpython-bme280 library.
Extend: Add MQTT Telemetry
To turn this bench project into a deployable environmental monitor, integrate the paho-mqtt library. Inside the while True loop, publish the temp_c and duty variables to a local Mosquitto broker topic like homeassistant/sensor/pi5_thermal/state. This allows you to graph the thermal throttling behavior of your Pi 5 over time in Home Assistant or Grafana, providing deep insights into your enclosure's airflow dynamics.
For authoritative hardware specifications and GPIO mapping updates, always refer to the official Raspberry Pi Compute and SoC documentation. For PWM fan electrical characteristics, consult the Noctua NF-A4x10 5V PWM datasheet.






