feat(adr-018): ESP32-S3 firmware, Rust aggregator, and live CSI pipeline

Complete end-to-end WiFi CSI capture pipeline verified on real hardware:

- ESP32-S3 firmware: WiFi STA + promiscuous mode CSI collection,
  ADR-018 binary serialization, UDP streaming at ~20 Hz
- Rust aggregator CLI binary (clap): receives UDP frames, parses with
  Esp32CsiParser, prints per-frame summary (node, seq, rssi, amp)
- UDP aggregator module with per-node sequence tracking and drop detection
- CsiFrame bridge to detection pipeline (amplitude/phase/SNR conversion)
- Python ESP32 binary parser with UDP reader
- Presence detection confirmed: motion score 10/10 from live CSI variance

Hardware verified: ESP32-S3-DevKitC-1 (CP2102, MAC 3C:0F:02:EC:C2:28),
Docker ESP-IDF v5.2 build, esptool 5.1.0 flash, 20 Rust + 6 Python tests pass.

Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
rUv
2026-02-28 13:22:04 -05:00
parent 885627b0a4
commit 92a5182dc3
22 changed files with 1786 additions and 169 deletions

View File

@@ -57,25 +57,27 @@ impl CsiFrame {
}
}
/// Metadata associated with a CSI frame.
/// Metadata associated with a CSI frame (ADR-018 format).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CsiMetadata {
/// Timestamp when frame was received
pub timestamp: DateTime<Utc>,
/// RSSI in dBm (typically -100 to 0)
pub rssi: i32,
/// Noise floor in dBm
pub noise_floor: i32,
/// WiFi channel number
pub channel: u8,
/// Secondary channel offset (0, 1, or 2)
pub secondary_channel: u8,
/// Channel bandwidth
/// Node identifier (0-255)
pub node_id: u8,
/// Number of antennas
pub n_antennas: u8,
/// Number of subcarriers
pub n_subcarriers: u16,
/// Channel center frequency in MHz
pub channel_freq_mhz: u32,
/// RSSI in dBm (signed byte, typically -100 to 0)
pub rssi_dbm: i8,
/// Noise floor in dBm (signed byte)
pub noise_floor_dbm: i8,
/// Channel bandwidth (derived from n_subcarriers)
pub bandwidth: Bandwidth,
/// Antenna configuration
/// Antenna configuration (populated from n_antennas)
pub antenna_config: AntennaConfig,
/// Source MAC address (if available)
pub source_mac: Option<[u8; 6]>,
/// Sequence number for ordering
pub sequence: u32,
}
@@ -143,13 +145,14 @@ mod tests {
CsiFrame {
metadata: CsiMetadata {
timestamp: Utc::now(),
rssi: -50,
noise_floor: -95,
channel: 6,
secondary_channel: 0,
node_id: 1,
n_antennas: 1,
n_subcarriers: 3,
channel_freq_mhz: 2437,
rssi_dbm: -50,
noise_floor_dbm: -95,
bandwidth: Bandwidth::Bw20,
antenna_config: AntennaConfig::default(),
source_mac: None,
sequence: 1,
},
subcarriers: vec![