Headless Axon detector/tracker for the XIAO ESP32-S3. Instant detection, no UI, just beeps. Power on → boot jingle → already scanning → the moment an Axon device is heard, an 8-bit arpeggio tells you, and a proximity heartbeat tells you how close it still is.
No Python, no PlatformIO, no drivers to install:
https://colonelpanichacks.github.io/axeoff/
Chrome, Edge, or Opera on desktop. Plug in the XIAO ESP32-S3 with a USB-C data cable, click Connect & Flash, pick the serial port. Always ships the latest committed src/axeoff.bin.
There is deliberately no user interface of any kind: no WiFi AP, no web server, no NVS config, no MQTT, no LED. The buzzer is the entire UX. Serial output at 115200 baud is a debug log, not an interface.
- Seeed XIAO ESP32-S3 (works on the OUI-SPY board — same pinout)
- Passive/active buzzer on GPIO3 (D2), driven by LEDC PWM square waves
- Any USB-C cable for power + serial
| Sound | Pattern | Meaning |
|---|---|---|
| Boot | 4 ascending beeps (G4→C5→E5→G5) | awake and scanning |
| Detection | fast 8-bit ascending arpeggio (C5→E5→G5→C6→E6) | an Axon device was just heard |
| Heartbeat | 2-beep "lub-dub" (C5, then G4) | target still present — rate = proximity: ~2 s when far/weak, ~250 ms when close/strong |
| Lost | descending 2-beep (E5→E4) | target silent past the lost timeout, back to IDLE |
After the lost jingle the next fresh match re-triggers the detection arpeggio.
Five hardcoded signatures, all uniquely attributable to Axon Enterprise:
| # | Signature | Source | Notes |
|---|---|---|---|
| 1 | OUI 00:25:DF |
WiFi probe requests | IEEE registry: "Axon Enterprise, Inc." — body cams, Fleet |
| 2 | OUI 00:25:DF |
WiFi beacons | same OUI, AP-mode frames |
| 3 | Company ID 0x034D |
BLE manufacturer data | BT SIG registry: "TASER International" = Axon |
| 4 | Service UUIDs 0xFC81, 0xFE6B, 0xFE6C |
BLE advert | BT SIG member registry: all three owned by Axon/TASER |
| 5 | MAC prefix 00:25:DF |
BLE advert | same IEEE OUI on the BLE interface |
AXEOFF never transmits: the BLE side is a fully passive scan (no SCAN_REQ) and the WiFi side is promiscuous listen-only. All four BLE signatures (MAC prefix, manufacturer CID, service UUIDs) are present in ADV_IND payloads, so passive mode loses nothing.
WiFi and BLE share the single 2.4 GHz radio, so scanning is time-sliced: a BLE passive scan (~2 s, first after boot so the first hit lands fast) alternates with a promiscuous WiFi sweep hopping channels 11→1 at 200 ms dwell.
Up to 8 distinct Axon MACs are tracked; the freshest signal within the lost
timeout drives the heartbeat rate. Re-logs are suppressed for 30 s per
(MAC, signature) so a chattering device doesn't flood serial, while
last_seen still updates on every matching frame/advert to keep presence
tracking live.
Example debug line:
{"mac":"00:25:df:12:34:56","rssi":-65,"type":"BLE_CID","match":"034D"} Axon signature (TASER CID)
pio run -e seeed_xiao_esp32s3 -t upload
pio device monitor -b 115200The detection core (802.11 promiscuous RX path, BLE matching, time-sliced radio scheduler, MAC randomization-on-boot) is ported from ouispy-detector, whose dual-domain detection is proven on this exact hardware.
All in src/main.cpp:
| Define | Default | What it does |
|---|---|---|
BUZZER_PIN |
3 |
buzzer GPIO (D2 on the XIAO) |
LOST_TIMEOUT_MS |
8000 |
silence this long → lost jingle → IDLE |
RSSI_FAR_DB / RSSI_NEAR_DB |
-90 / -40 |
heartbeat RSSI map bounds |
HEARTBEAT_PERIOD_FAR_MS / ..._NEAR_MS |
2000 / 250 |
heartbeat period at map bounds |
RELOG_SUPPRESS_MS |
30000 |
per (MAC, signature) serial re-log suppression |
TRACK_TABLE_SIZE |
8 |
distinct MACs tracked |
WIFI_SWEEP_MS / BLE_SCAN_MS |
2200 / 2200 |
radio time-slice durations |
WIFI_HOP_MS |
200 |
per-channel dwell during WiFi sweeps |
JINGLE_* tables |
— | note frequencies/durations for the four sounds |
Note tempos: boot 70–85 ms per note, detection ~65–75 ms, heartbeat 60/110 ms, lost 150/260 ms. Frequencies are named musical pitches (C5 = 523 Hz, etc.) — change the tables and everything else just works.