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
- ESP32 connects to WiFi router as a station
- Pings the gateway at 100 Hz (10ms interval)
- Each ping response triggers a CSI callback
- CSI data is formatted as CSV and sent via UDP
Data Flow
CSI Data Format (ESP32)
| 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:
- 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:
Python Visualization Tools
get-started/tools/
Requires serial connection (not UDP). Use for development with csi_recv firmware.
esp-radar/console_test/tools/
CLI and GUI tools for CSI analysis with presence detection algorithms.