From Blog Server to WiFi Radar: Vibecoding a Network Scanner on ESP32
In the previous post I vibecoded a blog server on an ESP32 in one afternoon. It worked, it was fun, and it left me wondering: what else can this $4 chip do?
The ESP32 has a WiFi radio. That radio can do more than connect to a network. It can listen to everything around it.
What I Built
A WiFi and Bluetooth radar running entirely on the ESP32, with a web dashboard served from the device itself:
- WiFi AP Scanner: discover nearby access points with signal strength, channel, encryption type, and vendor identification
- BLE Device Scanner: find Bluetooth Low Energy devices in range
- Probe Request Sniffer: capture which networks nearby devices are searching for
- Spectrum Analyzer: per-channel frame counts, byte totals, and signal levels across all 13 WiFi channels
- Deauth Detector: detect deauthentication attacks on the network
- Live Passive Monitoring: sniff traffic on the connected channel without dropping WiFi
All of it accessible through a browser on your phone or laptop, served from the ESP32 on your local network.
Why This Got Interesting Fast
Probe requests are the part that surprised me. Every WiFi device constantly broadcasts the names of networks it has connected to before. Your phone, your laptop, your smart watch. They’re all yelling “Hey, is HomeWiFi here? Is CoffeeShop_Free here? Is Hotel_Marriott_5G here?” to anyone listening.
This is not encrypted. It’s part of the WiFi standard. Any device in range can hear it.
I pointed the radar at my own network and within seconds I could see which devices were probing for which SSIDs, how often, on which channels, and from which manufacturers (via MAC address OUI lookup). It’s a sobering reminder of how much metadata leaks from the devices we carry.
The Hybrid Architecture
The blog server was pure MicroPython. For the radar, that wasn’t enough. WiFi promiscuous mode requires hooking into ESP-IDF at the C level. So the project became a hybrid:
Browser (JS) → MicroPython (web server + APIs) → C module (probe_sniffer)
↓
ESP-IDF promiscuous mode
(ISR context, zero allocations)
The C module (probe_sniffer.c) runs in interrupt context. It captures raw WiFi frames, parses probe requests for SSIDs, detects deauth frames, and tracks per-channel statistics. No memory allocation, no blocking. It writes to fixed-size circular buffers that MicroPython reads from.
MicroPython runs the web server, orchestrates scans, caches results, and handles the WiFi/BLE APIs. Easy to modify without recompiling.
The browser accumulates probe data across multiple scans, renders an SSID word cloud, and handles live polling.
The Radio Sharing Problem
The ESP32 has one radio. WiFi and BLE share it. This creates a real constraint:
- BLE scanning temporarily disconnects WiFi (~5 seconds to reconnect)
- Full spectrum sweep (all 13 channels) drops WiFi for ~8 seconds
- Passive monitoring on the connected channel works without disconnecting
That last one was the clever workaround. Instead of hopping channels (which requires disconnecting), the sniffer listens only on the channel the ESP32 is already connected to. You see less traffic but you never lose your dashboard connection.
Building the Firmware
Pure MicroPython firmware doesn’t include custom C modules. I needed to compile MicroPython with the probe_sniffer module baked in. The build uses Docker for reproducibility:
./build.sh # Docker builds ESP-IDF v5.5.1 + MicroPython v1.28.0 + probe_sniffer
The Dockerfile pins ESP-IDF and MicroPython to specific versions, compiles the C module into the firmware, and outputs flashable binaries. One command, reproducible on any machine.
The Vibecoding Journey
Like the blog server, this was built through directing Claude Code. But the complexity was different. The blog server was 37 lines of Python. The radar involves C code running in ISR context, ESP-IDF APIs, Docker builds, and radio hardware constraints.
What worked well: Claude handled the MicroPython web server, the API design, the OUI lookup table, the JavaScript dashboard, and the Docker build configuration. The iterative cycle of “add this feature, test it, adjust” stayed fast.
Where it got harder: the C module required understanding interrupt safety, ESP-IDF’s promiscuous mode callback, and WiFi frame parsing. Claude wrote the initial version but debugging ISR-related crashes needed back-and-forth. This is where having an EE background helped. I could look at the frame parsing logic and spot issues that would be hard to describe in a prompt.
This sits firmly at L2-L3 on the adoption pyramid. I brought the systems knowledge, Claude handled the implementation, and the result is something I couldn’t have built this fast on my own.
Security Perspective
I work in cloud security professionally. Building an offensive tool on a weekend project is a useful exercise because it shows exactly what an attacker with $4 and an afternoon can see:
- Every SSID your devices probe for (your travel history in WiFi names)
- Which devices are near you and who made them
- Whether someone is running a deauth attack on your network
- Channel congestion and interference patterns
The legal side matters: passive WiFi monitoring laws vary by country. In Germany, capturing frames you’re not the intended recipient of is a grey area. I only used this on my own network. The tool includes a legal disclaimer, and I’d encourage anyone building something similar to check their local regulations.
The defensive takeaway: disable probe requests on your devices where possible, use randomized MAC addresses (most modern phones do this by default), and be aware that your WiFi client broadcasts information about you constantly.
What’s Next
The ESP32 started as a toy blog server and turned into a legitimate network analysis tool in two sessions. The progression from serving static HTML to capturing WiFi frames in ISR context mirrors the L0-to-L3 journey in miniature: each step required trusting the tools with more.
The probe sniffer data is currently viewed in the browser and discarded. The next step would be logging it over time and looking for patterns. That’s where the eBPF monitoring approach I’ve been exploring for CI/CD could be interesting in a different context: behavioral baselines for your local network, anomaly detection when new devices appear, alerts when deauth frames spike.
But that’s a project for another weekend.
The views and opinions expressed here are my own and do not reflect those of my employer.