Files
esp32-hacking/docs/CHEATSHEET.md

16 KiB

Cheatsheet

Environment Setup

source ~/esp/esp-idf/export.sh          # Activate ESP-IDF (every shell)

Build & Flash

cd ~/git/esp32-hacking/get-started/csi_recv_router

idf.py set-target esp32                 # Set chip target
idf.py menuconfig                       # Configure WiFi/UDP settings
idf.py build                            # Compile firmware
idf.py -p /dev/ttyUSB0 -b 460800 flash  # Flash to device (460800 baud)
idf.py -p /dev/ttyUSB0 monitor          # Serial monitor (Ctrl+] to exit)
idf.py -p /dev/ttyUSB0 -b 460800 flash monitor  # Flash + monitor combined
idf.py fullclean                        # Clean build directory
idf.py reconfigure                      # Re-fetch managed components

Deployed Sensors

Name IP mDNS Location
muddy-storm 192.168.129.29 muddy-storm.local Living Room
amber-maple 192.168.129.30 amber-maple.local Office
hollow-acorn 192.168.129.31 hollow-acorn.local Kitchen

Target: 192.168.129.11:5500 (Pi) | Cmd port: 5501

Remote Management (esp-cmd)

esp-cmd <host> STATUS                   # Full device status (see STATUS Fields below)
esp-cmd <host> PROFILE                  # Heap, stack watermarks, CPU runtime stats
esp-cmd <host> IDENTIFY                 # LED solid 5s (find the device)
esp-cmd <host> RATE 50                  # Set ping rate to 50 Hz (disables adaptive)
esp-cmd <host> POWER 15                 # Set TX power to 15 dBm (NVS saved)
esp-cmd <host> ADAPTIVE ON              # Enable adaptive sampling (NVS saved)
esp-cmd <host> ADAPTIVE OFF             # Disable adaptive sampling
esp-cmd <host> THRESHOLD 0.005          # Set motion sensitivity (NVS saved)
esp-cmd <host> OTA http://pi:8070/fw    # Trigger OTA update (use esp-ota instead)
esp-cmd <host> HOSTNAME mydevice         # Set hostname (NVS saved, mDNS updated)
esp-cmd <host> SCANRATE 60              # BLE scan restart interval (5-300s)
esp-cmd <host> PROBERATE 5              # Probe dedup cooldown (1-300s)
esp-cmd <host> CSIMODE                  # Query current CSI output mode
esp-cmd <host> CSIMODE RAW             # Full I/Q array (default, ~900 B/pkt)
esp-cmd <host> CSIMODE COMPACT         # Features only (~200 B/pkt)
esp-cmd <host> CSIMODE HYBRID 10       # Compact + raw every Nth packet
esp-cmd <host> AUTH                     # Query auth status (on/off)
esp-cmd <host> AUTH mysecret123         # Enable HMAC auth (8-64 char secret)
esp-cmd <host> FLOODTHRESH             # Query deauth flood threshold (5/10s)
esp-cmd <host> FLOODTHRESH 10 30       # Set: 10 deauths in 30s = flood
esp-cmd <host> CALIBRATE                # Start baseline capture (default 10s, room must be empty)
esp-cmd <host> CALIBRATE 20            # Calibrate for 20 seconds
esp-cmd <host> CALIBRATE STATUS        # Show baseline info (valid/invalid, nsub)
esp-cmd <host> CALIBRATE CLEAR         # Delete baseline, disable presence
esp-cmd <host> PRESENCE                # Query: on/off, baseline, threshold, score
esp-cmd <host> PRESENCE ON             # Enable presence detection (needs baseline)
esp-cmd <host> PRESENCE OFF            # Disable presence detection
esp-cmd <host> PRESENCE THRESHOLD 0.08 # Set presence threshold (0.001-1.0)
esp-cmd <host> REBOOT                   # Restart device

Host can be an IP or mDNS name (amber-maple.local).

esp-cmd amber-maple.local STATUS        # Single device via mDNS
esp-cmd 192.168.129.29 IDENTIFY         # Single device via IP

Fleet Management (esp-fleet)

