Real-time passive drone detection and telemetry analysis firmware for the M5 Stack Cardputer ADV (ESP32-S3). Intercepts, decodes, and displays drone communications across multiple RF bands — all from a pocket-sized device.
- RemoteID Decoding — WiFi NAN/Beacon and BLE advertisement scanning per ASTM F3411
- ELRS Telemetry — Intercept ExpressLRS packets on 900 MHz (LoRa) and 2.4 GHz (NRF24)
- MAVLink Decoding — Full v1/v2 telemetry extraction (position, altitude, speed, battery, flight mode)
- DJI Detection — Identify OcuSync/O3 links with frequency and signal strength logging
- Pilot Localization — Estimate pilot position via RemoteID operator location, MAVLink home point, or RSSI triangulation
- Protocol Classification — Auto-identify ELRS, DJI, WiFi, MAVLink, Crossfire, FrSky from packet signatures
- 2D Map View — Positional display of aircraft, pilots, and your own location with adjustable zoom
- Proximity Alerts — Audio/visual notifications when drones enter a configurable radius
- Data Logging — CSV + KML export to microSD with automatic file rotation at 10 MB
- Hot-Swap Modules — Automatic LoRa ↔ NRF24 switching when hardware is connected/disconnected
| Component | Details |
|---|---|
| Board | M5 Stack Cardputer ADV |
| MCU | ESP32-S3FN8 (dual-core Xtensa LX7 @ 240 MHz) |
| Flash | 8 MB (QIO) |
| PSRAM | External, Octal SPI @ 80 MHz |
| Display | ST7789V2 1.14" LCD (240×135 px, RGB565) |
| Input | Built-in keyboard |
| Audio | NS4150B buzzer |
| Module | Interface | Band | Purpose |
|---|---|---|---|
| LoRa SX1262 (M5Stack Cap LoRa) | SPI | 862–928 MHz | ELRS 900 / LoRa monitoring |
| NRF24L01+ | SPI | 2400–2525 MHz | ELRS 2.4 GHz / DJI / WiFi detection |
| GPS ATGM 336H | UART | — | Monitor geolocation |
Note: LoRa and NRF24 share the SPI3 bus and are mutually exclusive — the firmware handles switching automatically.
- microSD (SPI/SDMMC) — Data logging, config files, KML export
The firmware follows a layered architecture with clean separation of concerns:
┌──────────────────────────────────────────────┐
│ UI Layer (screens) │
│ Scanner · Map · Settings · Log │
├──────────────────────────────────────────────┤
│ Services Layer │
│ Detection · Telemetry · Protocol Classifier │
│ Geolocation · Pilot Locator · Alerts │
│ Data Logger │
├──────────────────────────────────────────────┤
│ Domain Layer │
│ Aircraft Registry · Protocol Signatures DB │
│ Configuration Store │
├──────────────────────────────────────────────┤
│ HAL (Hardware Abstraction) │
│ LoRa · NRF24 · GPS · WiFi · BLE │
│ Display · SD Card · Buzzer · HW Manager │
├──────────────────────────────────────────────┤
│ ESP-IDF / FreeRTOS Platform │
│ SPI · UART · WiFi · BLE · GPIO │
└──────────────────────────────────────────────┘
Tasks are distributed across the ESP32-S3's dual cores:
| Core | Task | Priority |
|---|---|---|
| Core 0 (PRO) | WiFi/BLE Scanner | 5 |
| Core 0 (PRO) | RF Monitor (LoRa/NRF24) | 6 |
| Core 1 (APP) | UI Render | 7 |
| Core 1 (APP) | Decoder Pipeline | 5 |
| Core 1 (APP) | GPS Reader | 3 |
| Core 1 (APP) | Data Logger | 2 |
drone-telemetry-monitor/
├── CMakeLists.txt # Top-level ESP-IDF project config
├── sdkconfig.defaults # Default SDK configuration for ESP32-S3
├── partitions.csv # Custom partition table (3 MB app + 5 MB FAT)
├── components/
│ ├── common/ # Shared types, error codes, HAL base types
│ ├── hw_hal/ # Hardware abstraction layer
│ │ ├── include/ # HAL headers (lora, nrf24, gps, display, etc.)
│ │ └── src/ # HAL implementations + HW Manager state machine
│ ├── domain/ # Business logic and data models
│ │ ├── include/ # Aircraft registry, protocol signatures, config store
│ │ └── src/
│ ├── services/ # Application services
│ │ ├── include/ # Detection, decoding, classification, geolocation, etc.
│ │ └── src/
│ └── ui/ # Display screens and navigation
│ ├── include/ # UI manager + screen headers
│ └── src/
├── main/ # Application entry point
├── test/
│ ├── host/ # Host-side tests (Unity + Theft PBT, x86)
│ └── mocks/ # HAL mock implementations for testing
└── .github/workflows/ # CI/CD (build + auto-release)
- ESP-IDF v5.3+
- USB-C cable for flashing
- M5 Stack Cardputer ADV hardware
# Set up ESP-IDF environment
. $IDF_PATH/export.sh
# Set target to ESP32-S3
idf.py set-target esp32s3
# Build the firmware
idf.py build# Flash to device (adjust port as needed)
idf.py -p /dev/ttyACM0 flash
# Monitor serial output
idf.py -p /dev/ttyACM0 monitorThe firmware loads configuration from a config.json file on the microSD card. If the file is missing or malformed, sensible defaults are used.
Configurable parameters include:
- Alert thresholds (proximity distance, repeat interval, silent mode)
- Protocol signature table (updatable without reflashing)
- LoRa/NRF24 scan parameters
Tests run on the host machine (x86) using mocked HAL interfaces:
# Configure host tests in a fresh build directory
cmake -S test/host -B build/host
# Build host tests
cmake --build build/host
# Run all tests via CTest
ctest --test-dir build/host --output-on-failureFrameworks:
The project uses GitHub Actions with the espressif/idf:v5.3 Docker image:
- On every push/PR to
main: builds the firmware and uploads artifacts - On merge to
main: automatically creates a tagged release with firmware binaries
Release artifacts include:
drone-telemetry-monitor.bin(application)bootloader.binpartition-table.binflasher_args.json
The companion static dashboard is published at wsalmi.github.io/drone-tools/docs. It mirrors the Cardputer field workflow, including shortcuts 1–7, the deterministic browser emulator, aircraft telemetry views, and radar-style monitoring.
To connect a compatible Chromium desktop browser to the device, open the dashboard over HTTPS, select Connect Serial, and grant access to the device USB serial port. The dashboard uses the browser Web Serial API and communicates through the firmware NDJSON serial bridge; it does not require a SoftAP or server-side backend.
The static build is versioned in docs/ and is served free of charge by GitHub Pages from the public main branch and /docs directory.
| Protocol | Detection Method | Decoded Data |
|---|---|---|
| RemoteID | WiFi + BLE beacons | UAS ID, position, altitude, operator location |
| ELRS | LoRa (900 MHz) / NRF24 (2.4 GHz) | RSSI, LQ, battery, GPS |
| MAVLink v1/v2 | LoRa | Position, altitude, speed, battery, flight mode, home point |
| DJI (OcuSync/O3) | NRF24 | Frequency, RSSI, classification |
| Screen | Description |
|---|---|
| Main Menu | Navigation hub for all features |
| Scanner | Paginated list of detected aircraft (RSSI, protocol, distance, bearing) |
| Map | 2D positional view with aircraft, pilots, and monitor icons |
| Settings | Configure frequencies, alerts, display, and modules |
| Log | View recent events and trigger KML/CSV export |
| Partition | Type | Offset | Size |
|---|---|---|---|
| NVS | data/nvs | 0x9000 | 24 KB |
| PHY Init | data/phy | 0xF000 | 4 KB |
| Factory App | app/factory | 0x10000 | 3 MB |
| Storage (FAT) | data/fat | 0x310000 | ~5 MB |
- Fork the repository
- Create a feature branch (
git checkout -b feature/my-feature) - Build and test locally
- Submit a Pull Request targeting
main
- The firmware operates in passive monitoring mode only — it does not transmit RF signals
- All detected data is stored locally; no network connectivity is required for operation
- Protocol signature tables can be updated via SD card without reflashing
- The system continues operating in degraded mode when optional hardware modules are unavailable
