ruvnet / ruvnet/RuView

release_bins/ ships firmware that yields ~1 pps on ESP32-S3 display boards, blocking calibration

Open
#1,899 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug firmware
Dominant language
Rust
Stars
94.4k
Forks
12.5k
Avg merge
21h 27m
Merged PRs (30d)
43

Description

firmware/esp32-csi-node/release_bins/ ships firmware that yields 1 pps on ESP32-S3 display boards

Summary

The pre-built binaries committed under firmware/esp32-csi-node/release_bins/ are several
releases behind the published GitHub releases. Flashing them onto an ESP32-S3 board that has
an AMOLED panel produces a CSI yield of ~1 pps, which is below
CALIBRATION_GRID_MIN_RATE_HZ = 2.0 — so room calibration can never start, and
/api/v1/vital-signs abstains forever. The node looks alive in every other respect, which
makes this very hard to diagnose.

Upgrading the same board to the published v0.8.8 release fixes it completely
(1 pps → 34 pps). No hardware change.

Environment

Board Waveshare ESP32-S3 AMOLED 1.8" (SH8601 368x448), 16 MB flash, 8 MB PSRAM, chip rev v0.2
Repo commit b370d4f (2026-09-10)
Server ruvnet/wifi-densepose:latest, CSI_SOURCE=esp32
AP 2.4 GHz, channel 1, beacon interval 102400 µs (~9.8 Hz)

Reproduce

  1. Flash release_bins/{bootloader,partition-table,ota_data_initial,esp32-csi-node}.bin
    to an ESP32-S3 with a display panel.
  2. Provision and point it at a host running the sensing server.
  3. Watch the serial log and POST /api/v1/calibration/start.

Observed

Serial log, release_bins/esp32-csi-node.bin:

main: ESP32-S3 CSI Node (ADR-018 / ADR-110) — v0.7.0 — Node ID: 1
csi_collector: Promiscuous mode enabled (MGMT-only, RuView#396)
adaptive_ctrl: medium tick: state=8 yield=0pps motion=0.00 presence=0.00 rssi=-53
adaptive_ctrl: medium tick: state=8 yield=1pps motion=0.00 presence=0.00 rssi=-52

Independent measurement by binding UDP :5005 directly and counting 0xC5110001 frames
(25 s window): 1.32 Hz, matching the firmware's own yield=1pps.

Calibration is therefore unreachable:

POST /api/v1/calibration/start
→ {"success": false, "error_code": "calibration_source_required",
   "error": "Choose exactly one eligible ESP32 source for room calibration.",
   "eligible_sources": []}

GET /api/v1/vital-signs
→ {"authority": "abstained",
   "abstention_reason": "fresh explicit calibration with exactly one occupant and qualified evidence required"}

Generating heavy WiFi traffic (two concurrent ping -i 0.05 floods) does not change the
rate, ruling out a quiet-network explanation.

Root cause

main/main.c:657 picks the promiscuous filter from a compile-time flag plus a runtime panel probe:

#ifdef CONFIG_DISPLAY_ENABLE
    bool has_display = display_is_active();   /* runtime panel probe result */
#else
    bool has_display = false;
#endif
    if (!has_display) {
        csi_collector_enable_data_capture();
    }

A board with a panel keeps the MGMT-only filter (the RuView#396 crash workaround). The
comment in main/csi_collector.c:639 says that is fine because probe injection compensates:

/* MGMT-only gives ~10 Hz (beacons). Probe request injection at 10 Hz
 * adds ~10 Hz probe responses from APs → ~20 Hz total, matching the
 * edge processing designed sample rate of 20 Hz. */

But in the release_bins vintage that compensation does not exist:
csi_inject_ndp_frame() (main/csi_collector.c:889) is an explicit TODO stub, and
grep -rn csi_inject_ndp_frame main/ finds no call site — only a comment in
main/rv_radio_ops_esp32.c:87. So the MGMT-only path runs with nothing making up the
difference, and yield collapses.

The published v0.7.1 release ("CSI self-ping + heart-rate fix") and later address this.
release_bins/ predates it.

Fix verification

Flashing esp32-csi-node-v0.8.8-s3-8mb-flash-bundle.zip from the v0.8.8-esp32 release onto
the same board, same AP, same room:

adaptive_ctrl: medium tick: state=5 yield=33pps dsp=15.6Hz ...
adaptive_ctrl: medium tick: state=6 yield=36pps dsp=19.6Hz motion=1.00 presence=8.95 rssi=-47
csi_collector: CSI cb #900: len=256 rssi=-51 ch=1
release_bins (v0.7.0) v0.8.8
CSI yield 1 pps 34 pps
DSP rate 19.8 Hz
CSI payload 128 B (64 subcarriers) 404 B (192 subcarriers)
motion / presence 0.00 / 0.00 1.00 / 2–20
calibration/start eligible_sources: [] succeeds, rate_hz: 15.08

Display still initialises on v0.8.8, so this is not a display-vs-CSI trade-off.

Secondary problems in the same directory

  1. version.txt does not match the binaries. It says 0.6.7, but every S3/C6 binary
    reports 0.7.0 at boot — except s3-fair-adr110/, which is 0.6.6.
  2. s3-fair-adr110/ is older than the others and predates RuView#893 entirely
    (strings finds no MGMT+DATA/data-capture symbols in it, while the v0.7.0 binaries have them).
    Anyone following the README's display-less guidance to fix low yield will flash a build that
    cannot capture DATA frames and see no improvement — the exact wrong turn this issue's
    reporter took before finding v0.8.8.
  3. s3-fair-adr110/ has no ota_data_initial.bin, so its four-offset install is incomplete.

Suggested resolution

  • Refresh release_bins/ from the current release, or delete it and point the README at the
    GitHub releases page as the single source of truth.
  • Make version.txt generated rather than hand-maintained.
  • If release_bins/ stays, add a boot-time or docs-level warning that a display-equipped S3
    on a pre-0.7.1 build will starve at ~1 pps.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with firmware/esp32-csi-node/release_bins/, version.txt, and the README, then compare the bundled artifacts with the published v0.8.8 release. Inspect main/main.c:657, main/csi_collector.c:639 and :889, and main/rv_radio_ops_esp32.c:87 to understand the reported display-board path. Done means the chosen release-bin or documentation resolution is consistent and a display-equipped board no longer remains below the calibration threshold.

Written by the indexing model from the issue text.

Assessment

Tech stack
c
Domain
documentation, embedded-iot, release
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.