# 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 <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> AUTH OFF                 # Disable auth
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`).

```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://<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) |

```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,<hostname>,motion=<0|1> rate=<hz> wander=<value>` 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,<hostname>,presence=1 score=0.0832` / `EVENT,<hostname>,presence=0 score=0.0234`
- **Memory:** ~1 KB total (baseline + accumulators + score buffer)

### LED States

| LED | Meaning |
|-----|---------|
| Off | Not connected to WiFi |
| Slow blink (1 Hz) | Connected, no CSI activity |
| Fast blink (5 Hz) | CSI data flowing |
| Solid (5s) | IDENTIFY command active |
| 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
```

Requires firmware with `_esp-csi._udp` mDNS service (v1.1+).

## HMAC Command Authentication

```bash
# Set auth secret on device
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"

esp-ctl cmd amber-maple.local "AUTH OFF"  # Disable auth
```

Protocol: `HMAC:<16hex>:<cmd>` — first 16 hex chars of HMAC-SHA256(secret, cmd).

## 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 <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

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

## 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)
```
