fix(sensing-server): parse ADR-081 feature_state (0xC511_0006), the node's default payload#1296
Open
lakinduakash wants to merge 3 commits into
Open
Conversation
…ode's default payload udp_receiver_task decoded only CSI (0x01), vitals (0x02), fused vitals (0x04), and WASM output (0x07). Per firmware rv_feature_state.h, the feature_state packet (0xC511_0006) is "the default upstream payload from a node ... replaces raw ADR-018 CSI as the primary stream" — so a node emitting its default 60-byte compact state produced zero UI updates (/health stuck at tick:0, banner OFFLINE) despite frames arriving on UDP :5005. Observed on an ESP32-S3-DevKitC-1: ~5 pps 0xC511_0006 all dropped. Add parse_esp32_feature_state(): validates magic + 60-byte length and maps into the existing Esp32VitalsPacket. Presence fails closed on the node's RV_QFLAG_PRESENCE_VALID bit AND presence_score >= 0.5. Wired as an .or_else() fallback after the vitals parser so it flows through the unchanged SensingUpdate/WebSocket path. Pinned by feature_state_parser_tests (4): field extraction + presence mapping, fail-closed when valid-flag clear, wrong-magic reject, short-buffer reject. Bin tests 171 -> 175, 0 failed. Co-Authored-By: claude-flow <ruv@ruv.net>
Firmware adaptive_controller.c copies a raw, unclamped detector magnitude into presence_score/motion_score even though rv_feature_state.h documents them as 0..1 (respiration_conf reuses the same value). MEASURED 13.38 on an ESP32-S3, which surfaced as "1338%" confidence in the UI. Normalise at the parse boundary: non-finite -> 0.0, clamp to [0,1]. The presence/motion booleans derive from the clamped score, so a raw 13.38 still reads as present. Defensive: the server should not trust the node to honor the wire contract. Pinned by feature_state_parser_tests::clamps_raw_and_non_finite_scores_to_unit_range. Co-Authored-By: claude-flow <ruv@ruv.net>
…over raw-CSI classifier With CSI capture healthy (~34 pps raw 0xC511_0001), the server's raw-CSI motion classifier runs per frame and, being uncalibrated for the room, reads ambient CSI variance (the node's own 50 Hz self-ping, other WiFi, multipath) as present_moving. An empty room the ESP32 correctly reports as empty then rendered as "1 person", flickering as raw-CSI updates interleaved with the correct feature_state updates. The ESP32 already computes presence on-device against a 60 s empty-room baseline (ADR-081 feature_state, 0xC511_0006), which reads empty=0. Add prefer_board_presence(): when a fresh feature_state presence exists (< 5 s, tracked via AppStateInner::last_feature_state_at), override the raw-CSI classifier's presence/motion_level on the aggregate and per-node classification. Confidence is left as the server computed it. Pinned by prefer_board_presence_tests (3). Bin tests 175 -> 178, 0 failed. Co-Authored-By: claude-flow <ruv@ruv.net>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
udp_receiver_taskinwifi-densepose-sensing-serverdecodes only:0xC511_0001raw CSI0xC511_0002edge vitals0xC511_0004fused vitals0xC511_0007WASM outputBut firmware
rv_feature_state.hdocumentsfeature_state(0xC511_0006) as:So a node emitting its default 60-byte compact state (presence / motion / respiration / heartbeat / anomaly / coherence) produces zero UI updates —
/healthstuck attick:0, the UI banner reads OFFLINE — even though frames are arriving on UDP:5005.MEASURED on an ESP32-S3-DevKitC-1: ~5 pps of
0xC511_0006received and silently dropped; after this change the same stream advancestickand drives the sensing UI.Fix
parse_esp32_feature_state(): validates the magic + 60-byte length and maps the packet into the existingEsp32VitalsPacket.RV_QFLAG_PRESENCE_VALIDbit andpresence_score >= 0.5.ts_us → timestamp_ms;respiration_bpm/heartbeat_bpmpassthrough;motion_scoredrives the motion flag..or_else()fallback afterparse_esp32_vitals, so it flows through the unchangedSensingUpdate/ WebSocket path (no downstream churn).Tests
New
feature_state_parser_tests(4):PRESENCE_VALIDis clear (high score, still not present)wifi-densepose-sensing-serverbin tests 171 → 175, 0 failed.Field note (not in this diff)
On a display-less DevKitC the shipped v0.7.0 binary mis-detects a phantom panel (
disp_hal: TCA9554 not found — assuming display power is always-on) →has_display=true→ CSI promiscuous filter stays MGMT-only → 0 pps →DEGRADED. A display-disabled build restores ~30 pps yield.mainalready carries the RuView#1000 touch-probe bail, so this looks like a stale-release / build-variant issue rather than a code regression — flagging it so the headless release binaries can be rebuilt/re-released. No firmware code changed here.🤖 Generated with claude-flow