ruview @ v0.2.0-esp32
integrity
- size
- 107.5 MiB
- downloaded
- last checked
release notes
ESP32-S3 CSI Firmware v0.2.0
Pre-built firmware binaries for the ESP32-S3 CSI sensing pipeline.
Built from main with ESP-IDF v5.2 via Docker.
What's New (since v0.1.0)
- MAC address filtering — filter CSI frames by transmitter MAC to prevent signal mixing in multi-AP environments (#101)
- TDM sensing protocol — time-division multiplexing for multistatic mesh deployments (ADR-029/031)
- Channel hopping — configurable multi-channel scanning
- NVS runtime config — configure MAC filter, TDM slots, channel list, and dwell time without reflashing
What's Included
| File | Size | Flash Address | Description |
|---|---|---|---|
bootloader.bin |
21 KB | 0x0 |
ESP32-S3 second-stage bootloader |
partition-table.bin |
3 KB | 0x8000 |
Flash partition layout |
esp32-csi-node.bin |
789 KB | 0x10000 |
CSI firmware with MAC filter + TDM |
provision.py |
— | — | NVS provisioning script (WiFi + network + MAC filter config) |
Quick Start (No Build Required)
1. Install esptool:
pip install esptool
2. Flash all three binaries:
python -m esptool --chip esp32s3 --port COM7 --baud 460800 \
--before default-reset --after hard-reset \
write-flash --flash-mode dio --flash-freq 80m --flash-size 4MB \
0x0 bootloader.bin \
0x8000 partition-table.bin \
0x10000 esp32-csi-node.bin
Replace COM7 with your serial port (/dev/ttyUSB0 on Linux, /dev/cu.usbserial-* on macOS).
3. Provision WiFi credentials (no rebuild needed):
python provision.py --port COM7 \
--ssid "YourWiFi" --password "YourPassword" \
--target-ip 192.168.1.20 --target-port 5005
4. Run the aggregator:
# From the wifi-densepose repo (Rust)
cargo run -p wifi-densepose-sensing-server -- --http-port 3000 --source esp32
# Or Docker
docker run -p 3000:3000 -p 5005:5005/udp ruvnet/wifi-densepose:latest --source esp32
MAC Address Filtering (New)
Filter CSI to a single AP to prevent signal mixing:
# Via provision.py (runtime, no reflash)
python provision.py --port COM7 --filter-mac "AA:BB:CC:DD:EE:FF"
# Or compile-time via Kconfig
# Set CONFIG_CSI_FILTER_MAC="AA:BB:CC:DD:EE:FF" in sdkconfig.defaults
TDM Mesh Setup (New)
For multistatic mesh with 3+ nodes:
# Node 0
python provision.py --port COM7 --ssid "WiFi" --password "pass" \
--target-ip 192.168.1.20 --tdm-slot 0 --tdm-total 3
# Node 1
python provision.py --port COM8 --tdm-slot 1 --tdm-total 3
# Node 2
python provision.py --port COM9 --tdm-slot 2 --tdm-total 3
Hardware Compatibility
| Board | Status | Notes |
|---|---|---|
| ESP32-S3-DevKitC-1 | Verified | Tested with CP210x USB-UART |
| ESP32-S3-WROOM-1 | Expected | Same SoC |
| ESP32-S3-MINI-1 | Expected | Same SoC |
| ESP32 (classic) | Rebuild required | idf.py set-target esp32 |
Firmware Features
- WiFi STA + promiscuous mode for CSI capture
- ADR-018 binary serialization with 20-byte header + raw I/Q payload
- UDP streaming to configurable aggregator
- NEW: MAC address filtering (compile-time or runtime via NVS)
- NEW: TDM time-slot protocol for multistatic mesh (ADR-029/031)
- NEW: Channel hopping with configurable dwell time
- NVS runtime config — SSID, password, target IP/port, node ID, MAC filter, TDM config
- ~20 Hz CSI frame rate (LLTF + HT-LTF + STBC HT-LTF2)
- 64-128 subcarriers per frame depending on bandwidth
- Automatic WiFi reconnection (up to 10 retries)
download
unix · zip
curl -fL -o v0.2.0-esp32.zip https://ratatoskr.space/pkg/ruview/v0.2.0-esp32.zip
printf '%s %s\n' 'd102a51462e96eb596bcc017dc8e8d776200e21326550b8bd629d1735dade407' 'v0.2.0-esp32.zip' | sha256sum -c -
windows · zip
$url = "https://ratatoskr.space/pkg/ruview/v0.2.0-esp32.zip"
$out = "v0.2.0-esp32.zip"
Invoke-WebRequest -Uri $url -OutFile $out
if ((Get-FileHash $out -Algorithm SHA256).Hash.ToLowerInvariant() -ne "d102a51462e96eb596bcc017dc8e8d776200e21326550b8bd629d1735dade407") { throw "sha256 mismatch" }
unix · tar.gz
curl -fL -o v0.2.0-esp32.tar.gz https://ratatoskr.space/pkg/ruview/v0.2.0-esp32.tar.gz
printf '%s %s\n' '7f788821114503e3b7aabf7acba4cec7aaeb80cf32be374a2fa85987824cd579' 'v0.2.0-esp32.tar.gz' | sha256sum -c -
windows · tar.gz
$url = "https://ratatoskr.space/pkg/ruview/v0.2.0-esp32.tar.gz"
$out = "v0.2.0-esp32.tar.gz"
Invoke-WebRequest -Uri $url -OutFile $out
if ((Get-FileHash $out -Algorithm SHA256).Hash.ToLowerInvariant() -ne "7f788821114503e3b7aabf7acba4cec7aaeb80cf32be374a2fa85987824cd579") { throw "sha256 mismatch" }
download via yggdrasil mesh
unix · zip
curl -fL -o v0.2.0-esp32.zip http://14cc7d57b5e70f679b851fe5b272ce17c70632ff4beb5b35ab64bc706b2485af.pk.ygg/pkg/ruview/v0.2.0-esp32.zip
printf '%s %s\n' 'd102a51462e96eb596bcc017dc8e8d776200e21326550b8bd629d1735dade407' 'v0.2.0-esp32.zip' | sha256sum -c -
windows · zip
$url = "http://14cc7d57b5e70f679b851fe5b272ce17c70632ff4beb5b35ab64bc706b2485af.pk.ygg/pkg/ruview/v0.2.0-esp32.zip"
$out = "v0.2.0-esp32.zip"
Invoke-WebRequest -Uri $url -OutFile $out
if ((Get-FileHash $out -Algorithm SHA256).Hash.ToLowerInvariant() -ne "d102a51462e96eb596bcc017dc8e8d776200e21326550b8bd629d1735dade407") { throw "sha256 mismatch" }
unix · tar.gz
curl -fL -o v0.2.0-esp32.tar.gz http://14cc7d57b5e70f679b851fe5b272ce17c70632ff4beb5b35ab64bc706b2485af.pk.ygg/pkg/ruview/v0.2.0-esp32.tar.gz
printf '%s %s\n' '7f788821114503e3b7aabf7acba4cec7aaeb80cf32be374a2fa85987824cd579' 'v0.2.0-esp32.tar.gz' | sha256sum -c -
windows · tar.gz
$url = "http://14cc7d57b5e70f679b851fe5b272ce17c70632ff4beb5b35ab64bc706b2485af.pk.ygg/pkg/ruview/v0.2.0-esp32.tar.gz"
$out = "v0.2.0-esp32.tar.gz"
Invoke-WebRequest -Uri $url -OutFile $out
if ((Get-FileHash $out -Algorithm SHA256).Hash.ToLowerInvariant() -ne "7f788821114503e3b7aabf7acba4cec7aaeb80cf32be374a2fa85987824cd579") { throw "sha256 mismatch" }
| artifact | format | size | hashes |
|---|---|---|---|
| v0.2.0-esp32.zip | zip | 108.6 MiB |
blake3-24 6efe9f73710458908f477dad63d92001bb1eb054da88de45
sha256 d102a51462e96eb596bcc017dc8e8d776200e21326550b8bd629d1735dade407
sha1 330a5aed6588db2f9edb44991127aeec03628462
|
| v0.2.0-esp32.tar.gz | tar.gz | 102.8 MiB |
blake3-24 8cdbf5647d6408a3ca72c0f3226f297db5f8b85a8d0b1f0f
sha256 7f788821114503e3b7aabf7acba4cec7aaeb80cf32be374a2fa85987824cd579
sha1 acbb768d41925cfbeab2256f6103f6a258ca417c
|
install
bazel
http_archive(
name = "ruview",
urls = ["https://ratatoskr.space/pkg/ruview/v0.2.0-esp32.tar.gz"],
integrity = "sha256-f3iIIRFFA+O3qr96y6TOx6rrgM8yvjdKL6hZh4JM1Xk=",
strip_prefix = "ruview-v0.2.0-esp32",
)
zig
.url = "https://ratatoskr.space/pkg/ruview/v0.2.0-esp32.tar.gz",
install via yggdrasil mesh
bazel
http_archive(
name = "ruview",
urls = ["http://14cc7d57b5e70f679b851fe5b272ce17c70632ff4beb5b35ab64bc706b2485af.pk.ygg/pkg/ruview/v0.2.0-esp32.tar.gz"],
integrity = "sha256-f3iIIRFFA+O3qr96y6TOx6rrgM8yvjdKL6hZh4JM1Xk=",
strip_prefix = "ruview-v0.2.0-esp32",
)
zig
.url = "http://14cc7d57b5e70f679b851fe5b272ce17c70632ff4beb5b35ab64bc706b2485af.pk.ygg/pkg/ruview/v0.2.0-esp32.tar.gz",