If you are trying to set up WiFi on Raspberry Pi boards running modern OS releases (Bookworm or newer), the first thing you need to know is that the legacy wpa_supplicant.conf method is dead. Raspberry Pi OS has transitioned entirely to NetworkManager. Dropping a text file into the boot partition will no longer configure your wireless interface, and relying on outdated tutorials will leave you with a disconnected board.
This guide targets the Raspberry Pi 5 (8GB variant) running Raspberry Pi OS Bookworm (64-bit). We will cover the exact nmcli terminal commands to establish a connection, map a GPIO pin to monitor link status via Python, and debug the specific error strings NetworkManager throws when a connection fails.
Raspberry Pi WiFi Hardware & OS Compatibility Matrix
Before typing a single command, verify your board's physical capabilities and OS backend. The shift to NetworkManager fundamentally changed how the OS interacts with the Cypress/Infineon wireless chipsets. Below is the hardware and software compatibility matrix for current Pi models.
| Board Variant | WiFi Chipset | Bands | NetworkManager (Bookworm+) | Legacy wpa_supplicant |
|---|---|---|---|---|
| Raspberry Pi 5 (8GB/4GB) | Cypress CYW43455 / Infineon | 2.4 GHz / 5 GHz | Full Support (Default Backend) | Deprecated / Fails to bind |
| Raspberry Pi 4 Model B | Cypress CYW43455 | 2.4 GHz / 5 GHz | Full Support (Default Backend) | Deprecated / Fails to bind |
| Raspberry Pi Zero 2 W | Cypress CYW43436 | 2.4 GHz only | Full Support (Default Backend) | Deprecated / Fails to bind |
| Raspberry Pi 3B+ | Cypress CYW43455 | 2.4 GHz / 5 GHz | Supported (if OS upgraded) | Supported (on Buster/Legacy) |
Note: If you attempt to force the legacy daemon by running sudo systemctl enable wpa_supplicant on Bookworm, NetworkManager will fight it for control of the wlan0 interface, resulting in intermittent drops and IP assignment failures.
Parts List & GPIO Pin Mapping
To make this a complete embedded build rather than just a terminal exercise, we are adding a physical WiFi status indicator. This is critical for headless deployments where you cannot see the desktop network icon.
Required Components
- MCU: Raspberry Pi 5 (8GB)
- OS: Raspberry Pi OS Bookworm (64-bit, Lite or Desktop)
- Storage: 32GB+ MicroSD (Class 10 / A2 rated)
- Indicator: 5mm Green LED + 330Ω through-hole resistor
- Wiring: 2x Dupont jumper wires (Female-to-Male)
Pin Mapping Table
We will use gpiozero in Python to drive the LED. The Pi 5's RP1 southbridge handles GPIO routing, but the pin numbers remain backward compatible with the 40-pin header standard.
| Component | Pi 5 Physical Pin | BCM GPIO Number | Function |
|---|---|---|---|
| 330Ω Resistor (Anode side) | Pin 11 | GPIO 17 | Digital Output (LED Control) |
| LED Cathode (Short leg) | Pin 9 | GND | Ground Reference |
Step-by-Step CLI Setup via nmcli
NetworkManager's command-line tool is nmcli. It is verbose but highly reliable. Follow these steps to configure and save your WiFi profile.
- Scan for available networks:
nmcli device wifi list
Look for your SSID in the output. Note the exact spelling and whether it is on the 2.4GHz or 5GHz band. - Connect and save the profile:
nmcli device wifi connect "YourSSID" password "YourPassword" name "HomeWiFi_Profile"
Thenameargument creates a persistent profile. If you omit it, NetworkManager names it after the SSID. - Verify the connection state:
nmcli connection show --active
Ensurewlan0is listed and the state isactivated. - Set the connection to auto-start on boot:
nmcli connection modify "HomeWiFi_Profile" connection.autoconnect yes
For deeper technical reference on NetworkManager's architecture and command syntax, consult the official NetworkManager nmcli documentation.
Python WiFi Status Monitor (GPIO 17)
Below is the complete, compilable Python script. It polls the NetworkManager state via nmcli and updates the external LED. This script is designed to run as a background service on headless Pi 5 deployments.
import subprocess
import time
import sys
from gpiozero import LED
# --- PIN DEFINITIONS ---
# BCM GPIO 17 (Physical Pin 11)
WIFI_STATUS_LED = LED(17)
TARGET_SSID = "HomeWiFi_Profile"
def get_nmcli_state():
"""
Queries NetworkManager for the active connection state on wlan0.
Returns a tuple: (is_connected: bool, active_ssid: str)
"""
try:
# -t = terse (machine readable), -f = fields
cmd = ["nmcli", "-t", "-f", "GENERAL.STATE,GENERAL.CONNECTION", "device", "show", "wlan0"]
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
output = result.stdout.strip()
state_line = ""
ssid_line = ""
for line in output.split('\n'):
if line.startswith("GENERAL.STATE"):
state_line = line
elif line.startswith("GENERAL.CONNECTION"):
ssid_line = line
# State 100 means fully connected in NetworkManager
is_connected = "100 (connected)" in state_line
active_ssid = ssid_line.split(":")[1] if ssid_line else "None"
return is_connected, active_ssid
except subprocess.CalledProcessError as e:
print(f"[ERROR] nmcli failed: {e.stderr}")
return False, "Error"
except Exception as e:
print(f"[ERROR] Unexpected exception: {e}")
return False, "Error"
def main():
print(f"Starting WiFi Monitor. Target Profile: {TARGET_SSID}")
print("Press Ctrl+C to exit.")
try:
while True:
connected, current_ssid = get_nmcli_state()
if connected and current_ssid == TARGET_SSID:
WIFI_STATUS_LED.on()
else:
# Blink rapidly if disconnected or on wrong network
WIFI_STATUS_LED.blink(on_time=0.2, off_time=0.2, background=False)
# Note: background=False blocks, so we skip the sleep below if blinking
continue
time.sleep(5) # Poll every 5 seconds when connected
except KeyboardInterrupt:
print("\nShutting down monitor...")
WIFI_STATUS_LED.off()
sys.exit(0)
if __name__ == "__main__":
main()
Dependency note: Ensure gpiozero is installed via sudo apt install python3-gpiozero. The script uses standard library subprocess to avoid relying on complex D-Bus Python bindings which can break across OS updates.
Debugging: Exact Error Strings & Ranked Causes
When setting up WiFi on embedded Linux, the terminal output is your only diagnostic tool. Here are the exact error strings NetworkManager generates, what they actually mean, and how to fix them.
Error 1: Secrets Required
Error: Connection activation failed: (7) Secrets were required, but not provided.
Ranked Causes:
- Incorrect Password: The most common cause. Linux terminals do not mask input when pasting into some SSH clients, leading to trailing spaces. Retype manually.
- WPA3 Transition Mode Mismatch: If your router uses WPA2/WPA3 transition mode, the Pi's
wpa_supplicantbackend (which NetworkManager still uses under the hood for the actual handshake) may fail to negotiate the SAE (WPA3) handshake. Fix: Force WPA2-PSK on the router, or update the Pi's firmware viasudo rpi-eeprom-update. - Hidden SSID: If the network is hidden, you must explicitly tell NetworkManager to scan for it:
nmcli connection modify "ProfileName" wifi-sec.hidden yes.
Error 2: Network Not Found
Error: No network with SSID 'YourSSID' found.
Ranked Causes:
- 5GHz Regulatory Domain Blocking: The Pi's WiFi chip restricts certain 5GHz channels (like DFS channels 52-144) until it hears a beacon from a local router confirming the country code. If your router is on a DFS channel, the Pi literally cannot see it. Fix: Set your router to a non-DFS 5GHz channel (36, 40, 44, 48) or force the Pi's regulatory domain by adding
wifi_country=US(or your local code) to/boot/firmware/config.txt. - Out of Range / Antenna Issue: The Pi 5 uses a PCB trace antenna. If the board is inside a metal enclosure, the signal will be attenuated by 20-30dB. Move the board or use a USB WiFi dongle with an external SMA antenna.
The First Three Things to Check When It Fails
If your connection drops or refuses to initialize, run these three checks in order before rewriting your code:
- Check Interface State: Run
nmcli device status. Ifwlan0saysunmanaged, NetworkManager is blocked by a configuration file. Check/etc/NetworkManager/NetworkManager.confforunmanaged-devicesentries. - Check IP Assignment: Run
ip -4 addr show wlan0. If you see169.254.x.x, you are connected to the AP but DHCP is failing. Check your router's DHCP pool exhaustion or lease times. - Check RF Kill: Run
rfkill list. If WiFi showsSoft blocked: yes, runsudo rfkill unblock wifi. This often happens if the OS detects a missing keyboard/mouse on boot and enters a low-power radio state.
For comprehensive details on Raspberry Pi specific network configuration and headless setups, refer to the official Raspberry Pi NetworkManager documentation.
How to Simplify or Extend the Build
Simplify: Headless Setup via custom.toml
If you are deploying multiple Pi 5 boards and do not want to plug in a monitor to run nmcli, you can pre-configure WiFi before the first boot. In Bookworm, the wpa_supplicant.conf drop-in is replaced by a custom.toml file.
- Flash the OS using Raspberry Pi Imager (which handles this via the GUI), OR
- Mount the FAT32 boot partition on your PC and create a file named
custom.tomlin the root directory. - Add the following syntax:
[wlan0] ssid = "YourSSID" password = "YourPassword" hidden = false - Boot the Pi. The first-boot script will parse the TOML file, generate the NetworkManager profile, and delete the TOML file for security.
Extend: MQTT Integration for Fleet Monitoring
The Python script provided above only drives a local LED. To scale this to a fleet of embedded devices, extend the get_nmcli_state() function to publish the connection status to an MQTT broker. By adding the paho-mqtt library, you can push the active_ssid, RSSI signal strength (parsed from nmcli device wifi list), and IP address to a Home Assistant or Node-RED dashboard. This transforms a simple connectivity script into a robust remote telemetry node.






