If you are connecting a Raspberry Pi to WiFi on Raspberry Pi OS Bookworm (or newer), the legacy wpa_supplicant.conf method is dead. Modern Pi OS uses NetworkManager, meaning you must use the nmcli command-line tool or the Raspberry Pi Imager's advanced settings to establish a wireless connection. Failing to adapt to this shift is the number one reason headless Pi builds fail to connect on first boot in 2026.
This guide provides the exact headless setup procedure, a complete Python IoT script that verifies network status before publishing sensor data, and a debugging matrix for the specific nmcli error strings you will encounter on the bench.
Time to Complete: 25 minutes
Target Board Variant: Raspberry Pi 4 Model B (4GB) or Raspberry Pi 5 (Code and OS instructions apply identically to both)
The Modern Shift: NetworkManager vs wpa_supplicant
Historically, Pi users dropped a wpa_supplicant.conf file into the /boot partition to configure headless WiFi. With the transition to Debian Bookworm, Raspberry Pi OS adopted NetworkManager as the default networking stack. The /boot/wpa_supplicant.conf trick no longer works. You must now either pre-configure the network via the official imager or use nmcli over SSH/serial console.
Hardware Spec Sheet & Pin Mapping
To demonstrate a real-world IoT use case, we will connect the Pi to WiFi and read telemetry from a Bosch BME280 environmental sensor over I2C. This justifies the network connection and gives us data to transmit.
| Component | Exact Variant / Spec | Notes |
|---|---|---|
| Microcomputer | Raspberry Pi 4 Model B (4GB) | Dual-band 802.11ac WiFi, BLE 5.0 |
| Sensor | Adafruit BME280 Breakout (PID 2652) | I2C address 0x77 (default) or 0x76 |
| Power Supply | Official 27W USB-C PD Supply | Crucial for preventing WiFi brownouts |
| OS | Raspberry Pi OS Lite (64-bit, Bookworm) | Headless, NetworkManager enabled |
BME280 to Raspberry Pi GPIO Pin Mapping
| BME280 Pin | Pi Physical Pin | Pi GPIO / Function |
|---|---|---|
| VIN | 1 | 3.3V Power |
| GND | 6 | Ground |
| SCK (SCL) | 5 | GPIO 3 (SCL1) |
| SDI (SDA) | 3 | GPIO 2 (SDA1) |
Headless WiFi Setup: First Boot Configuration
The most reliable way to handle connecting Raspberry Pi to WiFi without a monitor attached is using the Raspberry Pi Imager before flashing the SD card.
- Open Raspberry Pi Imager and select your target OS (Raspberry Pi OS Lite 64-bit).
- Click the Gear Icon (Advanced Options) or press
Ctrl+Shift+X. - Check "Configure Wireless LAN". Enter your exact SSID and password. Note: SSIDs are case-sensitive and must match exactly.
- Set the Wireless LAN Country. This is mandatory. If omitted, the 5GHz radio will remain disabled due to regulatory DFS (Dynamic Frequency Selection) restrictions until the country code is set.
- Enable SSH (Use password authentication or allow-list your public key).
- Flash the SD card, insert it into the Pi, and power it on. Wait 60 seconds for the first-boot partition resize and network handshake.
sudo nmcli dev wifi connect "YourSSID" password "YourPassword"
Python IoT Build: WiFi Status & Sensor Telemetry
This script targets the Raspberry Pi 4B/5 running Bookworm. It uses subprocess to query nmcli for WiFi status, reads the BME280 sensor via I2C, and posts the payload to a local HTTP endpoint.
Prerequisites: Run pip install smbus2 RPi.bme280 requests and ensure I2C is enabled via sudo raspi-config.
import subprocess
import sys
import time
import json
import requests
from smbus2 import SMBus
from bme280 import BME280
# --- PIN & CONFIG DEFINITIONS ---
# I2C Pins (Physical Board Pinout)
# Pin 1: 3.3V Power
# Pin 3: GPIO 2 (SDA1)
# Pin 5: GPIO 3 (SCL1)
# Pin 6: Ground
I2C_BUS_ID = 1
BME280_I2C_ADDR = 0x76 # Adafruit breakout default; change to 0x77 if needed
WIFI_SSID = "YourNetworkSSID"
TELEMETRY_URL = "http://192.168.1.100:8080/api/sensor"
def verify_wifi_connection():
"""Checks NetworkManager for active WiFi connection."""
try:
result = subprocess.run(
["nmcli", "-t", "-f", "ACTIVE,SSID", "dev", "wifi"],
capture_output=True, text=True, check=True
)
if f"yes:{WIFI_SSID}" in result.stdout:
print(f"[OK] Connected to {WIFI_SSID}")
return True
else:
print(f"[WARN] Connected to a different network or offline.")
return False
except subprocess.CalledProcessError as e:
print(f"[ERROR] nmcli failed: {e.stderr.strip()}")
return False
def init_sensor():
"""Initializes BME280 over I2C."""
try:
bus = SMBus(I2C_BUS_ID)
sensor = BME280(i2c_dev=bus, i2c_addr=BME280_I2C_ADDR)
# Dummy read to initialize the sensor hardware
sensor.get_temperature()
print("[OK] BME280 initialized.")
return sensor
except Exception as e:
print(f"[FATAL] Sensor I2C failure: {e}")
sys.exit(1)
def main():
if not verify_wifi_connection():
print("[HALT] WiFi not connected to target SSID. Aborting telemetry.")
sys.exit(1)
sensor = init_sensor()
print("[INFO] Starting telemetry loop...")
while True:
try:
payload = {
"device": "pi4-env-node-01",
"temp_c": round(sensor.get_temperature(), 2),
"humidity": round(sensor.get_humidity(), 2),
"pressure_hpa": round(sensor.get_pressure(), 2)
}
response = requests.post(TELEMETRY_URL, json=payload, timeout=5)
print(f"[TX] {json.dumps(payload)} | Status: {response.status_code}")
except requests.exceptions.RequestException as e:
print(f"[ERROR] Network transmission failed: {e}")
except Exception as e:
print(f"[ERROR] Sensor read failure: {e}")
time.sleep(60)
if __name__ == "__main__":
main()
Debugging Connection Failures: Exact Errors & Fixes
When connecting Raspberry Pi to WiFi fails, the first three things to check are: 1) Is the wlan0 interface soft-blocked by rfkill? 2) Is the router using a 5GHz DFS channel (channels 52-144) that the Pi cannot see until radar clearance? 3) Is the power supply sagging under the WiFi radio's transmit spike?
If those pass, you will likely hit one of these specific nmcli errors:
Error: "Connection activation failed: (5) IP config not available."
- Cause 1 (Most Likely): DHCP timeout. The Pi associated with the access point, but the router refused to hand out an IP address.
- Cause 2: Router MAC address filtering is enabled and the Pi's wlan0 MAC is not allow-listed.
- Fix: Check your router's DHCP lease pool. Alternatively, assign a static IP via nmcli:
sudo nmcli con mod "YourSSID" ipv4.addresses 192.168.1.50/24 ipv4.gateway 192.168.1.1 ipv4.dns "1.1.1.1,8.8.8.8" ipv4.method manual
Error: "No network with SSID 'YourSSID' found."
- Cause 1 (Most Likely): The SSID is broadcasting on a 5GHz DFS channel, or it is a hidden SSID.
- Cause 2: Typo in the SSID string (case-sensitive).
- Fix: Log into your router and change the 5GHz control channel to a non-DFS channel (36, 40, 44, or 48). If the network is hidden, you must explicitly tell nmcli to scan for hidden networks:
sudo nmcli dev wifi connect "YourSSID" password "YourPass" hidden yes
Error: "wlan0: Failed to initialize driver 'nl80211'"
- Cause: The kernel module for the WiFi chip (usually
brcmfmac) failed to load, often due to missing firmware files in the/lib/firmware/brcmdirectory, or severe USB 3.0 interference masking the internal SDIO bus. - Fix: Run
dmesg | grep brcmfmacto check for firmware load errors. If using a Pi 4, ensure no unshielded USB 3.0 drives are plugged in directly next to the board, as USB 3.0 data lines emit RF noise that perfectly overlaps the 2.4GHz WiFi spectrum, killing the radio.
Extending and Simplifying the Build
To Simplify: If you do not need sensor telemetry and just want the Pi online for SSH, strip the Python script entirely. Rely solely on the Raspberry Pi Imager's pre-configuration. You can verify headless success by pinging raspberrypi.local from your main PC.
To Extend: Add a watchdog timer. NetworkManager can occasionally drop connections on marginal WiFi signals. You can extend the Python script to ping the gateway every 5 minutes; if the ping fails three times consecutively, use subprocess to trigger sudo nmcli networking off && sudo nmcli networking on to reset the radio stack without a full reboot.
Frequently Asked Questions
How do I connect Raspberry Pi to WiFi without a monitor or keyboard?
The most robust method is using the Raspberry Pi Imager on your desktop PC. Before clicking "Write", click the gear icon (Advanced Options) to inject your SSID, password, and country code directly into the OS image. If you have already flashed the OS and have physical access to the SD card, you can no longer use the old wpa_supplicant.conf trick on Bookworm. Instead, you must boot the Pi, connect via Ethernet temporarily, and use nmcli over SSH, or use a serial console cable (UART) to access the CLI and run the nmcli connection command.
Why is my Raspberry Pi WiFi dropping connection intermittently?
Intermittent drops on the Pi 4 and Pi 5 are almost always caused by one of two hardware issues. First, power supply ripple. The WiFi radio draws sudden current spikes during transmission. If your USB-C power supply cannot maintain 5.0V under transient loads, the SoC will brownout the radio to save itself. Use the official Raspberry Pi 27W PD supply. Second, USB 3.0 RF interference. Unshielded USB 3.0 peripherals emit broadband noise that overlaps 2.4GHz WiFi. If you have an external SSD plugged in, move it to a short USB extension cable away from the Pi board, or force the Pi onto the 5GHz band.
How can I force my Raspberry Pi to connect to 5GHz WiFi instead of 2.4GHz?
If your router uses a unified SSID for both bands, the Pi will often default to 2.4GHz due to its stronger signal penetration. To force the 5GHz band via NetworkManager, modify the connection profile to restrict the band. Run:
sudo nmcli con mod "YourSSID" wifi.band a
Then restart the connection:
sudo nmcli con up "YourSSID"
Note: "a" denotes the 5GHz band in networking terminology, while "bg" denotes 2.4GHz. Ensure your router's 5GHz band is set to a non-DFS channel (36-48) or the Pi will refuse to connect.






