Skip to content

⭐ Tutorial: Windows WiFi Sensing Quick Start (ADR-013) #36

@ruvnet

Description

@ruvnet

Windows WiFi Sensing Quick Start (ADR-013) — current as of 2026-05-19

⚠️ Updated 2026-05-19 — original tutorial referenced paths and a repo name that no longer match. Updated for the current state of ruvnet/RuView (note: the repo was renamed from wifi-densepose to RuView).

What changed vs the original: repo name, v1/src/...archive/v1/src/... (the v1 Python codebase was archived but is still runnable), updated release links, added the ./verify deterministic proof check.

Zero-cost presence and motion detection using your existing Windows WiFi — no special hardware needed.

This is the easier path. Reads real RSSI from your laptop's WiFi adapter via netsh, extracts spectral and statistical features, and classifies presence/motion in real-time. No ESP32, no firmware build, no soldering.

For higher-fidelity sensing (breathing rate, heart rate, multi-person, through-wall), use the ESP32-S3 CSI tutorial in #34 instead.


What you need

Item Notes
Windows 10/11 laptop Any WiFi adapter works
Connected WiFi network Must be actively associated with an AP
Python 3.10+ With pip
~5 minutes No hardware mods, no drivers, no root

What you'll get

Capability Works? How
Presence detection ✅ Yes RSSI variance threshold
Motion detection (still/active) ✅ Yes Spectral band power
Breathing detection ❌ No RSSI resolution too coarse (netsh is 1 dBm-quantized)
Heart rate ❌ No Not possible with RSSI alone
Pose estimation ❌ No Requires CSI — see #34
Through-wall sensing ❌ Limited RSSI is too coarse; CSI handles this

Step 1: Clone and install

git clone https://github.com/ruvnet/RuView.git
cd RuView
pip install numpy scipy

📁 The Python v1 code now lives under archive/v1/, not v1/. The original code is still functional and runnable; it was archived to make room for the v2 Rust workspace as the primary codebase, but all the v1 demos in this tutorial still work.

Step 2: Verify WiFi is connected

netsh wlan show interfaces

You should see State: connected and a Signal: value (e.g. 94%). If disconnected:

netsh wlan connect name="YourNetworkName"

Step 3: Run a single RSSI sample

# Quick test — run from the repo root
python -c "
import sys; sys.path.insert(0, 'archive/v1')
from src.sensing.rssi_collector import WindowsWifiCollector
c = WindowsWifiCollector(interface='Wi-Fi')
s = c.collect_once()
print(f'RSSI: {s.rssi_dbm} dBm, Quality: {s.link_quality:.0%}')
"

Expected output:

RSSI: -39.0 dBm, Quality: 94%

Step 4: Run the full pipeline (feature extraction + classification)

python -c "
import sys, time; sys.path.insert(0, 'archive/v1')
from src.sensing.rssi_collector import WindowsWifiCollector
from src.sensing.feature_extractor import RssiFeatureExtractor
from src.sensing.classifier import PresenceClassifier

collector = WindowsWifiCollector(interface='Wi-Fi', sample_rate_hz=2.0)
extractor = RssiFeatureExtractor(window_seconds=15.0)
classifier = PresenceClassifier(presence_variance_threshold=0.3)

collector.start()
print('Collecting 15 seconds of RSSI data...')
time.sleep(15)
collector.stop()

samples = collector.get_samples()
features = extractor.extract(samples)
result = classifier.classify(features)

print(f'Samples:   {len(samples)}')
print(f'RSSI mean: {features.mean:.1f} dBm')
print(f'Variance:  {features.variance:.4f}')
print(f'Motion:    {features.motion_band_power:.4f}')
print(f'Verdict:   {result.motion_level.value} ({result.confidence:.0%})')
"

Step 5: Live monitoring (walk around to test)

# From repo root
$env:PYTHONPATH = "archive/v1"
python archive/v1/tests/integration/live_sense_monitor.py

This prints a live dashboard every 3 seconds:

[14:00:05] RSSI= -37.0dBm var=0.000 motion_e=0.0000 => absent         (100%)
[14:00:08] RSSI= -37.0dBm var=0.000 motion_e=0.0000 => absent         (100%)
[14:00:26] RSSI= -37.1dBm var=0.120 motion_e=0.0016 => absent          (76%)  ← RSSI changing!
[14:00:32] RSSI= -41.3dBm var=4.850 motion_e=1.2300 => active          (95%)  ← MOTION DETECTED

