Crates & Libraries
The esp-csi-rs ecosystem is a collection of Rust crates for Espressif SoCs
that build upon the esp-hal and esp-radio crates to provide a no_std CSI
data collection and logging suite. esp-csi-rs provides both a low-level API
for building your own CSI-related applications, as well as a variety of
out-of-the-box tools for logging, visualizing, and controlling CSI data
collection.
In this chapter, we will walk through the crates that make up the esp-csi-rs
ecosystem, their use case, their role in the csi-rs data collection pipeline,
and later on in the
ecosystem walkthrough
chapter, we will provide a hands-on example of getting started with CSI data
collection to tie together all of the tools and concepts discussed.
Two Roles: Emitter and Collector
Before any of the crates make sense, one piece of vocabulary has to be settled, because every crate in the ecosystem uses it and it is the thing most likely to confuse a reader coming from older CSI tooling.
A node in a CSI collection network is described by four independent attributes. They are independent because they vary independently — the node that keeps a channel busy and the node that produces the dataset need not be the same one, and usually are not.
| Attribute | Values | Question it answers |
|---|---|---|
| Network role | Central, Peripheral | Who sources the traffic? |
| Collection mode | Collector, Listener | Do this node’s measurements leave it? |
| Operational mode | six, below | How does it reach the channel? |
| Session role | Initiator, Responder | Who starts and stops the run? |
Older CSI tooling — and older versions of these crates — collapsed the first two into a single binary: active/passive, transmitter/receiver, emitter/collector. That loses configurations a deployment actually uses. A node that both sources traffic and measures it has no name; neither does a node that measures without reporting, which is what you want when you are separating the cost of acquiring CSI from the cost of delivering it.
| Operational mode | How the node participates |
|---|---|
| Wi-Fi sniffer | Promiscuous capture on a locked channel; the traffic is already there |
| Wi-Fi station | Associates to an AP or a commercial router |
| Wi-Fi access point | Self-contained softAP with DHCP; associated stations generate uplink |
| Emitter | Unassociated transmit-only sounding; needs no peer and no handshake |
| ESP-NOW | Connectionless symmetric exchange; either end can collect |
| ESP-NOW simplex | Connectionless asymmetric; one end floods, the other only measures |
Not every mode admits every combination — a sniffer never transmits, so it cannot be a central — and the combinations a mode does not admit are not offered. Where an attribute is fixed there is no setter to call, so a central sniffer or a collecting emitter cannot be built. That is why you will not find a “role” argument on most constructors: the mode carries it.
The model is written down once, in
esp-csi-rs/docs/network-model.md,
which also covers how it relates to IEEE 802.11bf and what the firmware cannot enforce. This book
does not restate it.
On two renames you will run into. “Emitter” used to be described as a role, opposite “collector”. It is an operational mode: an emitter’s node is a central listener, which is the description the two-role vocabulary had no way to express. And the delivery switch was once
collection-mode, with valuescollectorandlistener; it becameset-csi-output/POST /config/csi-output. If you find--mode=listenerin an older script, that is its replacement — but note it had no effect on firmware beforeesp-csi-rs0.11, which stored the flag without reading it.
The Pipeline, and Its Three Tiers
The crates divide cleanly by where they run and how much they decide for you. This book refers to those divisions as tiers:
- Tier 0 — the library on the device.
esp-csi-rs. You write the firmware; the crate handles the radio, the capture path, and the output format. - Tier 1 — ready-made firmware on the device.
esp-csi-cli-rs,esp-csi-litetui-rs,esp-csi-litegui-rs. Flash a binary, configure it over serial or a touchscreen, and collect without writing embedded Rust. - Tier 2 — host-side tooling.
csi-webserver,csi-webserver-core,csi-webclient. These run on your computer, take the stream off the serial port, and turn it into something you can drive, watch, and store.
Every tier above 0 is built on the one below it, and none of them is mandatory:
TIER 0 TIER 1 TIER 2
┌──────────────┐ ┌──────────────────┐ ┌────────────────────┐
│ esp-csi-rs │───▶│ esp-csi-cli-rs │─────▶│ csi-webserver │
│ │ │ (serial console)│ USB │ HTTP + WebSocket │
│ radio, roles │ └──────────────────┘ │ Parquet dumps │
│ capture path │ ┌──────────────────┐ └─────────┬──────────┘
│ log formats │───▶│ esp-csi-litetui │ │
│ │ │ esp-csi-litegui │ ▼
└──────────────┘ │ (on-device UI) │ ┌────────────────────┐
│ └──────────────────┘ │ csi-webclient │
│ │ │ plots, export │
▼ ▼ └────────────────────┘
your own firmware SD card / screen
Inside a Tier 0 device, a captured packet travels a short and deliberately gated path. Knowing its shape explains most of the configuration surface you will meet later:1
esp-radio Wi-Fi driver (CSI event, Wi-Fi task context)
└─ capture_csi_info() [hot path]
├─ statistics counters (always, `statistics` feature)
├─ CSI output enabled? ── no ─▶ return (the master gate)
├─ build CSIDataPacket (~640 B, stack; per-chip shape)
├─ delivery mode:
│ Off → nothing
│ Callback → your fn(&CSIDataPacket), inline
│ Async → lock-free queue + waker, drained by CSINodeClient
└─ inline logging enabled? → serial output in the configured LogMode
The three gates are independent, which is what makes combinations like “process every packet on-device but do not flood the serial port” a matter of setting two flags rather than rebuilding the firmware.
Supported Espressif SoCs
The capabilities of your sensing network are strictly bound by the RF hardware
of the specific SoC. csi-rs supports the following chips, your ideal choice
depends on your use case and budget:
ESP32 Series:
- ESP32:
- 2.4 GHz Wi-Fi 4 (802.11n), Dual-core Xtensa.
ESP32-C Series:
- ESP32-C3:
- 2.4 GHz Wi-Fi 4, Single-core RISC-V.
- ESP32-C5:
- Dual-band (2.4/5 GHz) Wi-Fi 6 (802.11ax), Single-core RISC-V.
- The only dual-band part in this list. That matters for sensing: the 5 GHz band is where the wider channels live, and the two bands see the same room differently, so it is a choice worth having.
- ESP32-C6:
- 2.4 GHz Wi-Fi 6 (802.11ax), Single-core RISC-V. It also carries 802.15.4 (Thread/Zigbee), which the CSI path does not use.
ESP32-S Series:
- ESP32-S3:
- 2.4 GHz Wi-Fi 4, Dual-core Xtensa with vector SIMD support.
For more information on the ESP SoCs naming conventions, check out this espressif blog post
Every supported chip can act as a collector. Emitter support depends on the part
and on the esp-radio version you build against, so if you are buying
specifically to run an emitter, check the current status in the
esp-csi-rs repository rather than
relying on this page.
Summary
In this chapter we looked at:
- The four independent attributes that describe a node, and why collapsing the first two into a single binary loses configurations deployments actually use.
- The six operational modes, and why the combinations a mode does not admit are not offered rather than merely documented.
- The
collection-mode→csi-outputrename, and why it happened. - The three tiers the ecosystem’s crates fall into, and how a CSI packet travels from the radio to your host.
- Which Espressif parts are supported, and which of them can act as an emitter.
The sections that follow take each crate in turn, starting at Tier 0.
-
Adapted from
specs/crate_architecture_spec.md§5 in the esp-csi-rs repository. ↩