esp-fleet status                        # Query all sensors at once
esp-fleet identify                      # Blink all LEDs
esp-fleet rate 50                       # Set rate on all devices
esp-fleet reboot                        # Reboot entire fleet
esp-fleet ota                           # OTA update all (sequential)
esp-fleet ota /path/to/firmware.bin     # OTA with custom firmware

OTA Updates (esp-ota)

esp-ota amber-maple.local               # OTA with default build
esp-ota amber-maple.local -f fw.bin     # OTA with custom firmware
esp-ota amber-maple.local --no-wait     # Fire and forget

First flash after enabling OTA requires USB (partition table change). After that, all updates are OTA.

OTA Flow

  1. Verifies device is alive via STATUS
  2. Starts temp HTTP server on Pi (port 8070)
  3. Sends OTA http://<pi>:8070/<fw>.bin via UDP
  4. Device downloads, flashes, reboots
  5. Verifies device responds post-reboot

Rollback

If new firmware crashes or hangs, the 30s watchdog reboots and bootloader automatically rolls back to the previous firmware.

CSI Output Modes

Mode Payload Size BW @ 100 Hz
RAW (default) "[I,Q,I,Q,...]" (128 values) ~900 B ~90 KB/s
COMPACT "F:rms,std,max,idx,energy" ~200 B ~20 KB/s
HYBRID N Compact every packet, raw every Nth ~270 B avg (N=10) ~27 KB/s

Compact features (per packet, from 64 I/Q subcarrier pairs):

Feature Type Description
amp_rms float RMS amplitude = sqrt(mean(I²+Q²))
amp_std float Std dev of per-subcarrier amplitudes
amp_max float Peak subcarrier amplitude
amp_max_idx uint8 Index (0-63) of peak subcarrier
energy uint32 L1 norm (same as adaptive sampling)
esp-cmd amber-maple.local CSIMODE COMPACT   # Switch to compact
esp-cmd amber-maple.local CSIMODE HYBRID 10  # Raw every 10th packet
esp-cmd amber-maple.local CSIMODE RAW        # Back to full I/Q
esp-cmd amber-maple.local CSIMODE            # Query current mode

Mode is NVS-persisted and survives reboots.

Adaptive Sampling

When enabled, the device automatically adjusts ping rate based on CSI wander:

  • Motion detected (wander > threshold): 100 pkt/s
  • Idle (wander < threshold for 3s): 10 pkt/s
  • Rate changes send EVENT,<hostname>,motion=<0|1> rate=<hz> wander=<value> via UDP
esp-cmd amber-maple.local ADAPTIVE ON   # Enable
esp-cmd amber-maple.local THRESHOLD 0.005  # Tune sensitivity
# Lower threshold = more sensitive, higher = less sensitive
# Good starting range: 0.001 - 0.01

Presence Detection

CSI-based presence detection compares live per-subcarrier amplitudes against a baseline captured with the room empty.

Setup

# 1. Ensure room is empty, CSI is flowing (200+ packets)
esp-cmd amber-maple.local CALIBRATE 10   # Capture 10s baseline
# Wait for: EVENT,amber-maple,calibrate=done packets=1000 nsub=52

# 2. Verify baseline
esp-cmd amber-maple.local CALIBRATE STATUS  # Should show "valid nsub=52"

# 3. Enable presence detection
esp-cmd amber-maple.local PRESENCE ON       # Starts scoring

# 4. Tune threshold (optional)
esp-cmd amber-maple.local PRESENCE THRESHOLD 0.08  # Higher = less sensitive

How It Works

  • Baseline: Average per-subcarrier amplitude over N seconds (stored in NVS)
  • Scoring: Normalized Euclidean distance: sqrt(sum((live-baseline)^2) / sum(baseline^2))
  • Window: Rolling average of 50 scores
  • Threshold: Default 0.05 (5% normalized distance)
  • Holdoff: 10s debounce between state transitions
  • Events: EVENT,<hostname>,presence=1 score=0.0832 / EVENT,<hostname>,presence=0 score=0.0234
  • Memory: ~1 KB total (baseline + accumulators + score buffer)

LED States

Default mode is quiet (LED off unless noteworthy). Use LED AUTO for constant blink.

