Verified on real hardware: the Raspberry Pi 5’s built-in WiFi (brcmfmac) can run a full-band iw dev wlan0 scan while wlan0 is hosting an AP/hotspot — so a second USB WiFi dongle is NOT required for a combined hotspot + scanner setup.

Bottom line for wifi-hunter

One radio does both jobs. Host the hotspot on wlan0 and scan on the same wlan0 in AP mode. The only observed cost is a brief beacon stutter during each scan sweep. No dongle, no monitor-mode interface, no channel juggling.

What Was Tested

  • Hardware: Raspberry Pi 5, model 2712, kernel 6.18, aarch64, over ssh raspi.
  • Driver: brcmfmac (built-in Broadcom WiFi).
  • Hotspot brought up with:
    sudo nmcli device wifi hotspot ifname wlan0 ssid hunter-ap password huntme123
  • Then, with the AP live, ran:
    sudo iw dev wlan0 scan

Result

  • The scan succeeded with no error while in AP mode.
  • It returned the full band — both 2.4 GHz (e.g. 2412) and 5 GHz (e.g. 5180) networks were seen.
  • The result set was identical to managed mode — no channel lock, no band restriction.

Why This Matters

Contradicts common assumption

The widely-repeated assumption is that scanning is impossible, errors out, or is channel-locked to the AP’s operating channel while an interface hosts an AP. On this Pi 5 / brcmfmac / kernel 6.18 combo, that is not the case — the firmware performs off-channel scanning transparently. This is why the combined single-radio hotspot+scanner design in wifi-hunter works without extra hardware.

The tradeoff is only the brief beacon stutter during each sweep (clients on the AP may see a momentary hiccup while the radio hops channels to scan), which is acceptable for a hunting tool where you scan every few seconds.

Gotcha: Scans Can HANG in AP Mode and Wedge the Server (field test 2026-07-15)

Single-radio scanning has a failure mode: indefinite hang

Scanning works, but on the same radio that hosts the AP, iw dev wlan0 scan can occasionally hang indefinitely — not just return the usual Device or resource busy. Without a subprocess timeout this hangs the whole server.

Symptoms

  • Phone shows no networks after an initial burst.
  • journalctl -u wifi-hunter shows a handful of GET /scan 200 then zero further /scan completions — requests stop finishing entirely.

Root Cause

server.py serializes scans under a threading.Lock. A hung iw scan with no subprocess timeout holds the lock forever, so every subsequent /scan request blocks behind it (the phone polls every 2 s). The whole endpoint wedges.

Fix

  • Added SCAN_TIMEOUT = 8 and wrapped the scan in subprocess.run(..., timeout=SCAN_TIMEOUT), catching subprocess.TimeoutExpired → return None. A hung scan now times out, releases the lock, and run_scan falls back to iw dev wlan0 scan dump (kernel cache) or the last-good result.
  • Verified: 10 rapid /scan hits all returned 200 with real JSON, no hangs.

To stop overlapping polls from colliding into Device or resource busy:

  • Serialize scans under the lock (one live scan at a time).
  • Debounce with MIN_SCAN_INTERVAL = 2.0 s between real scans.
  • Fall back to iw dev wlan0 scan dump (cache) when a fresh scan can’t run.
  • Keep a last-good cache to always return something usable.

For Agents

Distinguish the two scan failure modes: Device or resource busy = overlapping/too-frequent scans (fixed by serialize + debounce + scan dump fallback), vs an indefinite hang = no completion at all (fixed by subprocess timeout so the lock releases). Both are handled in server.py.