NEW PAGE · JUNE 12, 2026 You're reading the new Complete Walkthrough — every command verified against Ruv's RuView v1701, today's build
The complete walkthrough · nothing skipped

"I just bought a Seed."
Here's every step to a sensing home.

This page takes you from an unopened box to a live dashboard showing who's in the room, their breathing rate, heart beat, motion — and how far you can honestly push skeletal pose. Every command below is quoted verbatim from RuView's own docs and code, not paraphrased.

✓ Verified against RuView main @ 3d7530f0 (v1701) · committed 2026-06-12 09:09 EDT · this page written 2026-06-12

00 — THE JOURNEY

Nine phases, in order

Don't jump ahead. Each phase has a "you'll know it worked when…" check. If a check fails, stop and fix it — the later phases can't compensate.

#PhaseTimeYou'll know it worked when…
1Know your brain: Seed vs V05 minYou know which box you have and what runs where
2Prepare the network10 minReserved IPs; you know your 2.4 GHz network name
3Run RuView on simulated data2 mincurl /health returns "status":"ok"
4Flash the ESP32 nodes15 min/node first timeSerial monitor shows the node booting
5Provision each node2 min/nodetcpdump shows ~10+ pkts/sec on UDP 5005
6Point RuView at real CSI5 minObservatory badge flips DEMO → LIVE
7Calibrate the empty room1–2 minroom-status says baseline: fresh
8Enroll & train your room~10 minSix specialists report ✓ trained
9Give the Seed a memory15 minBridge --stats shows vectors accumulating
01 — DAY ZERO