LED (quiet mode) Meaning
Off Normal operation
Solid Motion or presence detected
Double blink OTA in progress
Solid (5s) IDENTIFY command active
LED (auto mode) Meaning
Off Not connected to WiFi
Slow blink (1 Hz) Connected, no CSI activity
Fast blink (5 Hz) CSI data flowing
Double blink OTA in progress

Sensor Discovery

esp-ctl discover                        # Find all sensors via mDNS
esp-ctl discover -t 5                   # Longer browse (5s timeout)
esp-ctl status --discover               # Status using discovered fleet
esp-ctl target --discover               # Query targets via discovery

Sensors register their hostname via mDNS on boot.

HMAC Command Authentication

# Set auth secret on device (requires existing auth or serial access)
esp-ctl cmd amber-maple.local "AUTH mysecretkey123"

# Set env var so all tools sign commands automatically
export ESP_CMD_SECRET="mysecretkey123"   # add to ~/.bashrc.secrets

# All esp-cmd/esp-ctl/esp-fleet/esp-ota commands auto-sign when ESP_CMD_SECRET is set
# Unsigned commands are rejected with "ERR AUTH required"

Protocol: HMAC:<32hex>:<uptime_s>:<cmd> — first 32 hex chars of HMAC-SHA256(secret, <uptime_s>:<cmd>). Replay window: +/-5s from device uptime.

Serial Console (physical access)

Connect via USB serial (921600 baud) for auth management without network auth:

# Connect to serial console
idf.py -p /dev/ttyUSB0 monitor          # or: screen /dev/ttyUSB0 921600

# Serial commands (type directly):
AUTH                                      # Show full secret (unredacted)
AUTH <secret>                             # Set new secret (8-64 chars)
AUTH OFF                                  # Clear secret (disable auth)
STATUS                                    # Basic device info
HELP                                      # List serial commands

Provisioning Tool

esp-provision                             # Auto-generate secret, set via serial
esp-provision mysecretkey123              # Set specific secret via serial
esp-provision --serial                    # Set via serial console (device running)
esp-provision --generate-only             # Just print a random secret
esp-provision -p /dev/ttyACM0             # Use different serial port

OUI Vendor Lookup

esp-ctl oui --update                    # Download IEEE OUI database (~30k entries)
esp-ctl oui b0:be:76:a1:2d:c0          # Look up vendor for a MAC

Watch Daemon & OSINT

esp-ctl watch                           # Listen on :5500, store probes/BLE/alerts in DB
esp-ctl watch -c ~/my-config.yaml       # Custom config (HA webhooks, known MACs)
esp-ctl watch -v                        # Verbose logging

esp-ctl osint probes                    # Probe SSID history table
esp-ctl osint probes --mac AA:BB:CC:DD:EE:FF  # Filter by MAC
esp-ctl osint devices                   # All device sightings
esp-ctl osint devices -t ble            # BLE only
esp-ctl osint mac AA:BB:CC:DD:EE:FF    # Full profile for one MAC
esp-ctl osint stats                     # Summary counts

DB: ~/.local/share/esp-ctl/osint.db (SQLite with WAL). Config: ~/.config/esp-ctl/watch.yaml (HA webhooks, known MACs file).

Test CSI Reception

nc -lu 5500                             # Listen for CSI packets
socat UDP-RECV:5500 STDOUT              # Alternative listener
nc -lu 5500 | head -1                   # See one packet
nc -lu 5500 | wc -l                     # Count packets/sec (Ctrl+C)
esp-ctl listen -f probe -n 5            # Capture 5 probe requests (ESP32-C6+ only)
esp-ctl listen -f alert                 # Monitor deauth/disassoc alerts (ESP32-C6+ only)

Firmware Variants

Firmware Dir Output Needs
csi_recv_router get-started/csi_recv_router/ UDP WiFi router
csi_recv get-started/csi_recv/ Serial csi_send device
csi_send get-started/csi_send/ N/A csi_recv device

Key Config (menuconfig)

Path Setting
Example Connection Configuration → SSID WiFi network name
Example Connection Configuration → Password WiFi password
CSI UDP Configuration → IP 192.168.129.11
CSI UDP Configuration → Port 5500
CSI UDP Configuration → Cmd port 5501
CSI UDP Configuration → Hostname mDNS name (e.g., amber-maple)

