Driving the CLI
Getting Started flashed esp-csi-cli-rs and ran a
capture on the defaults. This section uses the rest of it: the real
configuration surface, the two-board pairings, and how to tell on hardware
whether a pairing is actually working.
Everything here happens over the serial console. The command reference lives in
the
esp-csi-cli-rs section;
this is the operating guide.
The One Rule
Most commands stage configuration; they do not apply it. They write into an
in-memory UserConfig, and the next start builds a node from it. Only
set-log-mode, restart, and set-csi-delivery take effect immediately.
This explains most confusion at the console. If a change appears to do nothing,
it is because you have not restarted the session. show-config prints the
staged state; reset-config restores defaults.
A Single-Board Capture, Configured
Ambient sniffing on a channel your own network uses, in a format worth parsing:
reset-config
set-wifi --mode=sniffer --set-channel=6
set-protocol --protocol=n
set-traffic --frequency-hz=0
set-log-mode --mode=array-list
show-config
start
Two choices there are deliberate. --frequency-hz=0 disables the traffic
generator, because a sniffer has nothing to generate traffic for — it is
measuring what already exists. And array-list is one line per packet instead
of text’s twenty-odd, which matters as soon as the rate rises.
Emitter and Collector
This is the arrangement most sensing work is built on, and the reason to own two boards. One sounds the channel at a known rate and bandwidth; the other measures it. Nothing associates, so there is no handshake to fail.
Collector board — lock the emitter’s channel and measure everything overheard:
set-wifi --mode=sniffer --set-channel=6
set-traffic --frequency-hz=0
set-log-mode --mode=array-list
start
Emitter board — 20 MHz injection every 20 ms, so roughly 50 frames a second:
set-wifi --mode=ht20-emitter --set-channel=6 --inject-period-ms=20
start
Use --mode=ht40-emitter for 40 MHz.
A single emitter sounds every collector in range at once, and adding collectors
costs the emitter nothing. Going the other way, several emitters can share one
collector: each frame carries its transmitter’s MAC, so the collector attributes
measurements by source. Unicasting to one collector with --peer-mac tends to
raise that collector’s CSI rate noticeably compared to broadcasting.
Cleaning Up the Capture
Out of the box, that collector reports CSI for everything it decodes — your AP’s beacons and ACKs, the association exchange, and any third-party device on the channel — mixed in with your emitter’s frames. Those rows are valid CSI, but they look wrong next to the ones you want: the leading field is the frame’s own 802.11 sequence number, which is per-transmitter and so neither starts at zero nor shares a counter with your traffic, and a legacy-rate frame carries the shorter L-LTF-only payload.
Two commands fix this, and both are worth applying by default:
set-csi-filter --peer-mac=aa:bb:cc:dd:ee:ff
set-csi --csi-legacy=off --csi-ht20=off --csi-ht40=on --dump-ack=off
The first delivers CSI only from your emitter’s MAC —
set-csi-filter --min-phy=ht is the looser version, keeping 802.11n and better
while dropping legacy-rate management and control frames. The second restricts
what the radio acquires at all. On classic parts (ESP32, C3, S3) set-csi takes
--lltf, --htltf, --stbc-htltf, and --ltf-merge instead.
Filtering on the device rather than on the host returns console bandwidth to the
traffic you asked for: a rejected frame is dropped in the Wi-Fi callback before
the packet copy and before any formatting. Rejected frames are still counted in
show-stats as RX drops, so the gap between captured and delivered stays
visible rather than unexplained.
Confirming HT40 Actually Engaged
If you configured ht40-emitter and the collector’s rows still show about 53
subcarriers, HT40 did not engage. Check the collector’s csi_data_len: 100 or
more (commonly ~117) confirms 40 MHz; ~53 or ~56 means it fell back to legacy
or HT20.
The usual causes, in order of likelihood:
- Legacy and ACK acquisition still on. Those reports arrive at ambient
rates and swamp the HT40 ones. Apply the
set-csiline above. - No room in the band.
--ht40=aboveon channel 7 occupies up to channel 11 and--ht40=belowoccupies down to channel 3. A primary too close to the band edge silently falls back. - Channel mismatch. The two boards are not on the same primary channel.
The Associated Link
When you want CSI from an ordinary Wi-Fi link rather than from blind sounding, pair a softAP collector with a station.
Board A, the AP collector:
reset-config
set-wifi --mode=wifi-ap --set-channel=6 --ap-ssid=esp-csi-ap
set-protocol --protocol=n
set-traffic --frequency-hz=4000
set-log-mode --mode=array-list
start
Board B, the station:
reset-config
set-wifi --mode=station --sta-ssid=esp-csi-ap --set-channel=6
set-protocol --protocol=n
set-traffic --frequency-hz=4000
set-log-mode --mode=array-list
start
CSI appears primarily on board A, from the station’s uplink replies.
The AP’s DHCP pool holds four leases by default, and with more than one lease
the traffic round-robins across all associated stations — so the pair scales to
several station boards with no extra configuration. --ap-leases=<1-8> sizes
the pool (1 is a single-target flood). The offered rate is shared: with N
stations, each sees roughly frequency-hz / N packets per second.
For time-aligned multi-receiver measurements, --ap-burst=on sends one frame
back-to-back to every station each tick instead of round-robining. Total airtime
then becomes frequency-hz × leases, so size it accordingly.
On a C5, the channel number selects the band. The default is 149, which is 5 GHz. Associating to a 2.4 GHz AP from that default reports only “no access point found” — pass an explicit 2.4 GHz channel.
ESP-NOW Pairs
Connectionless: no AP, no DHCP, no association, and both sides capture.
# Central board
set-wifi --mode=esp-now-central --set-channel=6
set-rate --rate=mcs0-lgi
set-log-mode --mode=array-list
start
# Peripheral board
set-wifi --mode=esp-now-peripheral --set-channel=6
set-rate --rate=mcs0-lgi
set-log-mode --mode=array-list
start
With more than two boards on one channel, set --peer-mac on both ends to pin
the pair explicitly rather than relying on magic-prefix discovery.
The asymmetric variant reaches the highest CSI rate of any pairing, because the collector stops transmitting once it hears a source and all airtime then belongs to one transmitter. Start the collector first:
# Collector board
set-wifi --mode=esp-now-fast-collector --set-channel=6
set-log-mode --mode=serialized
start
# Source board
set-wifi --mode=esp-now-fast-source --set-channel=6
start
set-rate does not apply to this pair — the fast profile fixes its own PHY.
Reading the Counters
show-stats is the first thing to reach for whenever a capture looks wrong. It
reports packets transmitted and received, rates in Hz, and drop counts.
Interpreting it is mostly one distinction. If the received count tracks your
emitter’s rate but the rows arriving on your terminal are fewer, the bottleneck
is the console or the async logging queue, not the radio — switch to a denser
format (array-list, or serialized) or raise the emitter’s period. If the
received count itself is low, the radio genuinely is not hearing the emitter,
and the problem is channel, band, distance, or acquisition configuration.
Drop counts also include frames rejected by set-csi-filter, which is
intentional: it keeps the difference between captured and delivered visible
rather than making filtered frames vanish silently.
Where to Go Next
Driving two boards from two serial terminals stops scaling quickly. The next section puts a server in front of them, so configuration, control, and recording happen in one place: Streaming to a Host.