MIT-licensed ESP32-S3 hardware / radio multitool firmware for the M5Stack StickS3. Bruce is a design reference, not a fork (ADR-0001).
A sferic (short for atmospheric radio wave) is a natural radio frequency emission caused by lightning.
The architecture is a lightning-detection network: distributed
stations (M5 devices) capture RF impulses; a central collector correlates
them. Vocabulary follows the field — station, strike (one event),
flash (a capture session). See
docs/adr/ for the full design and docs/mcp/
for the per-toolset contracts.
pio run -e sferic # default build
pio run -e sferic -t upload # flash (StickS3 on USB-C)
pio device monitor -b 115200 # serial — assert DTRToolchain: PlatformIO + Arduino framework + M5Unified. (ESP-IDF is a dead end here — M5GFX crashes in its SPI init under IDF 5.3.)
sferic-usbhid is a second build (ADR-0005):
ARDUINO_USB_MODE=0 (TinyUSB), which unlocks the USB-HID tool but makes
flashing less reliable — hold the side button while connecting to force the
bootloader. The default sferic build keeps the rock-solid
USB-Serial/JTAG.
sferic-rfpack adds the subghz (CC1101) + nrf24 (nRF24L01+) toolsets
for the pingequa RF Pack S3 backpack
(ADR-0012,
pin map). Drivers are scaffolded from the
datasheets and unverified — the build boots and reports "RF Pack not
detected" with no board attached. Same USB mode as sferic.
sferic-ws is the networked station
(ADR-0009,
ws-transport): it opens a persistent WebSocket to
the Pi collector and speaks the same op vocabulary as the serial link
(src/dispatch.* is shared). Needs src/ws_config.h — copy
src/ws_config.h.example and fill in the Wi-Fi creds, collector address, and
per-station token (gitignored). The collector-side endpoint is not built
yet.
Two buttons: A = next / (hold) prev · B = select / (hold) back.
Over serial you can also type a A b B to drive the menu.
Tools that emit IR / RF / keystrokes (TV-B-Gone, IR Replay, BLE/USB HID) are two-step: B arms, B again fires, A cancels, 3-second timeout.
| Toolset | Tools |
|---|---|
| WiFi | AP Scan · Packet Monitor · Probe/Beacon · Evil Portal · Handshake Capture · Wardrive · Deauth Detector |
| Bluetooth | Scan (→ decode → GATT browse) · Spam Detect · HID Keyboard |
| Infrared | Learn / Replay · TV-B-Gone (~39 codes) · Protocol Decode |
| Sensors | Motion / Tamper · Mic Spectrum · Gesture |
| USB (sferic-usbhid only) | HID Keyboard |
| SubGHz / 2.4GHz (sferic-rfpack only) | Band Scan ×2 — scaffolded, RF Pack S3 not yet verified |
| Settings | Tools (enable/disable, persisted) · Display (sleep timeout + brightness) · Capture (pcap buffer) · Web UI · About |
Each tool is a registry.add({...}) line in src/menu_tree.cpp plus a
Screen; the menu is generated from the registry, filtered by the
persisted enable flags. Authorized-use scope is in
ADR-0001 — capture /
replay / decode / scan, no untargeted-DoS headline features.
Settings > Web UI stands up an open SoftAP sferic-ui (192.168.4.1) and
a one-page dashboard: tool enable/disable, a Run button on
run()-capable tools, live status, screen sleep / brightness, and a
capture.pcap download. It is the exclusive WiFi user while up — WiFi tools
pause. (ADR-0006)
The page is authored in web/ (index.html + style.css +
app.js); scripts/gen_webui.py inlines it into src/webui_html.h on
every firmware build (CI checks the committed header is current) — don't
hand-edit that header.
GET /api/tools/run?id=<tool-id> is the headless launch path — the same
Registry::run dispatch the MCP server will use. Only non-radio tools
(sensors.motion, sensors.gesture) expose it on the station for now;
/api/tools reports has_run per tool. (docs/mcp/headless.md)
A tethered station also answers a headless command channel over USB serial —
>{"op":"..."} lines in, <{...} lines out, coexisting with log output.
hello (capability declaration), status, tools, run (same
Registry::run dispatch), set. This is the milestone-2 transport for
m5stack-lab/sferic-collector; over
serial the radio is free, so every toolset is reachable once it has a
headless path. (docs/mcp/serial-transport.md)
The collector also auto-enriches wifi.scan/ble.scan results with a
best-effort vendor + known-CVE lead per device (OUI lookup + cve-search) —
see ADR-0010.
No SD slot, so 802.11 captures go to a 512 KB PSRAM pcap buffer
(LINKTYPE_IEEE802_11_RADIOTAP, valid Wireshark files). Export: the web
UI's capture.pcap download, or base64 over serial from Settings > Capture. The fleet model (ADR-0003,
ADR-0007) hands captures to the MCP
collector — no local persistence needed.
src/hal.* M5Unified wrapper — the only board-specific code
src/ui.* Screen + ScreenStack, MenuScreen, InfoScreen
src/registry.* tool registry + NVS-persisted enable flags
src/menu_tree.* registerBuiltinTools() + the live menu
src/pcap.* PSRAM pcap capture buffer
src/confirm.h two-step arm/confirm guard
src/serial_control.* headless command channel (collector transport)
src/<tool>.* one screen per tool
web/ web UI sources -> src/webui_html.h via scripts/gen_webui.py
docs/adr/ architecture decision records (0001–0011)
docs/mcp/ per-toolset MCP contracts + strike/pcap formats
test/ host unit tests — `pio test -e native`
StickS3 firmware + local web UI, with screen sleep and the headless
run() seam in place (sensors only so far — radio tools get their headless
path on the collector). Not built yet: the MCP server (Pi 5 collector),
RF Pack S3 drivers (SubGHz / nRF24 — waiting on hardware).
Versioned by SFERIC_VERSION in src/version.h. Pushing a vX.Y.Z tag on
main runs CI for both envs and publishes a GitHub
Release with sferic-<tag>.bin
(default USB-Serial/JTAG build) and sferic-usbhid-<tag>.bin (TinyUSB
HID build) attached, plus auto-generated notes.
git tag -a v0.1.0 -m "sferic v0.1.0"
git push origin v0.1.0See CONTRIBUTING.md. Short version: clean-room only (no
Bruce/GPL code), design changes carry an ADR, branch + PR off main, keep
both pio run envs green, no secrets.
- ry-ops — author / maintainer
Firmware co-authored with Claude Code (trailers on the commits).
MIT © 2026 ry-ops
Part of m5stack-lab · unofficial M5Stack projects, not affiliated with M5Stack · built by ry-ops