diff --git a/.gitignore b/.gitignore index c634ead..9c70006 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ build/ managed_components/ +dependencies.lock sdkconfig.old *.pyc __pycache__/ diff --git a/TASKS.md b/TASKS.md index d4ab180..b8ede1a 100644 --- a/TASKS.md +++ b/TASKS.md @@ -9,14 +9,21 @@ - [x] Document current firmware and settings ### P1 - Important -- [ ] Document build & flash workflow step by step -- [ ] Create .gitignore for build artifacts -- [ ] Test building firmware from this repo +- [x] Document build & flash workflow step by step +- [x] Create .gitignore for build artifacts +- [x] Test building firmware from this repo ### P2 - Normal -- [ ] Document CSI config options (what each sdkconfig flag does) -- [ ] Compare csi_recv vs csi_recv_router differences +- [x] Document CSI config options (what each sdkconfig flag does) +- [x] Compare csi_recv vs csi_recv_router differences ### P3 - Low - [ ] Document esp-crab dual-antenna capabilities - [ ] Document esp-radar console features + +## Notes + +- Build confirmed working on ESP-IDF v6.1.0 (aarch64/Pi 5) +- Workaround needed: symlink `6.0` → `6.1` in `esp_csi_gain_ctrl` managed component +- Branch renamed from `master` to `main` +- Docs created: `docs/INSTALL.md`, `docs/USAGE.md`, `docs/CHEATSHEET.md` diff --git a/docs/CHEATSHEET.md b/docs/CHEATSHEET.md new file mode 100644 index 0000000..3b4fd08 --- /dev/null +++ b/docs/CHEATSHEET.md @@ -0,0 +1,95 @@ +# 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 flash # Flash to device +idf.py -p /dev/ttyUSB0 monitor # Serial monitor (Ctrl+] to exit) +idf.py -p /dev/ttyUSB0 flash monitor # Flash + monitor combined +idf.py fullclean # Clean build directory +idf.py reconfigure # Re-fetch managed components + +# IDF v6.1 workaround (after clean/reconfigure) +ln -s 6.0 managed_components/espressif__esp_csi_gain_ctrl/6.1 +``` + +## Deployed Sensors + +| Name | IP | Location | +|------|-----|----------| +| muddy-storm | 192.168.129.29 | Living Room | +| amber-maple | 192.168.129.30 | Office | +| hollow-acorn | 192.168.129.31 | Kitchen | + +**Target:** `192.168.129.11:5500` (Pi) + +## 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) +``` + +## 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 Data Format + +``` +CSI_DATA,seq,mac,rssi,rate,sig_mode,mcs,cwb,smoothing,not_sounding, +aggregation,stbc,fec_coding,sgi,noise_floor,ampdu_cnt,channel, +secondary_channel,timestamp,ant,sig_len,rx_state,len,first_word,"[I,Q,...]" +``` + +## 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 target config | +| `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 v6.1.0 | +| `~/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) +``` diff --git a/docs/INSTALL.md b/docs/INSTALL.md new file mode 100644 index 0000000..df784bc --- /dev/null +++ b/docs/INSTALL.md @@ -0,0 +1,134 @@ +# Build & Flash Guide + +## Prerequisites + +| Component | Version | Location | +|-----------|---------|----------| +| ESP-IDF | v6.1.0 | `~/esp/esp-idf/` | +| esp-csi | latest | `~/esp/esp-csi/` (reference only) | +| Xtensa toolchain | GCC 15.2.0 | `~/.espressif/tools/` | +| Python | 3.13+ | ESP-IDF venv | + +**Note:** No RISC-V toolchain installed. Only ESP32 (Xtensa) targets are supported. For ESP32-C3/C5/C6, run `~/esp/esp-idf/install.sh` with the appropriate target first. + +## Setup + +### 1. Activate ESP-IDF Environment + +```bash +source ~/esp/esp-idf/export.sh +``` + +This must be run in every new shell session. It adds `idf.py` and the toolchain to PATH. + +### 2. Set Target Chip + +```bash +cd ~/git/esp32-hacking/get-started/csi_recv_router +idf.py set-target esp32 +``` + +### 3. Configure WiFi and UDP Target + +```bash +idf.py menuconfig +``` + +Navigate to: +- **Example Connection Configuration** → Set WiFi SSID and password +- **CSI UDP Configuration** → Set target IP and port + +| Setting | Default | Description | +|---------|---------|-------------| +| WiFi SSID | (none) | Your router's SSID | +| WiFi Password | (none) | Your router's password | +| UDP Target IP | 192.168.129.11 | Pi's IP address | +| UDP Target Port | 5500 | Listening port on Pi | + +## Build + +```bash +cd ~/git/esp32-hacking/get-started/csi_recv_router +idf.py build +``` + +Build output goes to `build/` (excluded by `.gitignore`). External component `esp_csi_gain_ctrl` is fetched automatically to `managed_components/`. + +### ESP-IDF v6.1 Workaround + +The `esp_csi_gain_ctrl` component (v0.1.4) only ships prebuilt libraries up to ESP-IDF v6.0. On ESP-IDF v6.1+, the build fails with a missing `.a` file. Fix by symlinking: + +```bash +cd managed_components/espressif__esp_csi_gain_ctrl +ln -s 6.0 6.1 +``` + +This is needed after every `idf.py fullclean` or component re-fetch. The v6.0 library is ABI-compatible with v6.1. + +## Flash + +### Connect ESP32 + +Plug ESP32-DevKitC into USB. The serial port appears as `/dev/ttyUSB0` or `/dev/ttyACM0`. + +```bash +ls /dev/ttyUSB* /dev/ttyACM* +``` + +### Flash Firmware + +```bash +idf.py -p /dev/ttyUSB0 flash +``` + +### Monitor Serial Output + +```bash +idf.py -p /dev/ttyUSB0 monitor +``` + +Exit monitor with `Ctrl+]`. + +### Flash + Monitor (combined) + +```bash +idf.py -p /dev/ttyUSB0 flash monitor +``` + +## Deployed Devices + +| Name | IP | Location | Notes | +|------|-----|----------|-------| +| muddy-storm | 192.168.129.29 | Living Room | | +| amber-maple | 192.168.129.30 | Office | | +| hollow-acorn | 192.168.129.31 | Kitchen | | + +All devices run `csi_recv_router` firmware, sending CSI data to `192.168.129.11:5500`. + +## Building Other Firmware Variants + +Same process, different directory: + +```bash +# ESP-NOW receiver (serial output) +cd ~/git/esp32-hacking/get-started/csi_recv +idf.py set-target esp32 && idf.py build + +# ESP-NOW transmitter +cd ~/git/esp32-hacking/get-started/csi_send +idf.py set-target esp32 && idf.py build + +# Radar console (presence detection) +cd ~/git/esp32-hacking/esp-radar/console_test +idf.py set-target esp32 && idf.py build +``` + +## Troubleshooting + +| Problem | Solution | +|---------|----------| +| `idf.py: command not found` | Run `source ~/esp/esp-idf/export.sh` | +| `/dev/ttyUSB0` not found | Plug in ESP32, check `dmesg \| tail` | +| Permission denied on serial | `sudo usermod -aG dialout $USER`, then re-login | +| Build fails on dependencies | `idf.py reconfigure` to re-fetch components | +| WiFi not connecting | Check SSID/password in `idf.py menuconfig` | diff --git a/docs/USAGE.md b/docs/USAGE.md new file mode 100644 index 0000000..420c96c --- /dev/null +++ b/docs/USAGE.md @@ -0,0 +1,200 @@ +# Usage Guide + +## Firmware Variants + +This repo contains three firmware approaches for CSI data collection: + +| Firmware | Directory | Output | Use Case | +|----------|-----------|--------|----------| +| **csi_recv_router** | `get-started/csi_recv_router/` | UDP to Pi | **Production** - deployed on 3 sensors | +| **csi_recv** | `get-started/csi_recv/` | Serial console | Development/debugging | +| **csi_send** | `get-started/csi_send/` | N/A (transmit only) | Paired with csi_recv | + +## csi_recv_router (Deployed Firmware) + +### How It Works + +1. ESP32 connects to WiFi router as a station +2. Pings the gateway at 100 Hz (10ms interval) +3. Each ping response triggers a CSI callback +4. CSI data is formatted as CSV and sent via UDP + +### Data Flow + +``` +Router ←── ping ──→ ESP32 ──── UDP ──→ Pi (192.168.129.11:5500) + (100 Hz) (CSI_DATA) +``` + +### CSI Data Format (ESP32) + +``` +CSI_DATA,,,,,,,,, +,,,,,, +,,,,,, +,,,"[csi_values]" +``` + +| Field | Type | Description | +|-------|------|-------------| +| seq | int | Packet sequence number (increments from 0) | +| mac | MAC | Gateway MAC address | +| rssi | int8 | Received signal strength (dBm) | +| rate | uint8 | PHY rate | +| sig_mode | uint8 | 0=non-HT, 1=HT, 3=VHT | +| mcs | uint8 | Modulation and coding scheme | +| cwb | uint8 | Channel bandwidth (0=20MHz, 1=40MHz) | +| smoothing | uint8 | Channel estimate smoothing | +| not_sounding | uint8 | Not sounding frame | +| aggregation | uint8 | AMPDU aggregation | +| stbc | uint8 | Space-time block coding | +| fec_coding | uint8 | FEC coding (0=BCC, 1=LDPC) | +| sgi | uint8 | Short guard interval | +| noise_floor | int8 | Noise floor (dBm) | +| ampdu_cnt | uint8 | AMPDU sub-frame count | +| channel | uint8 | Primary channel number | +| secondary_channel | uint8 | Secondary channel offset | +| timestamp | uint32 | Local timestamp (microseconds) | +| ant | uint8 | Antenna number | +| sig_len | uint16 | Signal length | +| rx_state | uint32 | RX state (0 = valid) | +| len | int | CSI data length (bytes) | +| first_word_invalid | bool | First 4 bytes invalid flag | +| csi_values | int8[] | Raw CSI I/Q values (interleaved) | + +### CSI Values Interpretation + +The `csi_values` array contains interleaved I/Q pairs per subcarrier: + +``` +[I0, Q0, I1, Q1, I2, Q2, ...] +``` + +- Each pair represents one OFDM subcarrier +- Amplitude: `sqrt(I^2 + Q^2)` +- Phase: `atan2(Q, I)` +- Typical length: 128 bytes (64 subcarriers x 2 values) for HT20 + +## csi_recv vs csi_recv_router Comparison + +| Aspect | csi_recv | csi_recv_router | +|--------|----------|-----------------| +| **WiFi mode** | STA, no AP connection | STA, connects to router | +| **CSI source** | ESP-NOW packets from csi_send | Ping responses from gateway | +| **Output** | Serial (`ets_printf`) | UDP socket | +| **Requires** | Paired csi_send device | WiFi router only | +| **Promiscuous mode** | Yes | No | +| **MAC filtering** | Fixed `1a:00:00:00:00:00` | Gateway BSSID | +| **WiFi credentials** | Not needed | Required (menuconfig) | +| **CSI config (ESP32)** | All LTF types enabled | LLTF only (router compat) | +| **Gain compensation** | Logged to serial | Applied to UDP data | +| **Sequence counter** | From ESP-NOW payload (`rx_id`) | Local counter (`s_count`) | +| **Network** | Standalone (no router needed) | Depends on router | +| **Deployment** | Lab/testing | Production | + +### Key Differences in CSI Config (ESP32 target) + +| Setting | csi_recv | csi_recv_router | +|---------|----------|-----------------| +| `lltf_en` | true | true | +| `htltf_en` | **true** | **false** | +| `stbc_htltf2_en` | **true** | **false** | +| `manu_scale` | **false** | **true** | +| `shift` | **false** | **true** | + +The router firmware uses only LLTF for broader router compatibility. The ESP-NOW firmware enables all LTF types since it controls both endpoints. + +## CSI Configuration Reference + +### sdkconfig.defaults + +#### WiFi Settings + +| Config | Value | Description | +|--------|-------|-------------| +| `CONFIG_ESP32_WIFI_CSI_ENABLED` | `y` | **Required.** Enables CSI extraction from received frames | +| `CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED` | (empty) | Disables TX aggregation. Aggregated frames combine CSI, reducing quality | +| `CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED` | (empty) | Disables RX aggregation (csi_recv_router only) | +| `CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM` | `128` | Number of dynamic RX buffers. Higher = fewer drops at high CSI rates. Default is 32 | +| `CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER_NUM` | `32` | Number of dynamic TX buffers | + +#### Performance Settings + +| Config | Value | Description | +|--------|-------|-------------| +| `CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ` | `240` | Max CPU clock. Ensures CSI callback and UDP send complete within 10ms | +| `CONFIG_COMPILER_OPTIMIZATION_PERF` | `y` | `-O2` optimization. Faster CSI processing | +| `CONFIG_FREERTOS_HZ` | `1000` | 1ms tick resolution. Required for 100 Hz ping timer accuracy | + +#### System Settings + +| Config | Value | Description | +|--------|-------|-------------| +| `CONFIG_ESP_TASK_WDT_TIMEOUT_S` | `30` | Watchdog timeout. Extended from default 5s to avoid false resets during WiFi reconnect | +| `CONFIG_ESP_CONSOLE_UART_BAUDRATE` | `921600` | Fast serial for debug output | +| `CONFIG_ESPTOOLPY_MONITOR_BAUD` | `921600` | Monitor baud rate (must match console) | + +### Kconfig.projbuild (Custom Settings) + +| Config | Default | Range | Description | +|--------|---------|-------|-------------| +| `CONFIG_CSI_UDP_TARGET_IP` | `192.168.129.11` | Any IPv4 | Destination IP for UDP CSI packets | +| `CONFIG_CSI_UDP_TARGET_PORT` | `5500` | 1024-65535 | Destination UDP port | + +### wifi_csi_config_t (Code-Level Settings) + +#### ESP32 (Xtensa) + +| Field | csi_recv_router | Description | +|-------|----------------|-------------| +| `lltf_en` | `true` | Enable Legacy Long Training Field. Most compatible with routers | +| `htltf_en` | `false` | HT-LTF. Only in HT (802.11n) frames. Disabled for router compat | +| `stbc_htltf2_en` | `false` | STBC HT-LTF2. Only with STBC encoding | +| `ltf_merge_en` | `true` | Merge multiple LTF into one CSI report | +| `channel_filter_en` | `true` | Apply channel estimation filter | +| `manu_scale` | `true` | Manual amplitude scaling | +| `shift` | `true` | Bit shift for value range | + +### Compile-Time Defines (app_main.c) + +| Define | Value | Description | +|--------|-------|-------------| +| `CONFIG_SEND_FREQUENCY` | `100` | Ping rate in Hz (10ms interval) | +| `CONFIG_FORCE_GAIN` | `0` | Force AGC/FFT gain to baseline (disabled) | +| `CONFIG_GAIN_CONTROL` | `1` (auto) | Enable gain compensation (ESP32-S3, C3, C5, C6, C61) | + +## Receiving CSI Data on the Pi + +Listen on UDP port 5500: + +```bash +# Quick test +nc -lu 5500 + +# Save to file +nc -lu 5500 > csi_capture.csv + +# With socat (more reliable) +socat UDP-RECV:5500 STDOUT +``` + +## Python Visualization Tools + +### get-started/tools/ + +```bash +pip install -r get-started/tools/requirements.txt +python get-started/tools/csi_data_read_parse.py +``` + +Requires serial connection (not UDP). Use for development with `csi_recv` firmware. + +### esp-radar/console_test/tools/ + +```bash +pip install -r esp-radar/console_test/tools/requirements.txt +python esp-radar/console_test/tools/esp_csi_tool.py +python esp-radar/console_test/tools/esp_csi_tool_gui.py +``` + +CLI and GUI tools for CSI analysis with presence detection algorithms. diff --git a/get-started/csi_recv_router/dependencies.lock b/get-started/csi_recv_router/dependencies.lock deleted file mode 100644 index f6cebed..0000000 --- a/get-started/csi_recv_router/dependencies.lock +++ /dev/null @@ -1,156 +0,0 @@ -dependencies: - espressif/cmake_utilities: - component_hash: 351350613ceafba240b761b4ea991e0f231ac7a9f59a9ee901f751bddc0bb18f - dependencies: - - name: idf - require: private - version: '>=4.1' - source: - registry_url: https://components.espressif.com - type: service - version: 0.5.3 - espressif/esp_csi_gain_ctrl: - component_hash: bb7b9e28f47f4de968d4aaa2a083ea5764e5994dd0c5146146958060e6b66247 - dependencies: - - name: espressif/cmake_utilities - registry_url: https://components.espressif.com - require: private - version: 0.* - - name: idf - require: private - version: '>=4.4.1' - source: - registry_url: https://components.espressif.com/ - type: service - targets: - - esp32 - - esp32s3 - - esp32s2 - - esp32c3 - - esp32c5 - - esp32c6 - - esp32c61 - version: 0.1.4~1 - espressif/ethernet_init: - component_hash: b7303f1482091766b9b653ec8332775a9485f4df3c69141577de82847c4d2adf - dependencies: - - matches: - - if: $CONFIG{ETHERNET_SPI_USE_CH390} == 1 - name: espressif/ch390 - registry_url: https://components.espressif.com - require: private - version: '*' - - matches: - - if: $CONFIG{ETHERNET_SPI_USE_DM9051} == 1 - name: espressif/dm9051 - registry_url: https://components.espressif.com - require: private - rules: - - if: idf_version >=6.0 - version: ^1.0.0 - version: '*' - - matches: - - if: idf_version >=6.0 - version: ^1.0.0 - name: espressif/dp83848 - registry_url: https://components.espressif.com - require: private - rules: - - if: target in [esp32, esp32p4] - - if: $CONFIG{ETHERNET_PHY_USE_DP83848} == 1 - version: '*' - - matches: - - if: $CONFIG{ETHERNET_SPI_USE_ENC28J60} == 1 - name: espressif/enc28j60 - registry_url: https://components.espressif.com - require: private - version: '*' - - matches: - - if: idf_version >=6.0 - version: ^1.0.0 - name: espressif/ip101 - registry_url: https://components.espressif.com - require: private - rules: - - if: target in [esp32, esp32p4] - - if: $CONFIG{ETHERNET_PHY_USE_IP101} == 1 - version: '*' - - matches: - - if: idf_version >=6.0 - version: ^1.0.0 - name: espressif/ksz80xx - registry_url: https://components.espressif.com - require: private - rules: - - if: target in [esp32, esp32p4] - - if: $CONFIG{ETHERNET_PHY_USE_KSZ80XX} == 1 - version: '*' - - matches: - - if: $CONFIG{ETHERNET_SPI_USE_KSZ8851SNL} == 1 - name: espressif/ksz8851snl - registry_url: https://components.espressif.com - require: private - rules: - - if: idf_version >=6.0 - version: ^1.0.0 - version: '*' - - matches: - - if: $CONFIG{ETHERNET_SPI_USE_LAN865X} == 1 - name: espressif/lan865x - registry_url: https://components.espressif.com - require: private - version: ^0.1.1 - - name: espressif/lan867x - registry_url: https://components.espressif.com - require: private - rules: - - if: target in [esp32, esp32p4] - - if: $CONFIG{ETHERNET_PHY_USE_LAN867X} == 1 - version: '>=2.0.0' - - matches: - - if: idf_version >=6.0 - version: ^1.0.0 - name: espressif/lan87xx - registry_url: https://components.espressif.com - require: private - rules: - - if: target in [esp32, esp32p4] - - if: $CONFIG{ETHERNET_PHY_USE_LAN87XX} == 1 - version: '*' - - matches: - - if: idf_version >=6.0 - version: ^1.0.0 - name: espressif/rtl8201 - registry_url: https://components.espressif.com - require: private - rules: - - if: target in [esp32, esp32p4] - - if: $CONFIG{ETHERNET_PHY_USE_RTL8201} == 1 - version: '*' - - matches: - - if: $CONFIG{ETHERNET_SPI_USE_W5500} == 1 - name: espressif/w5500 - registry_url: https://components.espressif.com - require: private - rules: - - if: idf_version >=6.0 - version: ^1.0.0 - version: '*' - - name: idf - require: private - version: '>=5.4.3,!=5.5.0,!=5.5.1' - source: - registry_url: https://components.espressif.com/ - type: service - version: 1.2.0 - idf: - source: - type: idf - version: 6.1.0 -direct_dependencies: -- espressif/esp_csi_gain_ctrl -- espressif/ethernet_init -- idf -manifest_hash: 612e7d3604727e429e9b873bb7becd2967d5d7c5dc868ee1ff7595b6ee816ffd -target: esp32 -version: 2.0.0