esp-csi-cli-rs is a command-line interface (CLI) application that runs on top of the esp-csi-rs crate. esp-csi-cli-rs provides a user friendly interface for configuring and collecting Wi-Fi Channel State Information (CSI) on ESP devices. It allows users to configure various parameters related to CSI data collection.
In order to use this crate, you would need to flash the source code for your target device. Currently supported devices include:
- ESP32
- ESP32-C3
- ESP32-C5
- ESP32-C6
- ESP32-S3
- Multiple Wi-Fi Modes: Configure the ESP device as a Station, Sniffer, ESP-NOW Central, or ESP-NOW Peripheral.
- Traffic Generation: Generate traffic at configurable intervals.
- Fine-grained CSI Control: Enable or disable specific CSI features like LLTF, HTLTF, STBC HTLTF, and LTF Merge.
- PHY Rate / IO Task Control: Pin the ESP-NOW PHY rate and toggle TX or RX direction tasks at the CLI.
- Runtime Delivery Switching: Flip CSI delivery between async-queued, inline callback, and off without re-flashing.
- Statistics Snapshot:
show-statsreports PPS, drops, and one/two-way ESP-NOW latency on demand. - Collection Mode: Switch the node between Collector and Listener roles at runtime.
- Flexible Log Format: Choose between human-readable text, compact array-list, binary serialized, or ESP-CSI-Tool-compatible CSV output.
- CLI Control: Interact with the device using simple commands over a serial connection.
- Early Stop: Press
qto abort a running collection — even an indefinite one — without resetting the board. - Configuration Management: Show the current configuration or reset to defaults.
- Timed Collection: Start CSI collection for a specific duration or run indefinitely.
- Flexible Logging: Supports standard
println!or the more efficientdefmtlogging.
- Hardware: An ESP development board (ESP32, ESP32-C3, ESP32-C6, or ESP32-S3).
- Rust with ESP target support — full setup guide available here.
espflashfor flashing and monitoring — installation instructions available here.espflashalso supportsdefmtlog decoding out of the box.
-
Clone the repository:
git clone https://github.com/csi-rs/esp-csi-cli-rs cd esp-csi-cli-rs -
Build & Flash using the provided Cargo aliases — one command builds, flashes, and opens the monitor:
Device println(default)defmtESP32 cargo esp32cargo esp32-defmtESP32-C3 cargo esp32c3cargo esp32c3-defmtESP32-C5 cargo esp32c5cargo esp32c5-defmtESP32-C6 cargo esp32c6cargo esp32c6-defmtESP32-S3 cargo esp32s3cargo esp32s3-defmtTo build without flashing, append
-buildto any of the above (e.g.cargo esp32c6-build,cargo esp32s3-defmt-build, etc.).The
*-defmtvariants automatically: drop theprintlnfeature, enabledefmt, append-Tdefmt.xto the linker script set, and pass--log-format defmttoespflashso the monitor decodes binary log frames. No.cargo/config.tomlediting required.📝 The plain aliases default to
printlnlogging. Fordefmtbuilds use the parallel*-defmt(run + flash + monitor with frame decoding) and*-defmt-build(build only) aliases — they swap in the right features, runner, and linker script automatically. See EnablingdefmtLogging for details.Custom builds — if you need finer control over features, you can invoke
cargo builddirectly. The full set of available features is:Feature Description esp32Target: ESP32 esp32c3Target: ESP32-C3 esp32c5Target: ESP32-C5 esp32c6Target: ESP32-C6 (WiFi 6) esp32s3Target: ESP32-S3 printlnLog via println!(default)defmtLog via defmt(efficient binary logging)autoAuto-select JTAG or UART backend at runtime (default) async-printNon-blocking async logging — unstable, use with caution statisticsExpose runtime PPS/latency/drop counters via show-stats(default)jtag-serialForce JTAG serial backend uartForce UART backend # Example: ESP32-C6 with defmt, forced JTAG backend cargo build --no-default-features --features "no-std,esp32c6,defmt,jtag-serial" \ --target riscv32imac-unknown-none-elf --release
-
Monitor (if you used a
-buildalias or a manualcargo build): Connect your ESP device over USB and run:espflash flash --monitor
For
defmtbuilds, pass the ELF file to enable log decoding:espflash flash --monitor --log-format defmt
📝
defmtbuilds require a monitoring tool capable of interpretingdefmtencoding, such asespflash. Without it you will observe garbled output. The monitor requires the original ELF to decode incoming log frames.
🛑 Flashing is only required once. After disconnecting and reconnecting the device, run
espflash monitorthen pressctrl+Rto reset.
🛑 If you encounter strange behaviour with the CLI, press
ctrl+Rto reset the device. Pressctrl+Cto terminate the session — you will need to runespflash monitoragain to reconnect.
This is a list of commands available through the CLI interface:
📝 The
set-csicommand options differ on the ESP32-C5 and ESP32-C6 (which expose the HE/STBC field set instead of the classic LLTF/HTLTF flags).
-
help [command]- Description: Display the main help menu or details for a specific command.
- Example:
help set-wifi
-
set-traffic [OPTIONS]- Description: Configure traffic generation parameters.
- Options:
--frequency-hz=<NUMBER>: Specify the traffic frequency in Hertz (default: 100). Set to0to disable traffic generation.
- Examples:
set-traffic --frequency-hz=10set-traffic --frequency-hz=0
-
set-collection-mode [OPTIONS]- Description: Set the CSI node collection role.
- Options:
--mode=<collector|listener>:collectoractively generates and collects CSI data (default).listenerpassively receives CSI data only.
- Examples:
set-collection-mode --mode=collectorset-collection-mode --mode=listener
-
set-log-mode [OPTIONS]- Description: Set the CSI output logging format at runtime.
- Options:
--mode=<text|array-list|serialized|esp-csi-tool>: Output format for CSI packets (default:text).text: Verbose human-readable output with full metadata.array-list: Compact CSV-style array, one line per packet — best for host-side data processing.serialized: Binary COBS-framed postcard format — most compact, requires a compatible deserializer on the host.esp-csi-tool: Hernandez-style 26-column CSV (CSI_DATA,...lines) compatible with the ESP32-CSI-Tool collector.
- Examples:
set-log-mode --mode=textset-log-mode --mode=array-listset-log-mode --mode=esp-csi-tool
-
set-csi [OPTIONS]- Description: Configure CSI feature flags.
- Options (ESP32, ESP32-C3, ESP32-S3):
--disable-lltf: Disable LLTF CSI (default: enabled).--disable-htltf: Disable HTLTF CSI (default: enabled).--disable-stbc-htltf: Disable STBC HTLTF CSI (default: enabled).--disable-ltf-merge: Disable LTF Merge CSI (default: enabled).
- Options (ESP32-C5, ESP32-C6):
--disable-csi: Disable acquisition of CSI entirely.--disable-csi-legacy: Disable L-LTF acquisition for 11g PPDUs.--disable-csi-ht20: Disable HT-LTF for HT20 PPDUs.--disable-csi-ht40: Disable HT-LTF for HT40 PPDUs.--disable-csi-su: Disable HE-LTF for HE20 SU PPDUs.--disable-csi-mu: Disable HE-LTF for HE20 MU PPDUs.--disable-csi-dcm: Disable HE-LTF for HE20 DCM PPDUs.--disable-csi-beamformed: Disable HE-LTF for HE20 Beamformed PPDUs.--csi-he-stbc=<0-2>: STBC HE LTF selection (default: 2).--val-scale-cfg=<0-3>: Value scale configuration (default: 2).
- Examples:
set-csi --disable-lltf --disable-ltf-mergeset-csi --disable-csi-legacy --csi-he-stbc=1
-
set-wifi [OPTIONS]- Description: Configure WiFi and network settings. Note: SSIDs/passwords with spaces should be wrapped in single or double quotes (e.g.
--sta-ssid='My Network'or--sta-ssid="My Network"). Both quote styles are interchangeable. Underscores (_) are passed through literally. - Options:
--mode=<station|sniffer|esp-now-central|esp-now-peripheral>: Specify WiFi operation mode (default:sniffer).--sta-ssid=<SSID>: Set the SSID for Station mode.--sta-password=<PASSWORD>: Set the password for Station mode.--set-channel=<NUMBER>: Set the WiFi channel (default: 1).
- Examples:
set-wifi --mode=sniffer --set-channel=6set-wifi --mode=station --sta-ssid="My Network" --sta-password="my password"set-wifi --mode=esp-now-central
- Description: Configure WiFi and network settings. Note: SSIDs/passwords with spaces should be wrapped in single or double quotes (e.g.
-
start [OPTIONS]- Description: Start the CSI collection process. Ensure the device is configured first. Press
q(orQ) on the serial console at any time to stop collection early. - Options:
--duration=<SECONDS>: Specify the duration in seconds. If omitted, collection runs indefinitely.
- Examples:
startstart --duration=120
- Description: Start the CSI collection process. Ensure the device is configured first. Press
-
show-config- Description: Display the current configuration settings for all parameters.
- Example:
show-config
-
reset-config- Description: Reset all configurations to their default values.
- Example:
reset-config
-
set-rate [OPTIONS](ESP-NOW only)- Description: Pin the Wi-Fi PHY rate used by ESP-NOW central / peripheral nodes. Sniffer and station modes ignore this and derive their rate from the surrounding radio configuration.
- Options:
--rate=<NAME>: One ofmcs0-lgi(default),mcs1-lgi..mcs7-lgi,mcs0-sgi,1m,2m,5m5,11m,6m,9m,12m,18m,24m,36m,48m,54m.
- Examples:
set-rate --rate=mcs0-lgiset-rate --rate=24m
-
set-io-tasks [OPTIONS]- Description: Toggle the TX and/or RX direction tasks. Useful for asymmetric topologies — disabling RX makes the node a pure transmitter (skips the WiFi-callback CSI path); disabling TX makes it a pure receiver (no traffic generation).
- Options:
--tx=<on|off>: Enable or disable the TX task. Omit to keep the current state.--rx=<on|off>: Enable or disable the RX task. Omit to keep the current state.
- Examples:
set-io-tasks --tx=off(listener-only)set-io-tasks --tx=on --rx=on(default)
-
set-csi-delivery [OPTIONS]- Description: Switch the CSI delivery mode at runtime, and independently toggle the inline UART/JTAG log gate. The two delivery paths are mutually exclusive — the WiFi callback only ever pays for one per packet.
- Options:
--mode=<off|callback|async>:offdrops user delivery,callbackinvokes the registeredset_csi_callbackhook inline in the WiFi callback,asyncqueues toCSINodeClient::next_csi_packet(default for the indefinite collection path).--logging=<on|off>: Toggle the per-packetlog_csiUART/JTAG gate independently.
- Examples:
set-csi-delivery --mode=asyncset-csi-delivery --mode=off --logging=off
-
info- Description: Print a machine-parseable firmware identification block. Intended for host-side tooling that needs to verify which firmware is running on the device. The first line —
ESP-CSI-CLI/<version>— is also emitted at the top of the welcome banner on every reset, so a host can identify the firmware passively without sending this command. - Output format:
ESP-CSI-CLI/<version> name=esp-csi-cli-rs version=<version> chip=<esp32|esp32c3|esp32c5|esp32c6|esp32s3|unknown> protocol=<u32> features=<comma-separated-list> END-INFO - Example:
info
- Description: Print a machine-parseable firmware identification block. Intended for host-side tooling that needs to verify which firmware is running on the device. The first line —
-
show-stats(requiresstatisticsfeature, on by default)- Description: Print a one-shot snapshot of runtime CSI / traffic counters: RX/TX packet totals, average PPS, RX/TX rate in Hz, RX dropped packets, one-way and two-way ESP-NOW latency. Counters reset on the start of each new
startcollection. - Example:
show-stats
- Description: Print a one-shot snapshot of runtime CSI / traffic counters: RX/TX packet totals, average PPS, RX/TX rate in Hz, RX dropped packets, one-way and two-way ESP-NOW latency. Counters reset on the start of each new
-
Configure an ESP as a WiFi Sniffer on channel 6 and collect indefinitely in array-list format:
set-wifi --mode=sniffer --set-channel=6 set-log-mode --mode=array-list show-config start -
Configure an ESP as a Station connected to an existing network and collect for 5 minutes:
set-wifi --mode=station --sta-ssid="My Router" --sta-password="router password" set-traffic --frequency-hz=20 show-config start --duration=300 -
Configure an ESP as an ESP-NOW Central node in listener mode and collect for 2 minutes:
set-wifi --mode=esp-now-central set-collection-mode --mode=listener set-log-mode --mode=array-list show-config start --duration=120 -
ESP-NOW pair: pin the PHY rate and disable TX on the listener node, then check stats mid-run:
set-wifi --mode=esp-now-peripheral set-rate --rate=mcs0-lgi set-io-tasks --tx=off set-csi-delivery --mode=async --logging=on start # ... in another window or after pressing 'q' to stop: show-stats -
Emit ESP32-CSI-Tool-compatible CSV for a host pipeline:
set-wifi --mode=sniffer --set-channel=6 set-log-mode --mode=esp-csi-tool start --duration=60
💡 SSIDs and passwords with spaces can be passed as quoted strings to
set-wifi. Both quote styles work —--sta-ssid='My WiFi'and--sta-ssid="My WiFi"are equivalent — so you can pick whichever your terminal/keyboard makes easier to type. Underscores (_) are passed through literally.
🛑 To stop a running collection early — including indefinite runs started without
--duration— pressq(orQ) on the serial console.
This application can use either the standard println! macros or the defmt framework for logging. defmt produces compact binary frames that the host (espflash, probe-rs, etc.) decodes against the original ELF, so it's both faster on the device and richer on the host.
The recommended way is the *-defmt / *-defmt-build cargo aliases — pick the one matching your chip:
cargo esp32c6-defmt # build + flash + monitor with defmt decoding
cargo esp32c6-defmt-build # build only, skip flashingEach defmt alias automatically:
- drops
printlnfrom the default features and enablesdefmt, - appends
-Tdefmt.xto the linker script set (so the.defmtELF section is emitted), - swaps
espflash's runner toespflash flash --monitor --log-format defmtso log frames are decoded inline.
No edits to .cargo/config.toml are required — the aliases pass everything through cargo --config overrides at invocation time. If you want to invoke cargo build directly (e.g. in CI), the equivalent is:
cargo build --release \
--no-default-features \
--features esp32c6,defmt,no-std,auto,statistics \
--target riscv32imac-unknown-none-elf \
--config 'target.riscv32imac-unknown-none-elf.rustflags=["-C", "link-arg=-Tdefmt.x"]'Xtensa targets (esp32, esp32s3) need the -Wl, prefix on the link arg because their toolchain goes through a GCC linker driver:
--config 'target.xtensa-esp32s3-none-elf.rustflags=["-C", "link-arg=-Wl,-Tdefmt.x"]'This CLI is built around the esp-csi-rs crate. You can find full documentation for esp-csi-rs on docs.rs.
This crate is still in early development and currently supports no-std only. Contributions and suggestions are welcome!
Copyright 2026 The csi-rs Team
Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
Made with 🦀 for ESP chips
