Passive LoRa and sub-GHz field logging
RX-only LoRa/sub-GHz wardriving firmware for the M5Stack Cap LoRa-1262 (SX1262) riding on a Cardputer-Adv (ESP32-S3). GPS-tagged detection logging across four mission profiles — Meshtastic, MeshCore, Reticulum, and general LoRa/spectrum exploration — built like a field instrument, not a laptop-tethered tool.
Note
Receive-only, by design. LoRaTrace has no transmit path beyond what
the antenna-switch init requires, and never will (CLAUDE.md house
rule). It observes and GPS-tags radio activity already in the air —
including other people's mesh traffic — for later analysis. It does not
inject or transmit. Passive decryption is permitted for public channels and
known operator-supplied keys; brute-force key recovery is out of scope.
The current firmware decodes Meshtastic public-default-key NodeInfo;
operator-key support is not implemented. Operate
it the way you'd operate any RF-monitoring instrument: know your local
regulations and respect reasonable expectations of privacy.
- Features
- Version
- Documentation
- Build
- Flash from your browser
- Install without flashing (M5Launcher)
- Configuration
- Output files
- Display and controls
- Web dashboard
- Four mission profiles, one receiver: Meshtastic, MeshCore, Reticulum,
and General Exploration (
Spectrumon-device) — presets, not separate tools. - Three radio modes, all GPS-tagged and logged to SD: Watch (continuous single-channel RX), Probe (bounded discovery scan across the full 868–923MHz band), and Sweep (frequency-binned energy map, with CAD confirmation at peaks landing in Phase 9).
- GPS-stamped CSV logging, one directory per wardrive — see Output files.
- On-device menu UI on the Cardputer-Adv's own screen and keyboard — no laptop required in the field. See Display and controls.
- On-demand WiFi dashboard for live status, per-run CSV download, and channel configuration — off by default, never running unattended unless toggled on. See Web dashboard.
- No PSRAM, no problem. Runs entirely within the ESP32-S3FN8's static RAM budget — SD is the datastore, not RAM. See docs/DESIGN.md.
The stable release is v1.0.7. main also builds a rolling
dev-latest
prerelease on every merge for hardware iteration (see
Install); it is not a stable release. The firmware
semantic version lives in src/version.h and is bumped by hand only when its
release gate lands. See docs/STATUS.md for current
hardware verification and open work rather than treating this summary as a
release record.
| Doc | For |
|---|---|
| docs/STATUS.md | Current version, what's hardware-verified, what's still open. Start here. |
| docs/DESIGN.md | Shipped v1 hardware, RF parameters, and architecture rationale — read before changing those foundations. |
| docs/ROADMAP.md | Active V2 workstreams, gates, and release policy. |
| docs/research/V2_DESIGN.md | V2 product direction and permanent boundaries. |
| docs/LOG_GUIDE.md | Operator guide to run folders, CSV fields, identity observations, health checks, and privacy-aware export. |
| docs/HARDWARE_TESTING.md | Repeatable device-validation matrix and Phase 7 memory acceptance rules. |
| docs/BRAND.md | Naming, tone, and on-device UI copy conventions. |
| CLAUDE.md | House rules for AI-assisted development on this repo. |
| docs/README.md | Full documentation index, including archived history and research notes. |
PlatformIO + Arduino framework:
pio run -e cardputer-adv
pio run -e cardputer-adv --target upload
pio device monitorUnit tests (host-native, no board needed):
pio test -e nativeweb-flasher → writes the firmware straight to the Cardputer-Adv over USB, no PlatformIO or M5Burner install required — built on ESP Web Tools, so it needs a desktop build of Chrome, Edge, or Opera (Web Serial isn't available elsewhere). Offers both the current dev-latest build and, once one has been published, the latest tagged release. This overwrites whatever firmware is currently on the device, Launcher included — use the SD-drop method below instead if you want to keep other firmwares installed side by side.
If your Cardputer-Adv already runs bmorcelli/Launcher, you don't need to touch USB flashing at all:
- Download the latest build:
https://github.com/d3mocide/LoRaTrace-RX/releases/download/dev-latest/LoRaTraceRX-dev.bin(rebuilt automatically frommainon every merge — check that release's notes for the commit it came from). Tagged, more-stable versions will appear on the Releases page once one has shipped. - Copy the
.binonto a FAT32 SD card. - In Launcher: SD → select the file → Install.
- To get back to Launcher afterward: reset the device and press any key
during Launcher's own ~5s boot window (it prints "Press the button to
enter the Launcher!" over serial while waiting) — miss it and Launcher
auto-boots straight back into whatever ran last. To skip that timing
window entirely, enable Launcher's own Settings → "Boot to Launcher"
toggle; it then always stops at its menu on reset until you turn it
back off. Not a software hook this firmware implements — see
docs/history/PROGRESS.mdfor how this was confirmed against Launcher's own source.
Serial output still works normally over USB while running under Launcher
— pio device monitor to watch the boot banner and any [RX]/[config]
lines.
By default LoRaTrace RX locks to Meshtastic LongFast (US):
906.875MHz / SF11 / BW250kHz / CR4:8, and MeshCore's US-narrow default:
910.525MHz / SF7 / BW62.5kHz / CR5. The first time it boots with an SD
card that doesn't already have one, it creates /loratrace/config.txt on
the card pre-filled with both defaults, one block per profile
(meshtastic_freq_mhz=, meshcore_freq_mhz=, etc. — each profile has its
own independent set of keys, so editing one never touches the other's
saved values). Edit either block in place for a non-default regional
preset (e.g. MeshOregon) and reboot — see the file's own comments for the
format, or use the web dashboard's Settings tab instead of hand-editing
(below). (sd-template/loratrace/ still exists if you want to prepare a
card offline before ever booting the device with it.) A missing card, a
read-only card, or out-of-range values all fail safe back to the hardcoded
default for that profile. Channel changes apply on next boot, not live —
switching profiles on the running device (menu or web) always uses
whatever was last saved for that profile.
One wardrive is one directory. Each power-on claims the next free
/loratrace/runNNNN/ on the card, so a drive can be copied, shared or
deleted as a unit:
/loratrace/
config.txt channel override (not a run artifact)
run0001/
detections.csv
nodes.csv
session.csv
probe.csv
energy.csv
run0002/
...
detections.csv— one row per received packet: GPS-stamped, with RSSI/SNR, RF parameters and whatever routing metadata the protocol exposes in clear. The full frame is retained as hex for offline analysis; LoRaTrace does not claim a general payload decoder.session.csv— one health row a minute (packets, drops, worst SD bus hold, heap free and low-water, GPS state, battery), plus a row marking the start. An unattended run is judged on whether it held up, and nobody is watching the serial console at hour three — so the run records its own vital signs next to its findings.nodes.csv— supported Meshtastic NodeInfo and MeshCore advertisement identity observations, kept separate because many packets can belong to one node. Seedocs/LOG_GUIDE.mdfor the exact supported profiles and limits.probe.csv— written when a Probe (discovery scan) is run: which fixed-candidate channels produced CAD activity. A CAD hit is not necessarily a packet.energy.csv— written when a Sweep (energy scan) is run: sparse high-energy bins and follow-up CAD results. Not a full spectrum recording.
probe.csv and energy.csv may exist with only a header row if that
mode was never triggered during the run — their absence of data is not an
error. See docs/LOG_GUIDE.md for the full field-by-field schema of every
file above.
Runs are numbered rather than timestamped because the name has to be chosen
before the GPS knows what time it is, and this board has no verified RTC.
The wall clock still reaches the card, recorded inside the run once a fix
lands. docs/LOG_GUIDE.md is the operator reference for schemas and analysis;
docs/DESIGN.md §8 explains the design rationale. The current run number is shown
on the RADIO page as r<N>.
Boot progress (firmware version, antenna-switch/radio status, active channel, and any FATAL error) is shown on the built-in LCD as well as over serial. Once the tasks are running the panel shows four read-only status pages — RADIO, CHANNEL, GPS, SYSTEM — with a battery indicator and header status dots (GPS fix, heap health, live RX activity) on every one, plus a footer line showing the active profile and page position.
A keyboard-driven, nestable menu covers every operator-facing toggle, three
root rows deep: Trace (pause/standby the radio-listening pipeline
without losing GPS fix), Profile (Meshtastic / MeshCore / Node IDs
capture), and System (WiFi and Serial Control under Connectivity,
Debug and SD retry under Diagnostics, brightness and idle-dim under
Display). ,/. cycle pages or move the current menu level's selection,
digits 1-5 jump straight to a page, Enter acts on the highlighted row
(or scrubs a slider), and the backtick/ESC key opens/closes the menu and
steps back up one level at a time; with no keyboard detected the pages
rotate on their own, so a device sitting on a dashboard still cycles
through everything. A toast band confirms whatever action just fired.
An on-demand WiFi access point (off by default — toggle it from the
System menu, never running unattended during a drive unless you turn it
on) hosts a small embedded web page at 192.168.4.1 once it's up. Three
tabs: a live status dashboard (the same counters the on-device SYSTEM/
RADIO pages and serial [status] line already expose, plus whether Trace
is active or paused), a run browser to download any of detections.csv/
session.csv/nodes.csv/probe.csv/energy.csv per run without ejecting
the SD card, and a Settings tab with
one independent panel per profile for editing channel parameters — the
same config.txt the SD card holds, applied on next boot. Measured cost:
roughly 55-60KB of heap while the AP is up, with no impact on radio/GPS/SD
reliability under real traffic — see docs/history/PROGRESS.md for the
measurement session, or docs/STATUS.md for what's still open.