The Raspberry Pi 5 features a robust dual-band 2.4/5GHz WiFi radio, but headless embedded deployments still suffer from silent dropouts, IP conflicts, and MQTT disconnects. When building an always-on IoT node, the internal WiFi chip is only as reliable as the power delivery and network management stack driving it. This guide targets the Raspberry Pi 5 (4GB) running Raspberry Pi OS Bookworm, leveraging the newer NetworkManager stack and Paho MQTT v2.0 to build a self-healing environmental monitor.
nmcli connection show, (2) power supply undervoltage throttling via dmesg | grep -i undervoltage (the WiFi radio drops first when the 5V rail sags), and (3) 2.4GHz channel congestion using nmcli dev wifi list.
Hardware Spec Sheet and GPIO Pin Mapping
Before writing code, we need to define the physical layer. Because the Pi's internal WiFi antenna lacks user-accessible diagnostic pins, we map external GPIO components to provide physical network status feedback and a manual reset trigger.
Parts List
- Compute: Raspberry Pi 5 (4GB variant) with official 27W USB-C PD power supply (crucial for preventing WiFi brownouts).
- Sensor: Adafruit BME280 I2C Temperature, Humidity, and Pressure breakout.
- Indicators: 5mm Green LED (Network OK), 5mm Red LED (Fault/Reconnecting).
- Controls: 6x6mm tactile pushbutton for manual WiFi/MQTT reset.
- Passives: Two 330Ω resistors (LED current limiting), one 10kΩ resistor (button pull-up, though internal pull-ups are used in code).
GPIO Pin Mapping Table
| Component | Pi 5 GPIO (BCM) | Physical Pin | Function / Notes |
|---|---|---|---|
| BME280 SDA | GPIO 2 | 3 | I2C Data (Pin 1 on sensor) |
| BME280 SCL | GPIO 3 | 5 | I2C Clock (Pin 2 on sensor) |
| Green LED Anode | GPIO 17 | 11 | MQTT Connected Indicator |
| Red LED Anode | GPIO 27 | 13 | WiFi/MQTT Fault Indicator |
| Reset Button | GPIO 22 | 15 | Pulled HIGH, grounds on press |
Headless WiFi Configuration via NetworkManager
Raspberry Pi OS Bookworm deprecated wpa_supplicant and dhcpcd in favor of NetworkManager. If you are copying old wpa_supplicant.conf files to the boot partition, they will be ignored. You must use the nmcli command line tool or the raspi-config TUI.
- Verify the interface: Run
nmcli device status. Ensurewlan0shows asconnectedordisconnected(notunmanaged). - Create a persistent profile:
sudo nmcli connection add type wifi ifname wlan0 con-name 'IoT-Network' ssid 'YourSSID' wifi-sec.key-mgmt wpa-psk wifi-sec.psk 'YourPassword' - Force IPv4 auto-connect priority:
sudo nmcli connection modify 'IoT-Network' connection.autoconnect yes connection.autoconnect-priority 10 ipv4.method auto - Disable WiFi Power Management: The Pi's WiFi chip aggressively sleeps to save power, causing MQTT timeouts. Disable it by creating a NetworkManager dispatcher script or modifying the
iwpower save state. A quick persistent fix is addingoptions brcmfmac roamoff=1 feature_disable=0x82000to/boot/firmware/cmdline.txtor disabling power save viasudo iw dev wlan0 set power_save offin anrc.localscript.
Resilient MQTT Python Script with Error Handling
This script targets Python 3.11+ on Bookworm. It uses gpiozero for hardware abstraction, smbus2 and bme280 for I2C sensor reading, and paho-mqtt v2.0 for telemetry. It includes automatic reconnection logic and a physical button interrupt to force a network stack reset.
import time
import signal
import sys
import paho.mqtt.client as mqtt
from gpiozero import LED, Button
from bme280 import BME280
from smbus2 import SMBus
# --- PIN DEFINITIONS ---
PIN_LED_GREEN = 17 # MQTT Connected
PIN_LED_RED = 27 # Fault / Reconnecting
PIN_BUTTON = 22 # Manual Reset
# --- MQTT CONFIG ---
MQTT_BROKER = '192.168.1.50'
MQTT_PORT = 1883
MQTT_TOPIC = 'pi5/bme280/telemetry'
# Initialize Hardware
green_led = LED(PIN_LED_GREEN)
red_led = LED(PIN_LED_RED)
reset_btn = Button(PIN_BUTTON, pull_up=True, bounce_time=0.2)
# Initialize I2C Sensor
bus = SMBus(1)
bme280 = BME280(i2c_dev=bus)
def on_connect(client, userdata, flags, reason_code, properties):
"""Paho MQTT v2.0 callback signature."""
if reason_code == 0:
print('MQTT Connected successfully.')
green_led.on()
red_led.off()
else:
print(f'MQTT Connection failed with code: {reason_code}')
green_led.off()
red_led.blink(on_time=0.5, off_time=0.5)
def on_disconnect(client, userdata, flags, reason_code, properties):
print(f'MQTT Disconnected (Code: {reason_code}). Attempting auto-reconnect...')
green_led.off()
red_led.on()
def force_network_reset():
print('Button pressed: Forcing WiFi and MQTT reset...')
red_led.blink(on_time=0.1, off_time=0.1)
# Trigger OS-level WiFi reset via nmcli
import os
os.system('sudo nmcli device disconnect wlan0')
time.sleep(2)
os.system('sudo nmcli device connect wlan0')
time.sleep(5)
client.reconnect()
reset_btn.when_pressed = force_network_reset
# Setup MQTT Client (Paho v2.0 API)
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id='Pi5_Sensor_01')
client.on_connect = on_connect
client.on_disconnect = on_disconnect
client.username_pw_set('mqtt_user', 'mqtt_pass') # Optional
client.connect_async(MQTT_BROKER, MQTT_PORT, keepalive=60)
client.loop_start()
def graceful_exit(sig, frame):
print('Shutting down gracefully...')
client.loop_stop()
client.disconnect()
green_led.off()
red_led.off()
sys.exit(0)
signal.signal(signal.SIGINT, graceful_exit)
signal.signal(signal.SIGTERM, graceful_exit)
try:
while True:
temp_c = bme280.temperature
humidity = bme280.humidity
pressure = bme280.pressure
payload = f'{{"temp":{temp_c:.2f},"hum":{humidity:.2f},"pres":{pressure:.2f}}}'
if client.is_connected():
client.publish(MQTT_TOPIC, payload, qos=1)
else:
print('MQTT offline. Payload buffered locally (simulated).')
time.sleep(15)
except Exception as e:
print(f'Fatal sensor or loop error: {e}')
red_led.on()
client.loop_stop()
Debugging: Ranked Causes for Common WiFi and MQTT Errors
When the red LED stays solid or the script crashes, check these exact error strings in your journalctl -u your_service logs.
1. 'wlan0: link is not ready' or 'No suitable access point found'
- Cause A (Most Likely): 5GHz DFS (Dynamic Frequency Selection) channel routing. The Pi's WiFi chip must pause to listen for radar signals on certain 5GHz channels, causing dropouts. Fix: Log into your router and force the 5GHz band to a non-DFS channel (e.g., 36, 40, 44, 48).
- Cause B: Power supply undervoltage. The Pi 5 requires a 5V/5A PD supply. If you use a standard phone charger, the voltage sags when the WiFi radio transmits, resetting the SDIO bus. Fix: Use the official 27W Pi 5 power supply.
2. 'ValueError: Callback API version 2 is required'
- Cause: You upgraded to Paho MQTT v2.0 via
pip, but your code uses the legacy v1 callback signaturedef on_connect(client, userdata, flags, rc):. Paho v2 addedreason_codeandpropertiesto the signature. - Fix: Update your callback definitions to match the v2 signature (as shown in the code block above) and ensure you pass
mqtt.CallbackAPIVersion.VERSION2when instantiating the client. See the official Paho migration guide.
3. 'ConnectionRefusedError: [Errno 111] Connection refused'
- Cause A: The MQTT broker (e.g., Mosquitto) is rejecting the connection due to ACL (Access Control List) misconfiguration or the broker service crashed.
- Cause B: The Pi's WiFi connected, but it received an APIPA address (169.254.x.x) because the DHCP server timed out. Fix: Assign a static IP via NetworkManager:
sudo nmcli connection modify 'IoT-Network' ipv4.addresses 192.168.1.100/24 ipv4.gateway 192.168.1.1 ipv4.dns '1.1.1.1' ipv4.method manual.
Extending or Simplifying the Build
To Simplify: If you don't need a local MQTT broker and just want to log data to the cloud, strip out the paho-mqtt library and replace the publish block with an HTTPS POST request using the requests library to an endpoint like ThingSpeak or InfluxDB Cloud. Remove the red/green LEDs and rely on a single blinking LED for a heartbeat.
To Extend: Add a Watchdog Timer (WDT). The Pi 5's BCM2712 chip has a hardware watchdog. If the Python script freezes due to a hanging I2C bus (a common BME280 edge case), the hardware WDT will hard-reboot the Pi. Enable it via sudo systemctl enable watchdog and configure /etc/watchdog.conf to ping a local IP or monitor the systemd service running this script.
FAQ: Raspberry Pi and WiFi Long-Tail Questions
Why does my Raspberry Pi and WiFi connection drop only at night?
Nighttime dropouts are almost always caused by 2.4GHz channel congestion or DFS radar checks on 5GHz. At night, neighboring IoT devices, smart TVs, and baby monitors become highly active, saturating the 2.4GHz spectrum. Furthermore, atmospheric conditions at night can cause distant 5GHz radar signals to propagate further, triggering DFS channel evictions on your router, which temporarily disconnects the Pi while it scans for a new channel.
Can I use an external USB WiFi adapter instead of the internal Raspberry Pi and WiFi chip?
Yes, and it is highly recommended for industrial or metal-enclosure deployments. The internal chip uses a PCB trace antenna which suffers severe attenuation inside metal cases. Using a USB adapter like the Panda PAU09 (dual-band, external antenna) or an official Raspberry Pi USB Ethernet/WiFi dongle allows you to route an RP-SMA antenna outside the enclosure. Ensure you blacklist the internal brcmfmac driver if you want to force the OS to use the USB adapter exclusively.
How do I automatically reconnect Raspberry Pi and WiFi after a router reboot?
NetworkManager handles this natively if configured correctly. Ensure connection.autoconnect=yes is set. However, if the router reboots and the Pi's DHCP lease expires before the router comes back online, the Pi might drop to an APIPA address. To fix this, create a cron job or a NetworkManager dispatcher script (/etc/NetworkManager/dispatcher.d/) that pings your gateway every 5 minutes; if the ping fails, the script executes nmcli connection up 'IoT-Network' to force a fresh DHCP handshake.
Does the Raspberry Pi 5 WiFi support WPA3 enterprise?
Yes, the BCM43455 chip and the newer Bookworm NetworkManager stack support WPA3-SAE (Simultaneous Authentication of Equals) and WPA3-Enterprise. However, you must ensure your wpa_supplicant backend (if still invoked by NetworkManager for specific enterprise EAP methods) is updated, and your router's PMF (Protected Management Frames) is set to 'Optional' rather than 'Required', as early Pi WiFi firmware revisions struggle with strict PMF enforcement.






