Radioscope is a Rust service that turns Wi-Fi traffic into audible cues so you can “hear” the airwaves. It inspects packets from a monitor-mode interface and emits different synthesized sounds for different packet types, letting you distinguish beacons, data bursts, and management chatter without staring at a screen. You can aim a directional antenna to hunt for specific emitters or to map coverage while listening.
A built-in web UI is served over an access point hosted on the PI's built in wifi interface, so you can connect from a phone/laptop without relying on upstream networking. The UI lets you filter by device or packet type, start/stop playback, and adjust settings on the fly. Audio can be played locally through the Pi’s 3.5mm jack, or streamed through the UI for remote monitoring.
Under the hood it uses Dioxus SSR for the UI, Axum for the API, libpcap for capture, and cpal/ALSA for sound. A systemd unit handles startup on Raspberry Pi OS, and helper scripts configure the AP and monitor interface for you.
Sample audio:
Radioscope can watch for Evil Twin behavior by comparing live beacons/associations against a trusted AP roster. When you trust an AP, its SSID, BSSID, RSN profile, vendor OUI, channels, and capability fingerprint are snapshotted; future frames are scored for unknown BSSIDs, fingerprint drift, channel teleporting, downgrades, KARMA behavior, and sudden RSSI spikes. Findings emit a short claxon and are logged to syslog with evidence in the message.
The Evil Twin page shows trusted APs plus anything seen in the current window, with a “Trust” checkbox per entry and a live event feed that lists scores, severities, and concrete reasons (e.g., stored vs seen RSN/cipher suites, channels observed). You can untrust APs here to stop monitoring them. Web UI sound mirrors the backend alert tones when enabled.
- Packet capture via
libpcap(pcapcrate). - Audio tick synthesis via
cpal(ALSA on the Pi). - HTTP API and UI via
axum+ Dioxus SSR. - systemd unit for autostart on Raspberry Pi OS (64-bit, aarch64).
- Raspberry Pi 4 (64-bit) or Pi 5 running Raspberry Pi OS.
- Built-in Wi-Fi on
wlan0for the access point (hostapd + dnsmasq); keep it free from client connections while using the app. - External USB Wi-Fi dongle that supports monitor mode, ideally dual-band 2.4/5 GHz; the setup script will name it
wlan1mon. - Audio output via the 3.5mm jack with speakers or headphones.
- Stable power and microSD sized for normal Pi OS installs.
Environment variables (defaults in parentheses):
MONITOR_INTERFACE(wlan1mon) – monitor-mode Wi-Fi interface.HTTP_BIND(0.0.0.0:8080) – bind address for the UI/API.TICK_FREQUENCY_HZ(880) – tick sine frequency.TICK_DURATION_MS(20) – tick length.TICK_VOLUME(0.35) – tick amplitude.
Install system deps: libpcap headers and ALSA (e.g., sudo apt install libpcap-dev libasound2-dev).
cargo run
- Install cross toolchain + rsync:
sudo apt install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu pkg-config rsync
- Pull a Pi sysroot (one rsync; needs sudo locally to write under /opt):
export SYSROOT=/opt/pi-sysroot
sudo mkdir -p $SYSROOT
sudo rsync -a <pi-user>@<pi-ip>:/{lib,usr/lib,usr/include} $SYSROOT/
Ensure the sysroot contains alsa.pc at $SYSROOT/usr/lib/aarch64-linux-gnu/pkgconfig/alsa.pc and pcap.pc.
- Install Rust target:
rustup target add aarch64-unknown-linux-gnu
Set pkg-config to use the Pi sysroot when cross-compiling:
export PKG_CONFIG=/usr/bin/pkg-config
export PKG_CONFIG_ALLOW_CROSS=1
export PKG_CONFIG_SYSROOT_DIR=/opt/pi-sysroot
export PKG_CONFIG_LIBDIR=/opt/pi-sysroot/usr/lib/aarch64-linux-gnu/pkgconfig
export PKG_CONFIG_PATH=/opt/pi-sysroot/usr/lib/aarch64-linux-gnu/pkgconfig
cargo build --release --target aarch64-unknown-linux-gnu
For native host builds, unset these PKG_CONFIG_* variables to avoid pointing at the Pi sysroot.
The repository provides .cargo/config.toml pointing to the aarch64-linux-gnu-gcc linker.
Assumes key-based SSH for <pi-user>@<pi-ip> and the Pi has systemd plus shutdown on PATH.
PI_USER=pi PI_HOST=192.168.1.50 ./scripts/deploy.sh
This copies the binary and deploy/radioscope.service, installs them to /usr/local/bin and /etc/systemd/system, reloads systemd, enables, and restarts the service.
deploy/radioscope.service runs the binary as root (needed for monitor mode and shutdown). Edit the Environment= lines to change interface or bind address before deploying if needed, then systemctl daemon-reload && systemctl restart radioscope.
- The packet capture thread expects the interface to already be in monitor mode (e.g.,
wlan1mon). AdjustMONITOR_INTERFACEif you use a different name. - Shutdown endpoint requires an explicit confirmation flag in the request body and the UI shows a confirm dialog.
- For cross-builds, pkg-config must see the Pi sysroot (
alsa.pc,pcap.pc). Use the env vars above when cross-compiling; unset them for native builds. - The monitor interface should be created by the setup script as
wlan1mon; the sniffer opens it without forcing rfmon, so ensure the Wi‑Fi dongle is actually in monitor mode via the systemd unit.