fix(#505,#506): bump firmware to v0.6.4 + no-signal UI indicator

Fixes #505 — version string mismatch:
- firmware/esp32-csi-node/version.txt: 0.6.2 → 0.6.4
- .github/workflows/firmware-ci.yml: add verify-embedded-version step
  that extracts the esp_app_desc version string from the built binary and
  fails CI if it doesn't match version.txt. Prevents the v0.6.3 regression
  (tagged without bumping version.txt) from recurring.

Fixes #506 — stale skeleton shown after ESP32 unplugged:
- csi_pipeline.rs: add CsiPipelineState::last_csi_received (Instant),
  stamped on every process_frame() call; PipelineOutput gains csi_live:bool
  (true iff a real frame arrived within the last 5 s).
- stream.rs: /api/splats now includes csi_live in the JSON response.
- viewer.html: add #no-signal banner (red, centered overlay) shown when
  the server reports csi_live=false in live/remote transport mode.
  Skeleton is cleared while the banner is visible so stale pose data
  is not presented as live activity.

Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
ruv
2026-05-06 13:28:45 -04:00
parent e7904786f0
commit 2dfb4884be
5 changed files with 72 additions and 3 deletions
@@ -219,10 +219,12 @@ async fn api_splats(State(state): State<Arc<AppState>>) -> Json<serde_json::Valu
let splats = state.latest_splats.lock().unwrap();
let frames = *state.frame_count.lock().unwrap();
let pipeline = state.latest_pipeline.lock().unwrap();
let csi_live = pipeline.as_ref().map(|p| p.csi_live).unwrap_or(false);
Json(serde_json::json!({
"splats": &*splats,
"count": splats.len(),
"live": state.use_camera,
"csi_live": csi_live,
"frame": frames,
"pipeline": &*pipeline,
"timestamp": chrono::Utc::now().timestamp_millis(),