ESP32 CSI and Wi-Fi sensing: one-board, two-board and experiment design
By Wu Jun · Updated 2026-11-02 · 6 min read
ESP32 is one of the cheapest ways to reach real CSI. Espressif states that every ESP32 series supports CSI, including ESP32, S2, C3, S3, C5, C6 and C61 [1]. "Can obtain CSI" and "can run a stable experiment" are different claims, however: sender, packet rate, LTF parsing, channel and evaluation protocol all matter.
This guide follows the official esp-csi examples and ESP-IDF documentation.
1. Three capture routes
| Route | Hardware | Principle and best use |
|---|---|---|
| One board plus router | 1 ESP32 + AP | ESP32 pings the router and receives CSI; lowest cost, dependent on AP placement |
| Two boards plus router | 2 ESP32 + AP | one sends and the other receives; useful for device-to-device paths |
| Dedicated sender/receiver | at least 2 boards | a sender transmits continuously while receivers listen; controlled sampling and flexible deployment |
The official repository provides csi_recv_router, csi_recv and csi_send. The single-board route does not mean
the ESP32 transmits a radar pulse by itself. Another endpoint must send packets before the ESP32 can estimate CSI.
2. Route one: one ESP32 plus a router
This is the lowest-hardware starting point. The ESP32 joins the router, pings the gateway and receives CSI in replies.
git clone https://github.com/espressif/esp-csi.git
cd esp-csi/examples/get-started/csi_recv_router
idf.py set-target esp32s3 # replace with your chip
idf.py menuconfig # configure Wi-Fi or Ethernet
idf.py -p /dev/ttyUSB0 flash monitor
On macOS, serial devices are usually /dev/cu.usbserial-* or /dev/cu.SLAB_USBtoUART; do not copy Linux's
/dev/ttyUSB0 blindly.
Confirm four basics:
CSI_DATAappears on the serial console;lenmatches the expected format;- RSSI updates continuously;
- stopping ping or traffic reduces CSI output.
If only boot logs appear, check Wi-Fi association, packet flow and CSI enablement before blaming the parser.
3. Route two: two ESP32 boards
The official get-started example uses two boards: one flashes csi_send, the other csi_recv. Its current README
recommends C5/C6 and says the two antennas should be more than one metre apart. That does not mean older ESP32 chips
cannot produce CSI.
# sender
cd esp-csi/examples/get-started/csi_send
idf.py set-target esp32c3
idf.py -p /dev/ttyUSB0 flash -b 921600 monitor
# receiver
cd esp-csi/examples/get-started/csi_recv
idf.py set-target esp32c3
idf.py -p /dev/ttyUSB1 flash -b 921600
# receiver visualiser
cd esp-csi/examples/get-started/tools
python -m pip install -r requirements.txt
python csi_data_read_parse.py -p /dev/ttyUSB1
Both boards must use a target that matches the actual chip. Treating esp32c3 as a universal target is a common build
failure.
4. Subcarrier count is not a fixed 30-64
ESP-IDF CSI can contain three LTF fields:
- LLTF (legacy long training field);
- HT-LTF;
- STBC-HT-LTF.
The byte count depends on packet type, 20/40 MHz, STBC, secondary channel and which LTFs are enabled. The official
ESP32-S3 table lists len values such as 128, 256, 384, 380 and 612 bytes. len / 2 is not automatically the
number of independent subcarriers because the buffer may contain multiple LTF sections [2].
Preserve these fields:
| Field | Purpose |
|---|---|
len |
CSI byte count |
sig_mode / mcs |
packet type and modulation |
bandwidth / secondary_channel |
20/40 MHz and extension channel |
stbc |
STBC LTF presence |
first_word_invalid |
first four bytes are invalid on some ESP32-S3 frames |
local_timestamp |
estimate real packet spacing |
rssi / noise_floor |
link quality |
Each complex sample is int8 imaginary followed by int8 real. Do not apply the router CSI int16 I/Q layout directly to ESP32 data.
5. Two CSV text layouts
Older ESP32 examples commonly print:
type,id,mac,rssi,rate,sig_mode,mcs,bandwidth,...,len,first_word,data
CSI_DATA,0,... ,128,1,"[imag0,real0,imag1,real1,...]"
ESP32-C5/C6 examples can print:
type,seq,mac,rssi,rate,noise_floor,fft_gain,agc_gain,channel,
local_timestamp,sig_len,rx_state,len,first_word,data
Column order differs. A parser cannot rely on one fixed index:
- save one raw official CSV line as a format fixture;
- detect the header or the
type/seqcolumn; - validate
lenagainst thedataelement count; - retain raw text, not only cleaned amplitude.
6. Packet rate is the sampling-rate ceiling
ESP32 CSI is triggered by received packets. There is no guaranteed fixed 100 Hz. Independent sampling depends on the sender rate, channel contention, RSSI, packet length, power save and serial throughput.
Measure these before machine learning:
packets_total
valid CSI frames
duration from first/last local_timestamp
valid frames / duration
packet gap p50 / p95 / max
RSSI min / mean / max
len distribution
The official README also notes that expensive live plotting can overrun the serial buffer and produce mismatched element counts or corrupted display. Increase serial speed, disable high-CPU plotting or write to disk before judging the CSI itself.
7. Minimum reproducible experiment
Do not start with respiration or heart rate. Start with these classes:
| Scenario | Duration | Purpose |
|---|---|---|
| empty room baseline | 5 min | estimate environmental cycles and false alarms |
| still subject | 5 min | test whether micro-motion survives |
| walking subject | 5 x 30 s | validate motion envelope |
| empty/occupied alternation | 10 rounds | estimate presence false positives and misses |
Record:
- sender/receiver target, firmware commit and ESP-IDF version;
- channel, bandwidth, TX power, RSSI and noise floor;
- board distance, orientation and height;
- valid frames/s, maximum packet gap and serial loss;
- raw CSV or binary stream and capture-script version.
Split train/test by session or date. Do not randomly separate overlapping windows.
8. Realistic capability boundary
Good uses:
- learning amplitude/phase versus packet rate;
- low-cost presence or motion detection;
- ESPHome/Home Assistant integration experiments;
- small datasets for algorithm validation.
Do not claim by default:
- respiration or heart rate in every room;
- full walking micro-Doppler;
- high-accuracy range, angle or body imaging;
- one development board replacing a dedicated radar.
ESPectre and similar projects provide engineering references [3], but accuracy must still be tested in your own environment.
9. ESP32 versus GL-MT6000 router CSI
| Dimension | ESP32 examples | GL-MT6000 + mt76 CSI |
|---|---|---|
| Typical bandwidth | 20 MHz, some 40 MHz | 80 MHz |
| Frequency information | LTF/packet-dependent byte count | 256 subcarriers per chain |
| Spatial chains | usually one receive antenna | 2 Tx x 4 Rx |
| Sender | another board or router traffic | dedicated AP plus client |
| Sample rate | determined by packet rate | about 12.68 closed groups/s in this project (12.64 after anomaly filtering) |
| Deployment | cheap, flexible, single point | 24/7, broader coverage, harder flashing |
| Best for | entry, IoT, short experiments | long-running home capture and higher frequency resolution |
Router CSI is not a scaled-up ESP32. Binary format, chain count, extraction and sampling mechanisms differ. Algorithms may share ideas, but input dimensions, timelines and calibration do not transfer automatically.
10. From zero to a first plot
Recommended order:
- run single-board
csi_recv_router; - save one raw CSV line and verify
lenversusdata; - compute actual packet rate and maximum gap;
- capture five minutes of empty room and five minutes of walking;
- plot RSSI, CSI amplitude and motion envelope;
- only then try presence, vital signs or deep learning.
CSIKit supports ESP32 and several other formats; verify that its version and column definitions match your firmware. The Awesome Wi-Fi CSI Sensing list provides further tools and papers [4].
11. Conclusion
- every ESP32 series supports CSI, but targets, packet formats and recommendations differ;
- the single-board route depends on router traffic; the two-board route needs an explicit sender and receiver;
lenis not a fixed subcarrier count because LTF composition changes;- packet rate determines sampling and must be measured from timestamps and valid frames;
- ESP32 is excellent for low-cost entry and IoT, not a generic claim of full radar or medical-grade vital signs.
References and data sources
Every external claim carries a source and an evidence grade (grading scheme). Grade E marks first-party, reproducible measurements from this project.
- Grade C · official docs
espressif/esp-csi — Applications based on Wi-Fi CSI
Espressif Systems
github.com/espressif/esp-csi · verified: GitHub API 2026-10-29 (stars 1,723; README confirms the complete ESP32 series list and three sender/receiver routes) · The official README states: "All ESP32 series support CSI, including ESP32 / ESP32-S2 / ESP32-C3 / ESP32-S3 / ESP32-C5 / ESP32-C6 / ESP32-C61." - Grade C · official docs
ESP-IDF — Wi-Fi Channel State Information (ESP32-S3)
Espressif Systems
docs.espressif.com/projects/esp-idf/en/stable/esp32s3/api-guides/wifi-driver/wifi-vendor-features.html#wi-fi-channel-state-information · verified: official docs fetched 2026-10-29 (ESP32-S3 CSI section: LTF/byte-count table and the first_word_invalid note) - Grade C · official docs
francescopace/espectre — Wi-Fi CSI motion sensing for ESP32 (ESPHome)
F. Pace
github.com/francescopace/espectre · verified: GitHub API and official README fetched 2026-10-29 (stars 9,700; GPLv3; ESPHome source path, Direct API port 62587 and supported chips documented) · The official project provides ESPHome, native MQTT, Matter and browser flashing paths; one ESP32 covers one area and does not identify people or count occupants. - Grade C · official docs
NTUMARS/Awesome-WiFi-CSI-Sensing — curated papers and resources
NTU MARS Lab
github.com/NTUMARS/Awesome-WiFi-CSI-Sensing · verified: GitHub API 2026-10-11 (stars 1129, MIT, last push 2025-07-19) - Grade C · official docs
StevenMHernandez/ESP32-CSI-Tool — active/passive CSI extraction for ESP32
S. M. Hernandez
github.com/StevenMHernandez/ESP32-CSI-Tool · verified: GitHub API 2026-10-11 (stars 571, MIT, last push 2024-07-16)