USB Flash Notes

  • Use 460800 baud (-b 460800) — 921600 causes connection failures on some boards
  • Hostname can be changed at runtime via esp-ctl cmd <host> HOSTNAME <name>
  • First flash after enabling OTA partitions must be via USB

Data Packet Formats

All data packets include sensor hostname after the type tag:

CSI_DATA,<hostname>,seq,mac,rssi,rate,...,len,first_word,"[I,Q,...]"       # RAW mode
CSI_DATA,<hostname>,seq,mac,rssi,rate,...,len,first_word,"F:rms,std,max,idx,energy"  # COMPACT mode
BLE_DATA,<hostname>,mac,rssi,pub|rnd,name
EVENT,<hostname>,motion=0|1 rate=<hz> wander=<value>
EVENT,<hostname>,calibrate=done packets=<n> nsub=<n>
EVENT,<hostname>,presence=0|1 score=<float>
ALERT_DATA,<hostname>,deauth|disassoc,sender_mac,target_mac,rssi
ALERT_DATA,<hostname>,deauth_flood,<count>,<window_s>
PROBE_DATA,<hostname>,mac,rssi,ssid

CSI mode discriminator: quoted field starts with [ (raw) or F: (compact).

Note: On original ESP32, promiscuous mode (ALERT_DATA, PROBE_DATA) is disabled because it breaks CSI data collection at the driver level. These packet types are only generated on ESP32-C6 and newer chips.

STATUS Fields

Unauthenticated STATUS returns a minimal subset. Full fields require HMAC auth.

Field Example Auth Description
hostname amber-maple no Device hostname
uptime 1h23m no Human-readable uptime
uptime_s 4980 no Raw uptime in seconds
rssi -67 no Current AP RSSI (dBm)
channel 11 no WiFi channel
version 27aeddb no Firmware git commit
motion 0/1 no Motion detected
presence on/off no Presence detection
heap 108744 yes Free heap bytes
tx_power 10 yes TX power (dBm)
rate 100 yes Target CSI rate (Hz)
csi_rate 97 yes Actual CSI rate (Hz, computed)
adaptive on/off yes Adaptive sampling
ble on/off yes BLE scanning
target 192.168.129.11:5500 yes UDP destination
temp 0.0 yes Chip temperature (ESP32-S2/C3/C6 only)
csi_count 30002 yes Total CSI frames since boot
boots 3 yes Boot count (NVS persisted)
rssi_min -71 yes Lowest RSSI since boot
rssi_max -62 yes Highest RSSI since boot
csi_mode raw/compact/hybrid yes CSI output mode
hybrid_n 10 yes Raw packet interval (hybrid mode)
auth on/off yes HMAC command authentication
flood_thresh 5/10 yes Deauth flood: count/window_seconds
powersave on/off yes WiFi modem sleep
pr_score 0.0432 yes Current presence score

PROFILE Sections

Section Fields Description
HEAP free, min, dram, iram Heap usage and watermarks
TASKS stack_free per task Per-task stack high watermark
CPU % per task FreeRTOS runtime stats (requires CONFIG_FREERTOS_USE_TRACE_FACILITY)

Source Paths

File Purpose
get-started/csi_recv_router/main/app_main.c Main firmware source
get-started/csi_recv_router/main/Kconfig.projbuild UDP/cmd port config
tools/esp-cmd Pi-side management CLI
tools/esp-ota Pi-side OTA update tool
tools/esp-fleet Fleet-wide command tool
get-started/csi_recv_router/sdkconfig.defaults SDK defaults
get-started/csi_recv_router/main/idf_component.yml Dependencies
get-started/csi_recv_router/CMakeLists.txt Build config

ESP-IDF Paths

Path Contents
~/esp/esp-idf/ ESP-IDF v5.5.2
~/esp/esp-csi/ Original esp-csi examples
~/.espressif/tools/ Xtensa toolchain

USB Serial

ls /dev/ttyUSB* /dev/ttyACM*            # Find connected devices
dmesg | tail                             # Check USB detection
sudo usermod -aG dialout $USER           # Fix permissions (re-login)