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:

  1. CSI_DATA appears on the serial console;
  2. len matches the expected format;
  3. RSSI updates continuously;
  4. 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:

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:

  1. save one raw official CSV line as a format fixture;
  2. detect the header or the type/seq column;
  3. validate len against the data element count;
  4. 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:

Split train/test by session or date. Do not randomly separate overlapping windows.

8. Realistic capability boundary

Good uses:

Do not claim by default:

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:

  1. run single-board csi_recv_router;
  2. save one raw CSV line and verify len versus data;
  3. compute actual packet rate and maximum gap;
  4. capture five minutes of empty room and five minutes of walking;
  5. plot RSSI, CSI amplitude and motion envelope;
  6. 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

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.

  1. 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."
  2. 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)
  3. 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.
  4. 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)
  5. 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)