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.

@Viscous106 Viscous106 force-pushed the fix/multistatic-node-id-calibration branch from 4ec8298 to b2a40da Compare May 2, 2026 14:05
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