Expand description
§A crate for CSI collection on ESP devices
§Overview
This crate builds on the low level Espressif abstractions to enable the collection of Channel State Information (CSI) on ESP devices with ease.
Currently this crate supports only the ESP no-std development framework.
§Choosing a device
In terms of hardware, you need to make sure that the device you choose supports WiFi and CSI collection. Currently supported devices include:
- ESP32
- ESP32-C2
- ESP32-C3
- ESP32-C6
- ESP32-S3
In terms of project and software toolchain setup, you will need to specify the hardware you will be using. To minimize headache, it is recommended that you generate a project using esp-generate as explained next.
§Creating a project
To use this crate you would need to create and setup a project for your ESP device then import the crate. This crate is compatible with the no-std ESP development framework. You should also select the corresponding device by activating it in the crate features.
To create a projects it is highly recommended to refer the to instructions in The Rust on ESP Book before proceeding. The book explains the full esp-rs ecosystem, how to get started, and how to generate projects for both std and no-std.
Espressif has developed a project generation tool, esp-generate, to ease this process and is recommended for new projects. As an example, you can create a no-std project for the ESP32-C3 device as follows:
cargo install esp-generate
esp-generate --chip=esp32c3 [project-name]§Feature Flags
⚠️ At minimum you are required to choose only ONE of the device options. For the most efficient (and fastest) logging, you should combine defmt with the jtag-serial features.
no-std(enabled by default) — Build without the standard library.println(enabled by default) — Enable logging viaprintln!().auto(enabled by default) — Automatically select the best available backend for the target.async-print(enabled by default) — Non-blocking async logging. This is an unstable feature and may cause unexpected behavior. Use with caution.defmt— Enable logging via defmt.jtag-serial— Force the JTAG backend for logging.uart— Force the UART backend for logging (not recommended, much slower than JTAG backend).no-print— Disable all logging.statistics— Enable collection of runtime statistics (e.g., latency, dropped packets).esp32— Support for the ESP32esp32c2— Support for the ESP32-C2esp32c3— Support for the ESP32-C3esp32c6— Support for the ESP32-C6 (WiFi 6)esp32s3— Support for the ESP32-S3 (AIoT)
§Using the Crate
Each ESP device is represented as a node in a collection network. For each node, we need to configure its role in the network, the mode of operation, and the CSI collection behavior. The node role determines how the node participates in the network and interacts with other nodes, while the collection mode determines how the node handles CSI data.
§Node Roles
- Central Node: This type of node is one that generates traffic, also can connect to one or more peripheral nodes.
- Peripheral Node: This type of node does not generate traffic, also can optionally connect to one central node at most.
§Node Operation Modes
The operation mode determines how the node operates in terms of Wi-Fi features and interactions with other nodes. The supported operation modes are:
- ESP-NOW
- Wi-Fi Station (Central only)
- Wi-Fi Sniffer (Peripheral only)
§Collection Modes
- Collector: A collector node collects and provides CSI data output from one or more devices.
- Listener: A listener is a passive node. It only enables CSI collection and does not provide any CSI output.
A collector node typically is the one that actively processes CSI data. A listener on the other hand typically keeps CSI traffic flowing but does not process CSI data.
§Collection Network Architechtures
As ahown earlier, esp-csi-rs allows you to configure a device to one several operational modes including ESP-NOW, WiFi station, or WiFi sniffer. As such, esp-csi-rs supports several network setups allowing for flexibility in collecting CSI data. Some possible setups including the following:
- Single Node: This is the simplest setup where only one ESP device (CSI Node) is needed. The node is configured to “sniff” packets in surrounding networks and collect CSI data. The WiFi Sniffer Peripheral Collector is the only configuration that supports this topology.
- Point-to-Point: This set up uses two CSI Nodes, a central and a peripheral. One of them can be a collector and the other a listener. Alternatively, both can be collectors as well. Some configuration examples include
- WiFi Station Central Collector <-> Access Point/Commercial Router: In this configuration the CSI node can connect to any WiFi Access Point like an ESP AP or a commercial router. The node in turn sends traffic to the Access Point to acquire CSI data.
- ESP-NOW Central Listener/Collector <-> ESP-NOW Peripheral Listener/Collector: In this configuration a CSI central node connects to one other ESP-NOW peripheral node. Both ESP-NOW peripheral and central nodes can operate either as listeners or collectors.
- Star: In this architechture a central node connects to several peripheral nodes. The central node triggers traffic and aggregates CSI sent back from peripheral nodes. Alternatively, CSI can be collected by the individual peripherals. Only the ESP-NOW operation mode supports this architechture. The ESP-NOW peripheral and central nodes can also operate either as listeners or collectors.
§Output Formats & Logging Modes
esp-csi-rs is able to print CSI data in several formats. The output format can be configured when initializing the logger. The supported formats include:
- LogMode::ArrayList: This prints CSI data as an array, where the array represents the CSI values for a received packet. This format is more compact and easier to read for large volumes of CSI data.
Example output:
[3916,-93,11,157,1,1815804,256,0,260,2,0,1,1,128,0,1,1,0,1,0,0,0,256,128,[...]]The array fields map to the CSIDataPacket struct fields in the following order:
| Index | Field | Description |
|---|---|---|
| 0 | sequence_number | Sequence number of the packet that triggered the CSI capture |
| 1 | rssi | Received Signal Strength Indicator (dBm) |
| 2 | rate | PHY rate encoding (valid for non-HT / 802.11b/g packets) |
| 3 | noise_floor | Noise floor of the RF module (dBm) |
| 4 | channel | Primary channel on which the packet was received |
| 5 | timestamp | Local timestamp when the packet was received (microseconds) |
| 6 | sig_len | Length of the packet including Frame Check Sequence (FCS) |
| 7 | rx_state | Reception state: 0 = no error, non-zero = error code |
| 8 | secondary_channel | Secondary channel: 0 = none, 1 = above, 2 = below (non-ESP32-C6 only) |
| 9 | sgi | Short Guard Interval: 0 = Long GI, 1 = Short GI (non-ESP32-C6 only) |
| 10 | antenna | Antenna number: 0 = antenna 0, 1 = antenna 1 (non-ESP32-C6 only) |
| 11 | ampdu_cnt | Number of subframes aggregated in AMPDU (non-ESP32-C6 only) |
| 12 | sig_mode | Protocol: 0 = non-HT (11b/g), 1 = HT (11n), 3 = VHT (11ac) (non-ESP32-C6 only) |
| 13 | mcs | Modulation Coding Scheme; for HT packets ranges from 0 (MCS0) to 76 (MCS76) (non-ESP32-C6 only) |
| 14 | bandwidth | Channel bandwidth: 0 = 20 MHz, 1 = 40 MHz (non-ESP32-C6 only) |
| 15 | smoothing | Channel estimate smoothing: 0 = unsmoothed, 1 = smoothing recommended (non-ESP32-C6 only) |
| 16 | not_sounding | Sounding PPDU flag: 0 = sounding PPDU, 1 = not a sounding PPDU (non-ESP32-C6 only) |
| 17 | aggregation | Aggregation type: 0 = MPDU, 1 = AMPDU (non-ESP32-C6 only) |
| 18 | stbc | Space-Time Block Code: 0 = non-STBC, 1 = STBC (non-ESP32-C6 only) |
| 19 | fec_coding | Forward Error Correction / LDPC flag; set for 11n LDPC packets (non-ESP32-C6 only) |
| 20 | sig_len | Packet length including FCS (repeated) |
| 21 | csi_data_len | Length of the raw CSI data (number of i8 samples) |
| 22 | [csi_data] | Inner array of raw CSI i8 samples |
- LogMode::Text: This output prints CSI data in a more verbose, human-readable format. This includes additional metadata and explanations alongside the raw CSI values, making it easier to understand the context of each packet’s CSI data.
Example output:
mac: 56:6C:EB:6F:BC:3D
sequence number: 426
rssi: -82
rate: 11
noise floor: 165
channel: 1
timestamp: 2424915
sig len: 332
rx state: 0
dump len: 336
he sigb len: 2
cur single mpdu: 0
cur bb format: 1
rx channel estimate info vld: 1
rx channel estimate len: 128
time seconds: 0
channel: 1
is group: 1
rxend state: 0
rxmatch3: 1
rxmatch2: 0
rxmatch1: 0
rxmatch0: 0
sig_len: 332
data length: 128
csi raw data: [0, 0, 0, 0, 0, 0, 0, 0, -6, 0, 6, 0, -24, 10, -23, 9, -23, 8, -23, 7, -22, 6, -22, 5, -22, 6, -23, 5, -22, 6, -22, 6, -22, 7, -20, 7, -19, 9, -19, 10, -19, 12, -19, 12, -18, 14, -19, 14, -19, 16, -20, 17, -21, 18, -20, 18, -19, 18, -16, 18, -14, 19, -13, 18, 0, 0, -19, 22, -20, 22, -20, 22, -20, 21, -21, 19, -22, 18, -20, 16, -18, 16, -17, 15, -16, 15, -14, 15, -13, 13, -12, 13, -9, 13, -7, 14, -6, 14, -5, 13, -3, 12, 0, 13, 2, 12, 3, 12, 5, 12, 7, 13, 8, 13, 10, 13, 12, 14, 9, 1, -5, -4, 0, 0, 0, 0, 0, 0]- LogMode::Serialized: This mode serializes the
CSIDataPacketstructure and prints it in a serialized COBS format. This is a compact binary format that can be parsed by and serde compatible crate like postcard. It is not human-readable but is efficient for logging large amounts of CSI data on the host without overwhelming the console output.
§Example for creating WiFi Station Central Collector
There are more examples in the repository. The example below demonstrates how to collect CSI data with an ESP configured in WIFI Station mode.
§Step 1: Initialize Logger
init_logger(spawner, LogMode::ArrayList);§Step 2: Create a Hardware Instance for the CSI Node
let csi_hardware = CSINodeHardware::new(&mut interfaces, controller);§Step 3: Create a Station Configuration
let client_config = ClientConfig::default()
.with_ssid("SSID".to_string())
.with_password("PASS".to_string())
.with_auth_method(esp_radio::wifi::AuthMethod::Wpa2Personal);
let station_config = WifiStationConfig {
client_config, // Pass the config we created above
};§Step 3: Create a CSI Collection Node Instance with the Desired Configuration
let mut node = CSINode::new(
esp_csi_rs::Node::Central(esp_csi_rs::CentralOpMode::WifiStation(station_config)),
CollectionMode::Collector,
Some(CsiConfig::default()),
Some(100),
csi_hardware,
);§Step 4: Create a CSI Node Client to Control the Node and Receive CSI Data
let mut node_handle = CSINodeClient::new();§Step 5: Create Handler for Printing for Certain Duration
node.run_duration(1000, &mut node_handle).await;Modules§
Macros§
- log_ln
- Logging macro that routes to
println!/defmtbased on features. - log_raw
- Log raw bytes without any added newline (blocking path only; no-op in async-print mode).
Structs§
- CSINode
- Primary orchestration object for CSI collection.
- CSINode
Client - Client helper to receive CSI packets via a pub/sub channel.
- CSINode
Hardware - Hardware handles required to operate a CSI node.
- Control
Packet - Control packet sent from Central to Peripheral.
- EspNow
Config - Configuration for ESP-NOW traffic generation.
- Peripheral
Packet - Peripheral reply packet for latency/telemetry exchange.
- Wifi
Sniffer Config - Configuration for Wi-Fi Promiscuous Sniffer mode.
- Wifi
Station Config - Configuration for Wi-Fi Station mode.
Enums§
- Central
OpMode - Central node operational modes.
- Collection
Mode - CSI collection behavior for the node.
- Node
- High-level node type and mode.
- Peripheral
OpMode - Peripheral node operational modes.
Functions§
- run_
process_ csi_ packet - Internal task that processes CSI packets from the pub/sub channel.