fix: ESP32 CSI 0pps (#521), aggregator sibling magics (#517), version.txt (#505) + fix-marker CI guard - #526
Merged
Merged
Conversation
csi_collector_init() never called esp_wifi_set_ps(), leaving the radio on the ESP-IDF STA default WIFI_PS_MIN_MODEM. The modem then sleeps between DTIM beacons; combined with the MGMT-only promiscuous filter (#396) the CSI callback is starved and the per-second yield collapses toward 0 pps, which is what users on a clean multi-node setup were seeing (motion=0.00 presence=0.00 yield=0pps). Force WIFI_PS_NONE before enabling promiscuous mode — the textbook requirement for reliable CSI capture (every ESP-IDF CSI example does it). New boot line: "csi_collector: WiFi modem sleep disabled (WIFI_PS_NONE) for CSI capture". Battery duty-cycling is unaffected: power_mgmt_init() runs after this and re-enables modem sleep when provision.py is given --duty-cycle <100. Builds clean for esp32s3 (idf.py build, 48% flash free). Closes #521 Co-Authored-By: claude-flow <ruv@ruv.net>
…517) The ESP32 firmware multiplexes several wire packet types onto the same UDP port as ADR-018 raw CSI frames (magic 0xC5110001): 0xC5110002 ADR-039 edge vitals (32 B) 0xC5110003 ADR-069 feature vector 0xC5110004 ADR-063 fused vitals 0xC5110005 ADR-039 compressed CSI 0xC5110006 ADR-081 feature state 0xC5110007 ADR-095/#513 temporal classification Esp32CsiParser only knew 0xC5110001, so the standalone `aggregator` binary printed "parse error: Invalid magic: expected 0xc5110001, got 0xc5110002" for every vitals packet. No CSI data was lost — just noise. Add the sibling-magic constants + ruview_sibling_packet_name(), classify recognized siblings before the CSI-frame length gate, and return a new ParseError::NonCsiPacket { magic, kind } instead of InvalidMagic. The `aggregator` CLI now skips them quietly (logs "[skipped ADR-039 edge vitals packet — not a CSI frame]" only with --verbose); the library-level CsiAggregator already dropped them silently. New regression tests cover all seven magics. Closes #517 Co-Authored-By: claude-flow <ruv@ruv.net>
…atch (#505) version.txt on main was still 0.6.2. CMake reads PROJECT_VER from it, so esp_app_get_description()->version (and the boot log line) reported 0.6.2 for any source build — and v0.6.3-esp32 shipped a release binary that internally identified as 0.6.2 because the bump never landed on main. - version.txt: 0.6.2 -> 0.6.4 (matches the latest release tag) - firmware-ci.yml: new `version-guard` job that runs on v*-esp32 tag pushes and fails the run if the tag's X.Y.Z != version.txt, so a future release can't ship a mislabeled binary. Closes #505 Co-Authored-By: claude-flow <ruv@ruv.net>
Adds a fast per-PR gate that asserts previously-shipped fixes are still present in the tree — the CI analogue of the ruflo witness fix-marker system, but self-contained (no plugin dependency, reviewable as plain JSON). Complements the heavier checks (firmware build, deterministic pipeline proof, release witness bundle) by catching the silent-revert class of regression that build+test wouldn't. - scripts/fix-markers.json manifest: 11 markers (RuView#396, #521, #517, #505, #354, #263, #266/#321, #265, #232/#375/#385/#386/#390, ADR-028 proof + witness bundle). Each has files / require (literal substring or /regex/) / optional forbid / rationale / ref. - scripts/check_fix_markers.py stdlib-only checker. Exit 0 clean / 1 regression / 2 bad manifest. Modes: --list, --json, --only ID. - .github/workflows/fix-regression-guard.yml runs on PR + push to main/master; gates on the checker and writes the result table into the run summary + an artifact. If a fix is intentionally removed, update scripts/fix-markers.json in the same PR with a rationale — the diff becomes the audit trail. Co-Authored-By: claude-flow <ruv@ruv.net>
Owner
Author
Hardware-verified on a real ESP32-S3 (COM8,
|
Open
3 tasks
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.
Three open-issue fixes plus a new CI regression guard. Split into four reviewable commits.
Fixes
fix(esp32)— disable WiFi modem sleep so CSI capture isn't starved — closes #521csi_collector_init()never calledesp_wifi_set_ps(), so the radio stayed on the ESP-IDF STA defaultWIFI_PS_MIN_MODEM. The modem then sleeps between DTIM beacons; combined with the MGMT-only promiscuous filter (#396) the CSI callback is starved and the per-second yield collapses toward 0 pps — exactly themotion=0.00 presence=0.00 yield=0ppssymptom reported on clean multi-node setups. Now forcesWIFI_PS_NONEbefore enabling promiscuous mode (the textbook requirement for reliable CSI capture). New boot line:csi_collector: WiFi modem sleep disabled (WIFI_PS_NONE) for CSI capture. Battery duty-cycling is unaffected —power_mgmt_init()runs after this and re-enables modem sleep whenprovision.py --duty-cycle <100is used. Builds clean for esp32s3 (idf.py build, 48% flash free). Needs hardware verification on a real ESP32-S3 before the nextv*-esp32tag.fix(hardware)— aggregator tolerates sibling RuView UDP packet magics — closes #517The firmware multiplexes
0xC5110002–0xC5110007(vitals, feature, fused, compressed, feature-state, temporal) onto the same UDP port as ADR-018 raw CSI frames (0xC5110001).Esp32CsiParseronly knew0xC5110001, so the standaloneaggregatorbinary printedparse error: Invalid magic: expected 0xc5110001, got 0xc5110002for every vitals packet — noise, not data loss. Adds the sibling-magic constants +ruview_sibling_packet_name(), classifies recognized siblings before the CSI-frame length gate, and returnsParseError::NonCsiPacket { magic, kind }instead ofInvalidMagic. TheaggregatorCLI now skips them quietly (--verbose→[skipped ADR-039 edge vitals packet — not a CSI frame]); the library-levelCsiAggregatoralready dropped them silently. New regression tests cover all seven magics.cargo test -p wifi-densepose-hardware --no-default-features→ 116 passed.fix(firmware)— bumpversion.txtto 0.6.4 + CI guard for tag↔version — closes #505version.txtonmainwas still0.6.2; CMake readsPROJECT_VERfrom it, soesp_app_get_description()->version(and the boot log) reported0.6.2for any source build, and the v0.6.3-esp32 release binary internally identified as 0.6.2. Bumpsversion.txt→0.6.4and adds aversion-guardjob tofirmware-ci.ymlthat runs onv*-esp32tag pushes and fails the run when the tag'sX.Y.Z≠version.txt.New:
ci— fix-marker regression guard (witness-style)A fast per-PR gate that asserts previously-shipped fixes are still present in the tree — the CI analogue of the ruflo witness fix-marker system, but self-contained (no plugin dependency, reviewable as plain JSON). Complements the heavier checks (firmware build, deterministic pipeline proof, release witness bundle) by catching the silent-revert class of regression that build+test wouldn't.
scripts/fix-markers.json— 11 markers: RuView#396, All ESP32-S3 nodes report motion=0.00 presence=0.00 yield=0pps despite correct setup #521, aggregater cannot parse some csi frame from firmware 0.6.4 #517, v0.6.3-esp32 release ships a binary that internally identifies as 0.6.2 #505, Desperately seeking help with ESP32-S3 tracking setup #354, [Bug/Enhancement]: Default fall_thresh (500) causing excessive false positives in high-traffic environments #263, Unable to receive the data from esp32 #266/esp32 node crash #321, 4MB flash support? #265, fix(esp32): use runtime node_id from NVS in outgoing packets #232/Firmware sends node_id=1 for all nodes (relates to #232) #375/Multi-node ESP32-S3 repro on Windows: 6 provisioned nodes transmit, but every UDP packet decodes as node_id = 1 #385/Live UI connects, packets flow, but pose does not move correctly with 6 ESP32-S3 nodes on Windows #386/ESP32-S3: g_nvs_config.node_id clobbered to 1 between main.c:140 and csi_collector_init + LoadProhibited panic loop #390, and the ADR-028 proof + witness-bundle artifacts. Each hasfiles/require(literal substring or/regex/) / optionalforbid/rationale/ref.scripts/check_fix_markers.py— stdlib-only checker. Exit0clean /1regression /2bad manifest. Modes:--list,--json,--only ID …. Runs on Windows + Linux..github/workflows/fix-regression-guard.yml— runs on PR + push tomain/master; gates on the checker, writes the result table into the run summary + an artifact.If a fix is intentionally removed, you update
scripts/fix-markers.jsonin the same PR with a rationale — the diff becomes the audit trail.Pre-merge notes
esp_wifi_set_ps(WIFI_PS_NONE)) on a real ESP32-S3 before tagging a releaseCHANGELOG.mdentry under[Unreleased](Added: fix-marker guard; Fixed: All ESP32-S3 nodes report motion=0.00 presence=0.00 yield=0pps despite correct setup #521/aggregater cannot parse some csi frame from firmware 0.6.4 #517/v0.6.3-esp32 release ships a binary that internally identifies as 0.6.2 #505)CLAUDE.mdreferencingpython scripts/check_fix_markers.py🤖 Generated with claude-flow