Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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

CommandPurposeApplies
help [command]Help text, per command
set-wifiMode, credentials, channel, peer, emitterNext start
set-csiCSI acquisition flags (varies by chip)Next start
set-trafficTraffic generator frequencyNext start
set-csi-outputDeliver captured CSI, or capture silentlyNext start
set-csi-filterRestrict which frames are deliveredNext start
set-log-modeOutput formatImmediate
set-csi-deliveryDelivery mode and the inline log gateImmediate
set-protocolWi-Fi PHY protocolNext start
set-io-tasksToggle the TX and RX direction tasksNext start
set-rateRecord a PHY rateSee below
startBegin collection
show-configPrint the staged configuration
show-statsRuntime counter snapshot
reset-configRestore defaultsNext start
restartSoftware resetImmediate
infoFirmware 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:

ModeRole
sniffer (default)Collector, promiscuous capture path
stationCollector, associated to an AP
wifi-apCollector, self-contained softAP
ht20-emitterEmitter at 20 MHz
ht40-emitterEmitter at 40 MHz
esp-now-centralESP-NOW initiator
esp-now-peripheralESP-NOW responder
esp-now-fast-collectorAsymmetric ESP-NOW, receive side
esp-now-fast-sourceAsymmetric ESP-NOW, transmit side

Supporting flags:

  • --sta-ssid, --sta-password — station credentials.
  • --ap-ssid (default esp-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=on instead sends one frame back-to-back to every station each tick, for time-aligned multi-receiver CSI. Burst airtime is frequency-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 for ht40-emitter.
  • --inject-period-ms=<ms> — emitter frame period, default 20 ms.
  • --emitter-iface=<sta|ap> — which interface injects, default sta.

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 is array-list.
  • set-csi-output --enabled=<true|false> is the master delivery gate. With false the radio keeps capturing and its timing is unchanged, but nothing is decoded, logged, or handed onward. This is the command that replaced the old set-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:

  • defmt does 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.
  • serialized is the densest format and the one the host tools use. Reach for text when a human is reading and for esp-csi-tool when 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.

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.