60 WASM modules that run directly on an ESP32 sensor. No internet needed, no cloud fees, instant response. Each module is a tiny file (5-30 KB) that reads WiFi signal data and makes decisions locally in under 10 ms.
# Build all modules for ESP32
cd rust-port/wifi-densepose-rs/crates/wifi-densepose-wasm-edge
cargo build --target wasm32-unknown-unknown --release
# Run all 632 tests
cargo test --features std
# Upload a module to your ESP32
python scripts/wasm_upload.py --port COM7 --module target/wasm32-unknown-unknown/release/module_name.wasm- WiFi signals bounce off people and objects in a room, creating a unique pattern
- The ESP32 chip reads these patterns as Channel State Information (CSI) — 52 numbers that describe how each WiFi channel changed
- WASM modules analyze the patterns to detect specific things: someone fell, a room is occupied, breathing rate changed
- Events are emitted locally — no cloud round-trip, response time under 10 ms
WiFi Router ──── radio waves ────→ ESP32-S3 Sensor
│
▼
┌──────────────┐
│ Tier 0-2 │ C firmware: phase unwrap,
│ DSP Engine │ stats, top-K selection
└──────┬───────┘
│ CSI frame (52 subcarriers)
▼
┌──────────────┐
│ WASM3 │ Tiny interpreter
│ Runtime │ (60 KB overhead)
└──────┬───────┘
│
┌───────────┼───────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Module A │ │ Module B │ │ Module C │
│ (5-30KB) │ │ (5-30KB) │ │ (5-30KB) │
└────┬─────┘ └────┬─────┘ └────┬─────┘
│ │ │
└───────────┼───────────┘
▼
Events + Alerts
(UDP to aggregator or local)
Every module talks to the ESP32 through 12 functions:
| Function | Returns | Description |
|---|---|---|
csi_get_phase(i) |
f32 |
WiFi signal phase angle for subcarrier i |
csi_get_amplitude(i) |
f32 |
Signal strength for subcarrier i |
csi_get_variance(i) |
f32 |
How much subcarrier i fluctuates |
csi_get_bpm_breathing() |
f32 |
Breathing rate (BPM) |
csi_get_bpm_heartrate() |
f32 |
Heart rate (BPM) |
csi_get_presence() |
i32 |
Is anyone there? (0/1) |
csi_get_motion_energy() |
f32 |
Overall movement level |
csi_get_n_persons() |
i32 |
Estimated number of people |
csi_get_timestamp() |
i32 |
Current timestamp (ms) |
csi_emit_event(id, val) |
— | Send a detection result to the host |
csi_log(ptr, len) |
— | Log a message to serial console |
csi_get_phase_history(buf, max) |
i32 |
Past phase values for trend analysis |
-
Create
src/your_module.rsfollowing the pattern:#![cfg_attr(not(feature = "std"), no_std)] #[cfg(not(feature = "std"))] use libm::fabsf; pub struct YourModule { /* fixed-size fields only */ } impl YourModule { pub const fn new() -> Self { /* ... */ } pub fn process_frame(&mut self, /* inputs */) -> &[(i32, f32)] { /* ... */ } }
-
Add
pub mod your_module;tolib.rs -
Add event constants to
event_typesinlib.rs -
Add tests with
#[cfg(test)] mod tests { ... } -
Run
cargo test --features std
- No heap allocation: Use fixed-size arrays, not
VecorString - No
std: Uselibmfor math functions - Budget tiers: L (<2ms), S (<5ms), H (<10ms) per frame
- Binary size: Each module should be 5-30 KB as WASM
- ADR-039 — Edge processing tiers
- ADR-040 — WASM runtime design
- ADR-041 — Full module specification
- Source code
