# Cheatsheet ## Environment Setup ```bash source ~/esp/esp-idf/export.sh # Activate ESP-IDF (every shell) ``` ## Build & Flash ```bash 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) ```bash esp-cmd STATUS # Full device status (see STATUS Fields below) esp-cmd PROFILE # Heap, stack watermarks, CPU runtime stats esp-cmd IDENTIFY # LED solid 5s (find the device) esp-cmd RATE 50 # Set ping rate to 50 Hz (disables adaptive) esp-cmd POWER 15 # Set TX power to 15 dBm (NVS saved) esp-cmd ADAPTIVE ON # Enable adaptive sampling (NVS saved) esp-cmd ADAPTIVE OFF # Disable adaptive sampling esp-cmd THRESHOLD 0.005 # Set motion sensitivity (NVS saved) esp-cmd OTA http://pi:8070/fw # Trigger OTA update (use esp-ota instead) esp-cmd HOSTNAME mydevice # Set hostname (NVS saved, mDNS updated) esp-cmd SCANRATE 60 # BLE scan restart interval (5-300s) esp-cmd PROBERATE 5 # Probe dedup cooldown (1-300s) esp-cmd CSIMODE # Query current CSI output mode esp-cmd CSIMODE RAW # Full I/Q array (default, ~900 B/pkt) esp-cmd CSIMODE COMPACT # Features only (~200 B/pkt) esp-cmd CSIMODE HYBRID 10 # Compact + raw every Nth packet esp-cmd AUTH # Query auth status (on/off) esp-cmd AUTH mysecret123 # Enable HMAC auth (8-64 char secret) esp-cmd FLOODTHRESH # Query deauth flood threshold (5/10s) esp-cmd FLOODTHRESH 10 30 # Set: 10 deauths in 30s = flood esp-cmd CALIBRATE # Start baseline capture (default 10s, room must be empty) esp-cmd CALIBRATE 20 # Calibrate for 20 seconds esp-cmd CALIBRATE STATUS # Show baseline info (valid/invalid, nsub) esp-cmd CALIBRATE CLEAR # Delete baseline, disable presence esp-cmd PRESENCE # Query: on/off, baseline, threshold, score esp-cmd PRESENCE ON # Enable presence detection (needs baseline) esp-cmd PRESENCE OFF # Disable presence detection esp-cmd PRESENCE THRESHOLD 0.08 # Set presence threshold (0.001-1.0) esp-cmd REBOOT # Restart device ``` Host can be an IP or mDNS name (`amber-maple.local`). ```bash 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) ```bash 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) ```bash 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://:8070/.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) | ```bash 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,,motion=<0|1> rate= wander=` via UDP ```bash 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 ```bash # 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,,presence=1 score=0.0832` / `EVENT,,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 ```bash 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 ```bash # 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>::` — first 32 hex chars of HMAC-SHA256(secret, `:`). Replay window: +/-5s from device uptime. ### Serial Console (physical access) Connect via USB serial (921600 baud) for auth management without network auth: ```bash # 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 # Set new secret (8-64 chars) AUTH OFF # Clear secret (disable auth) STATUS # Basic device info HELP # List serial commands ``` ### Provisioning Tool ```bash 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 ```bash 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 ```bash 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 ```bash 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 HOSTNAME ` - 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,,seq,mac,rssi,rate,...,len,first_word,"[I,Q,...]" # RAW mode CSI_DATA,,seq,mac,rssi,rate,...,len,first_word,"F:rms,std,max,idx,energy" # COMPACT mode BLE_DATA,,mac,rssi,pub|rnd,name EVENT,,motion=0|1 rate= wander= EVENT,,calibrate=done packets= nsub= EVENT,,presence=0|1 score= ALERT_DATA,,deauth|disassoc,sender_mac,target_mac,rssi ALERT_DATA,,deauth_flood,, PROBE_DATA,,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 ```bash ls /dev/ttyUSB* /dev/ttyACM* # Find connected devices dmesg | tail # Check USB detection sudo usermod -aG dialout $USER # Fix permissions (re-login) ``` ## Security Testing ```bash # eFuse status (read-only, safe) source ~/esp/esp-idf/export.sh && espefuse.py -p /dev/ttyUSB0 summary # NVS dump (read-only) esptool.py -p /dev/ttyUSB0 -b 921600 read_flash 0x9000 0x4000 /tmp/nvs_dump.bin # Port scan sudo nmap -sU -p 5500,5501,5353 --open sudo nmap -sT -p 1-1000 # Firmware binary analysis binwalk build/csi_recv_router.bin strings -n 6 build/csi_recv_router.bin | grep -iE 'password|secret|key' ``` Full pentest guide: `docs/PENTEST.md` Pentest results: `docs/PENTEST-RESULTS.md`