Skip to content

fix(sensing): UDP relay for multi-node node_id firmware clobber#497

Open
Viscous106 wants to merge 1 commit intoruvnet:mainfrom
Viscous106:fix/multistatic-node-id-calibration
Open

fix(sensing): UDP relay for multi-node node_id firmware clobber#497
Viscous106 wants to merge 1 commit intoruvnet:mainfrom
Viscous106:fix/multistatic-node-id-calibration

Conversation

@Viscous106
Copy link
Copy Markdown

@Viscous106 Viscous106 commented Apr 30, 2026

ESP32 firmware (v0.4.3.1) overwrites node_id to 1 between app_main and csi_collector_init. All nodes send node_id=1 so sensing-server collapses them into a single node — multistatic fusion never engages.

scripts/csi_node_id_relay.py rewrites byte[4] by source IP before forwarding to sensing-server. Includes 12 unit tests.

Closes #374
May fix #386 (same root cause, unverified on Windows)

"Note: field_bridge.rs calibration fixes and heatmap.html are bundled here because they're needed for multi-node operation to work end-to-end. Happy to split into separate PRs if preferred."

Problem

When 2+ ESP32 nodes are flashed and provisioned with different node_id values, the sensing-server only ever sees "total": 1. Multistatic fusion never engages. On Docker (issue #374) this manifests as only 1 node's packets arriving inside the container. On multi-node setups (issue #386) packets flow but pose never moves.

Root cause: ESP32 firmware v0.4.3.1 clobbers g_nvs_config.node_id to 1 between app_main and csi_collector_init. Every node ships byte[4] = 1 regardless of NVS provisioning.

Serial boot log from node provisioned with node_id=2:

I (363) nvs_config: NVS override: node_id=2        ← NVS read correctly
I (373) main: ESP32-S3 CSI Node — Node ID: 2       ← main sees 2
I (1633) csi_collector: CSI collection initialized (node_id=1, channel=6)
                                                   ↑ clobbered to 1

Fix

scripts/csi_node_id_relay.py — UDP relay that rewrites byte[4] by source IP before forwarding to sensing-server.

ESP32 #1 (192.168.13.222) ──┐
                            ├─→ relay :5005 ─→ sensing-server :5099
ESP32 #2 (192.168.3.246)  ──┘   (rewrites node_id by source IP)

Before / After

Before — both nodes collapse to node_id=1:

{"nodes":[{"node_id":1,"status":"active"}],"total":1}

After — both nodes visible, multistatic fusion active:

{"nodes":[{"node_id":1,"status":"active"},{"node_id":2,"status":"active"}],"total":2}

Signal improvement after fusion engaged:

  • variance: 9 → 75
  • breathing-band power: 0 → 90

Relay stats after 10 min — node 2 had 13,000 rewrites, every packet arriving as node_id=1:
forwarded: {'192.168.13.222': 13000, '192.168.3.246': 13000} rewrites=13000

Usage

# 1. Start relay (listens :5005, forwards to :5099)
nohup python3 scripts/csi_node_id_relay.py --listen-port 5005 --dest-port 5099 &

# 2. Start sensing-server on relay's dest port
sensing-server --source esp32 --udp-port 5099 --node-positions "0,0,1;1.5,0,1"

# 3. Confirm both nodes visible
curl http://localhost:8080/api/v1/nodes

Tests

12 unit tests in scripts/test_csi_node_id_relay.py covering rewrite logic, passthrough cases, all three packet magics, and custom IP maps.

Copilot AI review requested due to automatic review settings April 30, 2026 17:26
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a host-side workaround for an ESP32 firmware bug that causes all UDP CSI/vitals packets to report node_id=1, preventing the sensing server from distinguishing multiple nodes (and blocking multistatic fusion in practice). It also includes several sensing-server/UI changes related to calibration and visualization.

Changes:

  • Add a Python UDP relay (scripts/csi_node_id_relay.py) that rewrites packet byte[4] (node_id) based on source IP, plus unit tests.
  • Adjust sensing-server FieldModel calibration logic (status guard) and single-link FieldModel configuration.
  • Update UI pages (new heatmap.html; tweak viz.html WebSocket URL and Three.js CDN version).

Reviewed changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
v2/crates/wifi-densepose-sensing-server/src/field_bridge.rs Updates FieldModel single-link config and calibration feed guard; adds tests.
v2/crates/wifi-densepose-sensing-server/Cargo.toml Changes wifi-densepose-signal feature flags (enables eigenvalue).
v2/Cargo.lock Lockfile updates from dependency/feature resolution changes.
ui/viz.html Updates Three.js CDN version and WebSocket URL to /api/v1/stream/pose.
ui/heatmap.html Adds a new top-down RF heatmap visualization page polling REST endpoints.
scripts/csi_node_id_relay.py Implements UDP relay + node_id rewrite logic.
scripts/test_csi_node_id_relay.py Adds unit tests for rewrite logic and passthrough behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread ui/heatmap.html Outdated
Comment thread scripts/csi_node_id_relay.py Outdated
Comment thread scripts/csi_node_id_relay.py Outdated
Comment thread scripts/csi_node_id_relay.py Outdated
Comment thread v2/crates/wifi-densepose-sensing-server/Cargo.toml
Comment thread v2/crates/wifi-densepose-sensing-server/src/field_bridge.rs
Comment thread v2/crates/wifi-densepose-sensing-server/src/field_bridge.rs Outdated
Comment thread v2/crates/wifi-densepose-sensing-server/Cargo.toml
@Viscous106 Viscous106 force-pushed the fix/multistatic-node-id-calibration branch from a65b1b4 to aa34ff5 Compare April 30, 2026 17:52
@Viscous106 Viscous106 requested a review from Copilot April 30, 2026 17:55
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a host-side workaround for an ESP32 firmware bug that causes all CSI UDP packets to report node_id=1, enabling multi-node sensing/multistatic behavior end-to-end. The PR also bundles supporting fixes in the sensing server field-model calibration path and UI updates for live visualization.

Changes:

  • Add a UDP relay script that rewrites the packet node_id byte based on source IP, plus unit tests for rewrite/passthrough behavior.
  • Fix sensing-server FieldModel single-link config to use 64 subcarriers and allow calibration feeding from the initial Uncalibrated state (with Rust unit tests).
  • Update UI endpoints/visualizations (viz WS URL; add heatmap.html) and adjust Rust dependency features for eigenvalue-based processing.

Reviewed changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
v2/crates/wifi-densepose-sensing-server/src/field_bridge.rs Single-link FieldModel config and calibration feed guard fixes; adds regression tests.
v2/crates/wifi-densepose-sensing-server/Cargo.toml Enables wifi-densepose-signal eigenvalue feature (BLAS/ndarray-linalg).
v2/Cargo.lock Lockfile updates from dependency resolution changes.
ui/viz.html Switches WS endpoint path and makes host/port dynamic; adjusts three.js version.
ui/heatmap.html Adds a new top-down heatmap view consuming /api/v1/sensing/latest + calibration status.
scripts/csi_node_id_relay.py New UDP relay that rewrites node_id based on source IP and forwards to sensing-server.
scripts/test_csi_node_id_relay.py Adds Python unit tests for packet rewrite logic.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread ui/heatmap.html
Comment thread v2/crates/wifi-densepose-sensing-server/Cargo.toml
Comment thread scripts/csi_node_id_relay.py Outdated
Comment thread scripts/csi_node_id_relay.py Outdated
Comment thread scripts/csi_node_id_relay.py
Comment thread scripts/test_csi_node_id_relay.py
Comment thread scripts/test_csi_node_id_relay.py
Comment thread ui/viz.html
@Viscous106 Viscous106 force-pushed the fix/multistatic-node-id-calibration branch from aa34ff5 to 5ac3258 Compare April 30, 2026 18:13
@Viscous106 Viscous106 requested a review from Copilot April 30, 2026 18:14
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a host-side workaround for ESP32 firmware clobbering node_id (all nodes sending node_id=1), plus related end-to-end updates to make multi-node operation usable from ingestion through visualization.

Changes:

  • Add a UDP relay script that rewrites packet byte[4] (node_id) based on source IP, plus a Python test file for rewrite logic.
  • Fix/extend sensing-server field model behavior for calibration start conditions and ESP32 CSI subcarrier dimensionality (incl. Rust unit tests).
  • Update UI: adjust viz.html Three.js/WebSocket wiring and add a new heatmap.html live 2D view.

Reviewed changes

Copilot reviewed 6 out of 7 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
v2/crates/wifi-densepose-sensing-server/src/field_bridge.rs Sets single-link subcarrier count to 64; allows calibration feeding from Uncalibrated; adds unit tests.
v2/crates/wifi-densepose-sensing-server/Cargo.toml Changes wifi-densepose-signal feature selection (enables eigenvalue).
v2/Cargo.lock Updates resolved dependency graph accordingly.
ui/viz.html Downgrades Three.js to match non-module OrbitControls path; updates WebSocket endpoint URL.
ui/heatmap.html Adds a new live heatmap-style UI page driven by REST endpoints.
scripts/csi_node_id_relay.py New UDP relay that rewrites node_id by source IP before forwarding.
scripts/test_csi_node_id_relay.py New Python tests for packet rewrite/passthrough behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread scripts/csi_node_id_relay.py Outdated
Comment thread scripts/csi_node_id_relay.py
Comment thread scripts/csi_node_id_relay.py Outdated
Comment thread ui/viz.html
Comment thread v2/crates/wifi-densepose-sensing-server/Cargo.toml
  Closes ruvnet#374
  May fix ruvnet#386 (same root cause, unverified on Windows)
@Viscous106 Viscous106 force-pushed the fix/multistatic-node-id-calibration branch from 5ac3258 to b2a40da Compare April 30, 2026 18:32
@Viscous106
Copy link
Copy Markdown
Author

@claude review

@Viscous106
Copy link
Copy Markdown
Author

@ruvnet Have a look at the pr and tell me if there are changes needed I was checking out ruview and needed this fixes so I did it for myself and then found out that there are issues related so opening this pr please have a review and tell me for further changes needed . I have 2 esp32-S3 right now and planning to buy some more for testing so i am in for a long time commitment.

@Viscous106
Copy link
Copy Markdown
Author

@ruvnet Have a look at the pr and tell me if there are changes needed I was checking out ruview and needed this fixes so I did it for myself and then found out that there are issues related so opening this pr please have a review and tell me for further changes needed . I have 2 esp32-S3 right now and planning to buy some more for testing so i am in for a long time commitment.

also i didnt have windows i use Arch Linux so I couldnt test out the fixes in windows env please have the testing for windows on your end.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants