esp-csi-cli-rs
esp-csi-cli-rs is the ready-made command-line tool for CSI collection on ESP32
series devices. Built directly on top of the esp-csi-rs crate, its primary
purpose is to provide easy configuration and control without requiring
developers to write custom embedded Rust code.
In the context of the csi-rs ecosystem, esp-csi-cli-rs occupies Tier 1.
While it is flashed directly onto the edge device, it operates at a higher
abstraction layer than the raw peripheral drivers found in Tier 0
(esp-csi-rs).
Its core operational function is to provide a streamlined interface for
extracting CSI data from the edge device and transmitting it to a host machine
via the serial port for downstream processing and storage. esp-csi-cli-rs acts
as the intermediary bridging esp-csi-rs and higher-level host-side
applications like csi-webserver and csi-webclient.
This section describes version 0.7.0. Note that esp-csi-cli-rs is firmware
rather than a library, so it is not published to crates.io — you get it from
GitHub, either as a prebuilt binary or by building from source.
Getting It onto a Board
The fastest route is a prebuilt binary. Tagged releases publish a per-chip
.bin alongside a manifest.json describing each artifact, including the baud
rate it was built for. Flash it with espflash and open the monitor:
espflash flash --monitor <chip>.bin
To build from source instead, clone the repository and use the per-chip cargo aliases, which build, flash, and open a monitor in one step:
git clone https://github.com/csi-rs/esp-csi-cli-rs && cd esp-csi-cli-rs
cargo esp32c6 # build + flash + monitor, println logging
cargo esp32c6-defmt # the same with defmt logging
cargo esp32c6-build # compile only, no flash
Replace esp32c6 with any of esp32, esp32c3, esp32c5, esp32s3. For a
fully explicit invocation:
cargo build --no-default-features \
--features "no-std,esp32c6,println,jtag-serial,statistics" \
--target riscv32imac-unknown-none-elf --release
Feature selection mirrors the library: one device feature, one logging backend
(println by default or defmt), a transport (auto by default,
jtag-serial, or uart), and statistics, which is on by default here
because show-stats depends on it.
When the board resets you will see the identification banner and the root menu:
ESP-CSI-CLI/0.7.0
mac=D0:CF:13:E2:90:E8
******* Welcome to the CSI Collection CLI utility! *******
That first line is the firmware identification contract described in the
previous section — it is how csi-webserver and
every other host tool recognises the board.
How the CLI Behaves
Three properties of the runtime are worth knowing before the command list, since they explain most of what surprises first-time users.
Configuration is staged, not live. The CLI holds a UserConfig in memory.
Most commands write into it and take effect on the next start, not
immediately. The exceptions are set-log-mode, restart, and
set-csi-delivery (other than its raw setting), which apply at once.
show-config prints the current staged configuration; reset-config restores
defaults.
start owns the session. It builds a node from the staged configuration and
runs it, either for --duration=<seconds> or indefinitely. While a session is
running, pressing q aborts it and returns to the root menu.
Quoting matters. SSIDs and passwords containing spaces must be wrapped in
single or double quotes — --sta-ssid='My WiFi' — and underscores pass through
as literal underscores rather than as space substitutes.
Command Reference
| Command | Purpose | Applies |
|---|---|---|
help [command] | Help text, per command | — |
set-wifi | Mode, credentials, channel, peer, emitter | Next start |
set-csi | CSI acquisition flags (varies by chip) | Next start |
set-traffic | Traffic generator frequency | Next start |
set-csi-output | Deliver captured CSI, or capture silently | Next start |
set-csi-filter | Restrict which frames are delivered | Next start |
set-log-mode | Output format | Immediate |
set-csi-delivery | Delivery mode and the inline log gate | Immediate |
set-protocol | Wi-Fi PHY protocol | Next start |
set-io-tasks | Toggle the TX and RX direction tasks | Next start |
set-rate | Record a PHY rate | See below |
start | Begin collection | — |
show-config | Print the staged configuration | — |
show-stats | Runtime counter snapshot | — |
reset-config | Restore defaults | Next start |
restart | Software reset | Immediate |
info | Firmware identification block | — |
The CLI’s own help <command> output is authoritative and always matches the
firmware you have flashed; the tables below summarise the flags you will reach
for most.
set-wifi
The command that decides what the node is. --mode takes one of nine values,
which map onto the roles from the
previous chapter:
| Mode | Role |
|---|---|
sniffer (default) | Collector, promiscuous capture path |
station | Collector, associated to an AP |
wifi-ap | Collector, self-contained softAP |
ht20-emitter | Emitter at 20 MHz |
ht40-emitter | Emitter at 40 MHz |
esp-now-central | ESP-NOW initiator |
esp-now-peripheral | ESP-NOW responder |
esp-now-fast-collector | Asymmetric ESP-NOW, receive side |
esp-now-fast-source | Asymmetric ESP-NOW, transmit side |
Supporting flags:
--sta-ssid,--sta-password— station credentials.--ap-ssid(defaultesp-csi-ap),--ap-password,--ap-dhcp=<on|off>,--ap-leases=<1-8>(default 4),--ap-burst=<on|off>. With more than one lease the ICMP traffic round-robins across associated stations;--ap-burst=oninstead sends one frame back-to-back to every station each tick, for time-aligned multi-receiver CSI. Burst airtime isfrequency-hz × leases.--set-channel=<n>— default 1, or 149 on the C5. On the C5 the channel number also selects the band, which is a common first stumble: associating to a 2.4 GHz AP from the 5 GHz default reports only “no access point found”.--peer-mac=<mac>— one field with two meanings. In emitter modes it is the destination of injected frames, and unicasting to a collector’s MAC usually raises that collector’s CSI rate; empty means broadcast. In ESP-NOW modes it is the explicit peer address.--ht40=<above|below|none>— the secondary channel forht40-emitter.--inject-period-ms=<ms>— emitter frame period, default 20 ms.--emitter-iface=<sta|ap>— which interface injects, defaultsta.
set-csi
The flags here are the CsiConfig fields, so the command has two variants
depending on your chip.
On the classic parts (ESP32, C3, S3) the flags are --lltf, --htltf,
--stbc-htltf, and --ltf-merge, each on or off and all defaulting to
on.
On the C5 and C6 they are --csi, --csi-legacy, --csi-ht20,
--csi-ht40, --dump-ack (each on/off), --val-scale-cfg=<0-3> (default
2), and --preset=<default>. The C5 additionally accepts --csi-force-lltf and
--csi-vht.
For a clean HT40 capture, turn the legacy and ACK acquisition off:
set-csi --csi-legacy=off --csi-ht20=off --csi-ht40=on --dump-ack=off
Leaving them on is the usual cause of a capture that stays stubbornly at ~53 subcarriers when HT40 was configured.
set-csi-filter
A collector is promiscuous: it reports CSI for every frame its radio decodes, including your AP’s beacons and ACKs and any third-party device on the channel. Those rows are valid CSI, but they look wrong next to your own traffic — the leading field of a row is the frame’s own 802.11 sequence number, which is per-transmitter and so neither starts at zero nor shares a counter with yours, and a legacy-rate frame carries the shorter L-LTF-only payload.
set-csi-filter --peer-mac=aa:bb:cc:dd:ee:ff
set-csi-filter --min-phy=ht
--peer-mac delivers CSI only for frames from that source (any clears it).
--min-phy=ht keeps 802.11n and better, dropping the legacy-rate management and
control frames.
Filtering on the device rather than on the host also 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.
set-log-mode, set-csi-output, set-csi-delivery
These three are the CLI’s face of the three independent gates described in Crates & Libraries.
set-log-mode --mode=<text|array-list|serialized|esp-csi-tool>chooses the output format. The firmware’s own default at boot isarray-list.set-csi-output --enabled=<true|false>is the master delivery gate. Withfalsethe radio keeps capturing and its timing is unchanged, but nothing is decoded, logged, or handed onward. This is the command that replaced the oldset-collection-mode --mode=listener.set-csi-delivery --mode=<off|callback|async> --logging=<on|off>selects the delivery path and independently gates the inline serial log.
set-traffic, set-io-tasks, set-protocol, set-rate
set-traffic --frequency-hz=<n> sets the traffic generator rate (default 100;
0 disables it). set-io-tasks --tx=<on|off> --rx=<on|off> toggles the two
direction tasks. set-protocol selects the Wi-Fi PHY protocol.
set-rate --rate=<rate> is the odd one out: it records a PHY rate that most
modes never apply. The emitter modes force their own PHY, and the fast ESP-NOW
modes ignore it; only the symmetric ESP-NOW central/peripheral pair acts on it.
Treat it as reporting metadata unless you are running that pair.
start, show-config, show-stats, info
start # run until 'q'
start --duration=60 # run for 60 seconds
show-config prints the staged configuration, which is also what a host tool
parses to learn a device’s current state. show-stats prints the runtime
counters — packets transmitted and received, rates in Hz, and drop counts — and
exists only when the firmware was built with the statistics feature, which is
on by default. info prints the identification block that host tooling keys on.
Console Throughput
A collector can capture faster than a serial console can carry the results, so the output format and the transport are throughput decisions, not cosmetic ones. Three things to know:
defmtdoes not raise the CSI rate by itself. It shrinks each line on the wire, which helps when the console is the bottleneck, but it does not make the radio capture more.- The baud rate is fixed at build time, which is why release artifacts
publish it in
manifest.json— a host that opens the port at the wrong rate sees garbage, not a slow stream. serializedis the densest format and the one the host tools use. Reach fortextwhen a human is reading and foresp-csi-toolwhen an existing analysis script is.
If a capture looks thinner than the rate you configured, show-stats is where
to start: a large drop count means the console or the async logging queue is the
limit, not the radio.
Relevant Links & Resources
User guide and source code can be viewed (and contributed to) on
GitHub. The full command
specification lives in specs/SPECS.md in that repository, and
specs/WEBSERVER.md documents the integration contract that host tooling
implements.