Turning a Raspberry Pi into a dedicated wireless access point (WAP) used to mean wrestling with hostapd, dnsmasq, and fragile iptables NAT rules. With the shift to Raspberry Pi OS Bookworm and the NetworkManager daemon, those days are over. NetworkManager handles the DHCP server, routing, and AP broadcast natively.
However, headless network appliances still need physical feedback. This guide walks through building a robust wireless access point Raspberry Pi 5 node, complete with a physical GPIO status LED and a hardware reset button to recover the network stack without SSH access.
Project Overview & Hardware Requirements
While the Pi 5 has an internal Infineon CYW43455 Wi-Fi chip, using it for an AP while simultaneously using Bluetooth causes severe coexistence interference. For a reliable WAP, we use an external USB adapter with native in-tree Linux drivers.
| Component | Exact Variant / Spec | Why This Part? |
|---|---|---|
| Microcontroller | Raspberry Pi 5 (8GB) | Handles multiple DHCP leases and Python polling without thermal throttling. |
| Wi-Fi Adapter | Panda Wireless PAU09 (N600) | Uses Ralink RT5572 chipset. Native rt2800usb kernel support; no DKMS compilation required. |
| Power Supply | Official 27W USB-C PD | Pi 5 requires 5V/5A to prevent USB peripheral brownouts. |
| Status LED | 5mm Green LED + 330Ω Resistor | Visual confirmation of AP broadcast status. |
| Reset Switch | 6x6mm Tactile Pushbutton + 10kΩ Pull-up | Hardware trigger to restart the NetworkManager AP connection. |
Hardware Assembly & GPIO Pin Mapping
Wire the status LED and reset button to the Pi 5's 40-pin header. We use hardware pull-up resistors for the button to ensure a clean logic HIGH state, though the Python script also enables the Pi's internal pull-ups as a fallback.
| Component | Component Pin | Pi 5 GPIO / Pin | Notes |
|---|---|---|---|
| LED | Anode (+) | GPIO 17 (Pin 11) | Connect via 330Ω current-limiting resistor. |
| LED | Cathode (-) | GND (Pin 9) | Direct to ground. |
| Pushbutton | Leg 1 | GPIO 27 (Pin 13) | Signal line to Pi. |
| Pushbutton | Leg 2 | GND (Pin 14) | Connect via 10kΩ resistor to 3.3V (Pin 1) for hardware pull-up. |
Configuring the Wireless Access Point Raspberry Pi via NetworkManager
Forget editing /etc/hostapd/hostapd.conf. We will use nmcli to create a shared Wi-Fi connection. This automatically spins up an internal DHCP server for connected clients.
- Identify your USB adapter interface name:
Runiw dev. The internal chip is usuallywlan0. Your Panda USB adapter will likely bewlan1or a predictable name likewlx0013eff41234. We will usewlan1for this example. - Create the base AP connection:
sudo nmcli connection add type wifi ifname wlan1 con-name Pi-AP autoconnect yes ssid "FluxNet-AP" - Configure AP mode, band, and IP sharing:
sudo nmcli connection modify Pi-AP 802-11-wireless.mode ap 802-11-wireless.band bg ipv4.method shared ipv4.addresses 10.42.0.1/24
Note:ipv4.method sharedis the magic flag that replaces dnsmasq and iptables masquerading. - Add WPA2 security:
sudo nmcli connection modify Pi-AP wifi-sec.key-mgmt wpa-psk wifi-sec.psk "BenchTest2026!" - Bring the interface up:
sudo nmcli connection up Pi-AP
Python GPIO Status & Hardware Reset Script
Headless Pis often drop Wi-Fi interfaces due to USB bus resets or RF interference. This Python script monitors the Pi-AP connection state via nmcli, lights the LED when active, and allows you to hold the physical button for 3 seconds to force a network stack restart.
import time
import subprocess
import logging
from gpiozero import LED, Button
from signal import pause
# --- PIN DEFINITIONS ---
STATUS_LED_PIN = 17
RESET_BUTTON_PIN = 27
# Setup logging to systemd journal
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# Initialize GPIO hardware
ap_led = LED(STATUS_LED_PIN)
reset_btn = Button(RESET_BUTTON_PIN, hold_time=3, pull_up=True, bounce_time=0.05)
def check_ap_status():
"""Checks if the Pi-AP NetworkManager connection is currently active."""
try:
result = subprocess.run(
["nmcli", "-t", "-f", "NAME,DEVICE", "connection", "show", "--active"],
capture_output=True, text=True, check=True
)
# Parse terminal output for our specific connection name
if "Pi-AP" in result.stdout:
ap_led.on()
return True
else:
ap_led.off()
return False
except subprocess.CalledProcessError as e:
logging.error(f"nmcli command failed: {e.stderr.strip()}")
ap_led.blink(on_time=0.2, off_time=0.2) # Fast blink indicates CLI error
return False
except Exception as e:
logging.error(f"Unexpected error checking AP state: {e}")
ap_led.off()
return False
def restart_ap_service():
"""Hardware reset: tears down and rebuilds the AP connection via nmcli."""
logging.warning("Hardware reset button held! Restarting Pi-AP interface...")
ap_led.blink(on_time=0.1, off_time=0.1) # Rapid blink during reset
try:
# Force down first to clear hung USB states
subprocess.run(["nmcli", "connection", "down", "Pi-AP"], check=False)
time.sleep(2) # Allow USB bus to settle
# Bring back up
subprocess.run(["nmcli", "connection", "up", "Pi-AP"], check=True)
logging.info("Pi-AP successfully restarted via hardware trigger.")
except subprocess.CalledProcessError as e:
logging.error(f"Failed to restart AP: {e.stderr.strip()}")
ap_led.off()
# Bind hardware interrupt to function
reset_btn.when_held = restart_ap_service
if __name__ == "__main__":
logging.info("Starting AP Hardware Monitor Daemon...")
try:
while True:
check_ap_status()
time.sleep(5) # Poll every 5 seconds to minimize CPU overhead
except KeyboardInterrupt:
logging.info("Monitor stopped by user.")
ap_led.off()
Save this as ap_monitor.py and run it via a systemd service so it survives reboots.
Debugging: Ranked Causes for AP Activation Failures
When running sudo nmcli connection up Pi-AP, the most common failure string is:
Error: Connection activation failed: (2) Device not found
If you see this exact error, or if clients simply cannot connect, follow this ranked troubleshooting path.
The First Three Things to Check
- Interface Name Mismatch: NetworkManager binds to the specific MAC-based predictable name (e.g.,
wlx0013eff41234). If your USB adapter was plugged into a different port, or if you usedwlan1in step 1 but the OS assignedwlan2, NM will look for a ghost device. Runnmcli connection modify Pi-AP 802-11-wireless.interface-name ""to unbind it from a specific MAC, or update it to the correctiw devoutput. - RFKill Soft Block: The Pi's internal Bluetooth/Wi-Fi management can accidentally soft-block external USB radios. Run
rfkill list. If you see "Soft blocked: yes" on your wireless interface, runsudo rfkill unblock wifi. - USB Power Limiting (Brownout): The Pi 5 limits USB current by default to save power. High-draw adapters will fail to initialize the AP radio. Edit
/boot/firmware/config.txtand addusb_max_current_enable=1to unlock the full 1.2A USB budget, then reboot.
Frequently Asked Questions
Can I use the internal Wi-Fi chip for a wireless access point Raspberry Pi build?
You can, but it is not recommended for production or heavy-use environments. The internal CYW43455 chip shares an antenna and internal bus with Bluetooth. When operating in AP mode, the chip's time-division multiplexing heavily degrades Bluetooth audio and peripheral performance. Furthermore, the internal chip struggles to maintain stable beacon intervals under heavy CPU load, leading to random client disconnects. An external USB adapter offloads the RF stack and provides a dedicated antenna.
How do I extend this build to include a captive portal?
To extend this into a guest network with a splash page, keep the NetworkManager AP setup exactly as written above. Then, install Nodogsplash or OpenNDS via the package manager. OpenNDS intercepts HTTP traffic on the 10.42.0.0/24 subnet created by NetworkManager's shared mode and redirects it to a local web server running on the Pi. You will need to open port 80 in the local firewall using sudo firewall-cmd --zone=trusted --add-port=80/tcp.
Why does my AP drop clients when the CPU load spikes?
Wi-Fi beacon frames must be transmitted at strict intervals (typically every 100ms). If the Pi's CPU is pegged at 100% by a heavy compilation or Docker container, the kernel scheduler delays the Wi-Fi driver's interrupt handling. Clients interpret the missed beacons as a dead AP and disassociate. To fix this, you can isolate a CPU core specifically for network interrupts using the isolcpus kernel parameter in cmdline.txt, or simply move the compute-heavy workload to a secondary machine.
How can I simplify the build if I don't need the GPIO hardware monitor?
If you just want a pure software AP without the breadboard hardware, skip the Python script and GPIO wiring entirely. The NetworkManager CLI commands in Section 3 are completely self-sufficient. To ensure the AP survives reboots without the Python watchdog, simply verify that autoconnect yes was included in your initial nmcli connection add command. NetworkManager will automatically bring the AP up on boot as long as the USB adapter is detected.