Know your brain: Seed vs V0 (and your laptop's role)

The single biggest beginner confusion. Three machines can be involved, and each has a different job.

MachineWhat it isIts job in this walkthrough
Cognitum One SeedPi Zero 2 W applianceThe memory: persistent vector store, kNN search, witness chain, MCP tools for AI assistants. It does not run the heavy CSI processing.
Cognitum V0 appliancePi 5 + Hailo-8The always-on LAN core: can run the RuView sensing server itself, plus its calibration dashboard on port 9000.
Your Mac/PCWhatever you haveFlashing the nodes (always), and running the sensing server + Seed bridge (if you don't have a V0). 4 GB RAM minimum, 8 GB recommended; ~2–5 GB disk.

Mental model: ESP32 nodes are the senses (they capture WiFi-CSI), the sensing server — on your laptop or the V0 — is the perception (it turns CSI into people, breathing, pose), and the Seed is the memory (it stores what was perceived, tamper-evidently, and lets AI agents query it). You can complete phases 1–8 with no Seed at all, then add it in phase 9.

The architectural choice everyone stumbles on: just-on-my-network, or wired into the Seed?

Here's the thing nobody tells you up front: your sensors never stream their raw data to the Seed either way. Raw CSI is a firehose (~50+ packets/sec/node) and the Seed is a Pi Zero — it physically can't run the perception pipeline. Raw CSI always goes to the sensing server on a capable machine (your laptop, or the V0). The real choice is what happens after perception:

Network-only (no Seed)Seed-connected
What you getEverything live: dashboards, vitals, pose, alerts — full real-time experienceAll of that, plus a permanent record: each second's 8-number room summary stored with a tamper-evident witness chain
What happens to historyForgotten instantly. The server keeps no database — close it and yesterday never happenedQueryable forever: "was anyone in the living room at 3am?" becomes answerable — by you or your AI assistant over MCP
What must stay onThe laptop/PC running the serverSame, plus the Seed (~2 W) — and currently a small bridge script on the host (an upstream limitation)
Extra capabilitySeed's own sensors (PIR, temp/humidity) become free ground-truth signals — they make camera-free pose training meaningfully better (10 signals vs 3)
Best forFirst week: learning, placing nodes, calibratingAnything you actually want to use: elder care, sleep trends, security, training models

Our advice: start network-only (phases 1–8) — fewer moving parts while you learn. Add the Seed (phase 10) the day you catch yourself wanting history. The one decision to get right early: provision nodes to a reserved, stable IP, because every node carries that address in its flash. Moving the server later means re-provisioning each node over USB (a 2-minute chore × 7 nodes — avoidable by choosing the stable machine, like a V0, up front). If you own a V0, skip the dilemma: it runs the perception and the always-on role in one box.

02 — NETWORK PREP

Ten minutes in your router's admin page

1

Find your 2.4 GHz network name

ESP32-S3 and C6 nodes only see 2.4 GHz; the C5 is the dual-band exception. If your router broadcasts one combined name, that's fine — but if 2.4 and 5 GHz have separate names, note the 2.4 GHz one. Bonus for C6 nodes: if your router supports WiFi 6 (802.11ax) on 2.4 GHz, enable it — the C6 then captures 242-tone high-fidelity CSI instead of 64-tone (4× the spectral detail). (And if you're wondering why your laptop's far fancier WiFi can't just do the sensing itself: every WiFi chip computes the needed CSI internally, but only the ESP32's maker exposes it — your OS keeps that door locked, which is why these little boards exist at all.)

2

Reserve IP addresses

In your router's DHCP settings, reserve a fixed IP for the machine that will run the sensing server (your laptop or the V0). The nodes are provisioned with this exact IP — if it changes, every node goes silent and you'll think the hardware died. Reserve the Seed's IP too while you're there.

3

Open the firewall port

The nodes stream to UDP 5005 on the sensing-server machine. macOS will prompt to allow it; on Linux check ufw; on Windows allow the app through Defender Firewall.

03 — SOFTWARE FIRST

Run RuView before you touch a soldering-free sensor

Counter-intuitive but right: get the software working on simulated data first. Then when real hardware misbehaves, you know the problem is the hardware side, not the software side.

1

The 30-second demo (Docker — recommended)

docker run -p 3000:3000 -p 3001:3001 ruvnet/wifi-densepose:latest
# then open  http://localhost:3000  in your browser

You'll see a Three.js view with a 3D body skeleton, signal heatmap, phase plot and a vitals panel — all from synthetic CSI. The image is multi-arch (Intel/AMD + Apple Silicon).

2

Verify with the API — your first "it works" moment

curl http://localhost:3000/health
# Expected: {"status":"ok","source":"simulated","clients":0}
curl http://localhost:3000/api/v1/sensing/latest   # latest frame
curl http://localhost:3000/api/v1/vital-signs      # breathing + heart rate
curl http://localhost:3000/api/v1/pose/current     # 17 COCO keypoints
3

Building from source instead? (V0, or you want the latest)

git clone https://github.com/ruvnet/RuView.git
cd RuView/v2
cargo build --release                              # Rust 1.85+ recommended
cargo test --workspace --no-default-features       # optional: runs 1,400+ tests
# binary lands at  target/release/sensing-server

Port gotcha that bites everyone: Docker publishes 3000/3001, but the source binary defaults to 8080/8765. Running from source, pass the ports and UI path explicitly: --http-port 3000 --ws-port 3001 --ui-path ../ui.

04 — FLASH THE NODES

Firmware onto every ESP32 — exact commands

Download the prebuilt binaries from RuView Releases ↗. As of today, use v0.7.1-esp32 or newer (fixes a CSI-starvation bug and a heart-rate bug stuck near 45 BPM); v0.8.0-esp32 adds the C6's 242-tone WiFi-6 CSI. The easiest path is still asking Claude Code with the RuView plugin to do this — below is what happens under the hood.

ESP32-S3 — most boards (8 MB flash)

# your serial port: /dev/cu.usbmodem… on macOS, COM7-style on Windows
python -m esptool --chip esp32s3 --port COM7 --baud 460800 \
  write-flash --flash-mode dio --flash-size 8MB --flash-freq 80m \
  0x0 bootloader.bin 0x8000 partition-table.bin \
  0xf000 ota_data_initial.bin 0x20000 esp32-csi-node.bin

4 MB boards (S3 SuperMini): use the -4mb binaries from the release and --flash-size 4MB, with partition-table-4mb.bin at 0x8000 and esp32-csi-node-4mb.bin at 0x20000.

ESP32-C6 — the high-fidelity node (incl. the chip in your MR60 XIAO)

Build with current ESP-IDF (the v0.8.0-esp32 release notes say v5.5.2 for full HE-LTF; the in-repo README still documents v5.4 — check the release notes), or grab the C6 binaries from v0.8.0-esp32:

cd firmware/esp32-csi-node
idf.py set-target esp32c6
idf.py build                    # ~1.0 MB binary
idf.py -p COM6 flash

Then watch it boot with idf.py -p COM6 monitor — you want to see lines like:

I (353) main: ESP32-C6 CSI Node (ADR-018 / ADR-110) — Node ID: 1
I (413) c6_ts: init done: channel=15 EUI=… leader=yes(candidate)
I (463) wifi: mac_version:HAL_MAC_ESP32AX_761   ← the 802.11ax MAC is loaded

Know before you buy/flash: the C6 has no OTA (single partition) — every reflash is over USB. And the C6 build has no mmWave fusion or display support; it's the precision CSI instrument, the S3 is the workhorse.

If nothing happens when you flash: 95% of the time it's a charge-only USB-C cable. The other 5%: hold the board's BOOT button while plugging in to force download mode. (MR60 XIAO: you must open the case and use the inner USB-C port — the edge port is power-only.)

05 — PROVISION

Tell each node who it is and where to stream

Provisioning writes settings into the node's flash (NVS) — no reflash needed to change them later. One script handles S3 and C6 alike: firmware/esp32-csi-node/provision.py.

1

The basic provision — one command per node

python firmware/esp32-csi-node/provision.py --port COM7 \
  --ssid "YourWiFi" --password "YourPassword" \
  --target-ip 192.168.1.20 --node-id 1

--target-ip is the reserved IP of the sensing-server machine from phase 02. --node-id must be unique per node (1, 2, 3…7) — it's the only way the server tells your nodes apart; there is no discovery handshake. Re-running the script merges new settings, it doesn't wipe old ones.

2

Multi-node array: give each node a TDM slot

So nodes take turns transmitting instead of jamming each other:

python firmware/esp32-csi-node/provision.py --port COM7 --tdm-slot 0 --tdm-total 7
python firmware/esp32-csi-node/provision.py --port COM8 --tdm-slot 1 --tdm-total 7
# …and so on through slot 6 for a 7-node array
3

Optional but recommended: on-device vitals (Tier 2)

python firmware/esp32-csi-node/provision.py --port COM7 \
  --ssid "YourWiFi" --password "YourPassword" --target-ip 192.168.1.20 \
  --edge-tier 2

Tier 2 makes each node compute presence, breathing, heart-rate, motion and fall flags on the chip (~33 KB RAM) and stream them alongside raw CSI — this is also what the Seed ingests in phase 09.

4

Mesh authentication — not provisionable yet

Mesh beacon authentication (HMAC-SHA256, ADR-032) exists in the firmware but has no provisioning flag yet — skip this; it is not user-configurable today.

06 — GO LIVE

Point RuView at the real hardware

1

Confirm packets are arriving at all

sudo tcpdump -ni any udp port 5005   # expect a steady packet stream from each node's IP
2

Restart the server in ESP32 mode

# Docker:
docker run --rm -e CSI_SOURCE=esp32 \
  -p 3000:3000 -p 3001:3001 -p 5005:5005/udp \
  ruvnet/wifi-densepose:latest

# From source (e.g. on the V0):
./target/release/sensing-server --source esp32 --udp-port 5005 \
  --http-port 3000 --ws-port 3001 --ui-path ../ui

Only one process can own UDP 5005. If you ran a standalone aggregator for testing, stop it first — the sensing server is the aggregator behind the dashboard, API and WebSocket.

3

Check every node showed up

curl http://localhost:3000/health            # "source" should no longer say simulated
curl http://localhost:3000/api/v1/nodes      # every node-id you provisioned, with RSSI + freshness
curl http://localhost:3000/api/v1/sensing/latest
4

Open the two dashboards

ViewURLWhat it's for
Dashboardhttp://localhost:3000/ui/index.htmlSignal field, vitals, live skeleton
Observatoryhttp://localhost:3000/ui/observatory.htmlCinematic room view + per-node status — your array health screen

The Observatory auto-detects the live server: its HUD badge flips from DEMO to LIVE. That badge is your single source of truth — if it says DEMO, you're looking at canned data, not your home.

07 — CALIBRATE

Teach it what "empty" looks like (60 seconds)

Out of the box the system has never seen your room. Calibration records the empty-room RF fingerprint that everything else is measured against. This is the step almost everyone skips, and it's why their presence detection flickers.

1

Leave the room, then run

wifi-densepose calibrate --udp-port 5005 --duration-s 60 --output baseline.bin   # you must NOT be in the room

The CLI ships in the RuView build (wifi-densepose-cli crate). On a V0 appliance you can instead press Calibrate on its dashboard at http://cognitum-v0:9000. Pets count as "not empty" — shut the cat out.

2

Know when to redo it

Recalibrate after: moving furniture, moving/adding any node, or when room-status reports baseline drift. It's deliberate that this never happens automatically — the system can't verify the room is empty by itself, and a baseline captured with someone in the room silently poisons everything downstream.

08 — TRAIN

Ten minutes of guided enrollment → six trained specialists

This is the out-of-box training process you've been looking for. No camera, no dataset downloads — you are the training data. (ADR-151's four-stage flow.)

1

Guided enrollment — the system coaches you through 8 anchors (~4 min)

wifi-densepose enroll --room-id living-room
#   → "Stand still in view of the sensor…"  [✓ anchor accepted: coherence 0.91]
#   → "Sit down…"                            [✗ low SNR, retrying]
#   …walk, lie down, leave the room, etc. — 8 anchor poses total

Each anchor has a quality gate — if your WiFi was noisy during one, it just asks again. Do this once per room.

2

Train the specialist bank

wifi-densepose train-room --enrollment ./enrollment.json --output ./room-bank.json

Instead of one big model, RuView trains six small statistical specialists calibrated to your room — breathing, heartbeat, restlessness, posture, presence and anomaly. All six always train (there's no flag to pick a subset). They run in microseconds, fully on-device.

3

Confirm — and learn this status command, you'll use it forever

wifi-densepose room-status --bank ./room-bank.json
#   baseline: fresh (drift 0.04 < 0.20)
#   breathing  ✓ trained  conf p50 0.88
#   heartbeat  ✓ trained  conf p50 0.71
#   posture    ✓ 3 prototypes (stand/sit/lie)
#   anomaly ✓ · presence ✓ · restlessness ✓

What you now have — honestly stated

  • Presence & person count — solid. The flagship capability.
  • Breathing rate — solid (6–30 BPM band, needs a fairly stationary subject). No per-person training required.
  • Heart rate — works, lower confidence than breathing; treat as experimental. (Validated against an Apple Watch in Ruv's own tests; a Polar H10 strap is a great ground-truth check.)
  • Motion / activity / fall flags — solid, runs on the nodes themselves at Tier 2.
  • Anonymous re-identification — "same body as yesterday, probably." Named identity from WiFi alone is not a shipped capability — Ruv's own measurements say two people aren't yet separable on cardiac/respiratory channels alone.
09 — SKELETAL POSE

The honest pose ladder: three rungs

The dashboard's skeleton works out of the box — but know what you're looking at, and what it takes to make it real. The training answer everyone asks: calibration is 60 seconds, enrollment ~4 minutes, real pose needs one ~40-minute camera-teacher session + ~1 hour of training — never hours or days of empty-room recording. And it's a 17-point skeleton by physics and by benchmark standard: a 2.4 GHz wave is ~12.5 cm long, so fingers and faces (most of MediaPipe's extra 16 points) are below what the radio can resolve — finer grain comes from sensor fusion (LD6004 Z-axis, 60 GHz radar, camera point-cloud), not more WiFi keypoints.

Rung 1 — what you get free: a heuristic skeleton

The default 17-keypoint figure is signal-derived — limbs animated from motion energy, not learned keypoint inference. Great for "is something moving and roughly where," not for "is their left arm raised." The dashboard labels this honestly ("Signal-Derived vs Model Inference").

Rung 2 — camera-free training (works with your Seed, no camera ever)

RuView can train a pose model by fusing up to 10 weak signals — PIR, temperature, humidity, cross-node RSSI triangulation, vibration footsteps, door reed switch, kNN clusters from the Seed's vector store and more:

# with a Cognitum Seed connected (all 10 signals):
node scripts/train-camera-free.js \
  --data data/recordings/pretrain-*.csi.jsonl \
  --seed-url https://169.254.42.1:8443 --seed-token "$SEED_TOKEN"

# without a Seed (CSI-only, 3 signals — still works):
node scripts/train-camera-free.js \
  --data data/recordings/pretrain-*.csi.jsonl --no-seed

Output: an 82.8 KB model (8 KB quantized) with full-skeleton predictions and per-node adapters. Expect coarse-but-plausible poses, not precision.

Rung 3 — camera-supervised training (highest accuracy; camera is a temporary teacher)

A webcam + MediaPipe records real poses paired with CSI for 35–40 minutes while you move through varied activities; then the camera goes away forever and the model runs on WiFi alone. (Intriguing hardware option: the XIAO ESP32S3 Sense ↗ is an S3 CSI node with a camera on board — teacher and student in one $27 part, though RuView's documented flow uses a host webcam.)

pip install mediapipe opencv-python
python scripts/calibrate-camera-room.py            # 5 min, two checkerboards — makes the model survive relocation

# Terminal 1 + Terminal 2, simultaneously, 40 minutes:
python scripts/record-csi-udp.py --duration 2400
python scripts/collect-ground-truth.py --duration 2400 --preview \
  --calibration data/calibration/camera-room.json

node scripts/align-ground-truth.js --gt data/ground-truth/*.jsonl --csi data/recordings/csi-*.csi.jsonl
node scripts/train-wiflow-supervised.js --data data/paired/*.jsonl --scale small --epochs 50
node scripts/eval-wiflow.js --model models/wiflow-supervised/wiflow-v1.json --data data/paired/*.jsonl

Reality check, from Ruv's own changelog: a previously cited 92.9% accuracy figure was retracted (it came from a flawed protocol). Change activity every 1–2 minutes during capture — 40 minutes of sitting trains a model that only knows sitting. A 5-minute session is not enough, period.

About the pretrained models on Hugging Face: ruvnet/wifi-densepose-pretrained is real (a CSI encoder + presence head, 82.3% held-out triplet accuracy) and ruvnet/wifi-densepose-mmfi-pose reports 82.69% torso-PCK@20 on the MM-Fi benchmark. But the live sensing server's --model flag can't read the published JSONL format yet (a tracked gap) — so for the live dashboard, run without --model and use the weights from the Python/training side. Don't burn an evening on this; it's upstream's gap, not your mistake.

10 — SEED MEMORY

Give it a memory: wire the array into your Seed

Everything so far is real-time and forgotten. The Seed adds the persistent part: every second, an 8-number summary of the room (presence, motion, breathing, heart rate, phase variance, person count, fall flag, signal strength) is stored with a tamper-evident witness chain — and becomes queryable by AI assistants over MCP.

1

Pair with the Seed over USB (once)

# Plug the Seed into your laptop via USB — it appears as a network device at 169.254.42.1
curl -sk -X POST https://169.254.42.1:8443/api/v1/pair/window     # opens a 30-second pairing window
curl -sk -X POST https://169.254.42.1:8443/api/v1/pair \
  -H 'Content-Type: application/json' -d '{"client_name":"my-laptop"}'
# SAVE THE RETURNED TOKEN — it is shown exactly once

Pairing is USB-only by design — nobody on your WiFi can pair with your Seed.

2

Provision the nodes' feature stream (UDP 5006 — separate from the CSI stream)

python firmware/esp32-csi-node/provision.py --port COM9 \
  --ssid "YourWiFi" --password "secret" \
  --target-ip 192.168.1.20 --target-port 5006 --node-id 1

--target-ip here is your laptop (where the bridge runs) — not the Seed. The bridge is the middleman; current limitation is that it runs on a host machine, not on the Seed itself. Nodes need --edge-tier 2 (phase 05) to produce the feature packets.

3

Run the bridge

export SEED_TOKEN="your-pairing-token"
python scripts/seed_csi_bridge.py \
  --seed-url https://169.254.42.1:8443 --token "$SEED_TOKEN" \
  --udp-port 5006 --batch-size 10 --validate
4

Watch the memory grow — and maintain it

python scripts/seed_csi_bridge.py --token "$SEED_TOKEN" --stats     # vector counts, ingest rate
python scripts/seed_csi_bridge.py --token "$SEED_TOKEN" --compact   # run periodically — Seed works best under ~100K vectors

From here, AI assistants (Claude, etc.) can query your home's sensing history through the Seed's MCP proxy — "was anyone in the living room at 3am?" becomes an answerable question, with a cryptographic audit trail behind the answer.

11 — WHEN IT WON'T WORK

The end-to-end troubleshooting ladder

Work top-down — each row assumes the rows above it pass.

SymptomCause & fix
Flashing fails / no serial portCharge-only USB-C cable (swap it), or wrong port on the MR60 (use the inner one), or hold BOOT while plugging in.
Node won't join WiFiWrong band — S3/C6 can't see 5 GHz networks. Re-provision with the 2.4 GHz SSID.
tcpdump shows nothing on 5005Wrong --target-ip (must be the sensing-server machine, and reserved in the router), or the firewall is eating UDP 5005.
Packets arrive but /api/v1/nodes is emptyAnother process owns UDP 5005 (stop the standalone aggregator), or Docker is missing -p 5005:5005/udp and -e CSI_SOURCE=esp32.
Observatory stuck on DEMOYou opened the file directly — open it through the server: http://localhost:3000/ui/observatory.html.
Presence flickers when the room is emptyYou skipped calibration (phase 07), or the baseline is stale — recalibrate with the room genuinely empty.
Vitals show 0 BPMSubject must be fairly stationary and within range; give the MR60 ~60 s warm-up; check --edge-tier 2 was provisioned.
Heart rate pinned at ~45 BPMOld firmware — that exact bug was fixed in v0.7.1-esp32. Reflash.
C6 CSI looks low-resolutionFirmware built with IDF older than v5.5.2 (per the v0.8.0-esp32 release notes), or your AP isn't doing 802.11ax on 2.4 GHz — both downgrade you to 64-tone CSI.
Seed ingest fails with 401The pairing token is shown once — re-pair over USB (30-second window) and update $SEED_TOKEN.

Where these commands come from: RuView's user-guide.md ↗, ADR-069 (Seed pipeline), ADR-135 (calibration), ADR-151 (room training) — all read from main @ 3d7530f0 (v1701), committed 2026-06-12. RuView moves fast (multiple releases a day); if something here drifts, the repo wins and we'll re-verify this page against it.

← Back to the sensor primer  ·  Setup & flashing basics →