To trigger detection: walk between your laptop and the WiFi router. This causes 3–10+ dBm RSSI swings that the classifier picks up as ACTIVE motion.

Press Ctrl+C to stop and see a summary.

Step 6: Use the CommodityBackend API

import sys; sys.path.insert(0, 'archive/v1')
from src.sensing.backend import CommodityBackend, Capability
from src.sensing.rssi_collector import WindowsWifiCollector

collector = WindowsWifiCollector(interface="Wi-Fi", sample_rate_hz=2.0)
backend = CommodityBackend(collector=collector)

print(backend.get_capabilities())
# {<Capability.PRESENCE>, <Capability.MOTION>}

backend.start()
# ... wait for data collection ...
result = backend.get_result()
print(result.motion_level)   # MotionLevel.ABSENT / PRESENT_STILL / ACTIVE
print(result.confidence)     # 0.0 to 1.0
backend.stop()

Step 7: Run the tests

# Unit tests (36 tests, no WiFi needed — uses SimulatedCollector)
$env:PYTHONPATH = "archive/v1"
python -m pytest archive/v1/tests/unit/test_sensing.py -v -o "addopts="

# Live integration tests (5 tests, requires connected WiFi)
python -m pytest archive/v1/tests/integration/test_windows_live_sensing.py -v -o "addopts=" -s

Step 8: (Optional) Run the deterministic proof

While you're set up, also verify the signal-processing pipeline is real (not mocked):

./verify

Should print RESULT: PASS. This is the ADR-028 trust kill switch: a 100-frame deterministic replay through the production CSI pipeline whose SHA-256 hash matches a pinned expected value. If anyone claims "the project is mocked", ./verify is how you check.


How it works

Windows WiFi (netsh)          Feature Extraction              Classification
┌─────────────────┐     ┌───────────────────────┐     ┌──────────────────┐
│ netsh wlan show │     │ Hann-windowed FFT     │     │ Variance > 0.3?  │
│ interfaces      │────▶│ Band power analysis   │────▶│  → PRESENT       │
│                 │     │ CUSUM change-point    │     │ Motion energy?   │
│ RSSI: -39 dBm   │     │ Rolling statistics    │     │  → STILL/ACTIVE  │
└─────────────────┘     └───────────────────────┘     └──────────────────┘

Pipeline: WindowsWifiCollectorRssiFeatureExtractor (FFT, CUSUM, spectral bands) → PresenceClassifier (rule-based, interpretable)

Limitations (honest assessment)

Limitation Why
RSSI quantized to 1 dBm netsh reports integers; sub-dBm variation invisible
~2 Hz max sample rate netsh takes 200–400 ms per call
No breathing/heartbeat Requires sub-dBm resolution (use ESP32 CSI instead)
Best for coarse motion Person must cross the WiFi signal path for large RSSI swings
Single receiver only Multi-receiver fusion requires multiple machines

When to upgrade to ESP32 CSI

If you need any of:

  • Breathing / heart rate (vitals)
  • Multi-person discrimination
  • Through-wall sensing
  • Pose detection (eventually — see #509)
  • Real-time positional tracking (multistatic)

...follow the ESP32-S3 CSI tutorial in #34 instead. Hardware cost is ~$9 per node, 3-node setup runs ~$30 + a Raspberry Pi as the aggregator (or any existing PC).

The current firmware release is v0.6.5-esp32 with flash-ready binaries.


Verified on

  • OS: Windows 11 Home 10.0.26200
  • Adapter: Intel Wi-Fi 7 BE201 320MHz (any Windows-compatible adapter should work)
  • Network: WPA2-Personal, 5 GHz 802.11ax
  • RSSI observed: -37 to -40 dBm in 5 GHz, similar range in 2.4 GHz
  • Tests: 36 unit + 5 integration = 41 passed
  • Python: 3.13, numpy, scipy

Related

  • ADR-013: Feature-level sensing on commodity gear
  • ADR-012: ESP32 CSI sensor mesh (the upgrade path)
  • ADR-028: Trust kill switch (the ./verify proof)
  • #34: ESP32 CSI end-to-end tutorial
  • User Guide: All deployment modes
  • cognitum.one/ruview: Cognitum Seed integration (persistent memory + AI)

If anything in this tutorial breaks against current main, open a new issue — these tutorials track the code as it evolves.

Metadata

Metadata

Assignees

Labels

documentationImprovements or additions to documentation

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions