Skip to content

panel: visual ISP programming indicator — progress bar + post-flash smiley#26

Merged
mbreiser merged 8 commits into
mainfrom
mreiser/isp-progress-indicator
Jul 24, 2026
Merged

panel: visual ISP programming indicator — progress bar + post-flash smiley#26
mbreiser merged 8 commits into
mainfrom
mreiser/isp-progress-indicator

Conversation

@mbreiser

@mbreiser mbreiser commented Jul 17, 2026

Copy link
Copy Markdown

Rescues the ISP visual-indicator commit that got orphaned by a stacked-merge mishap: PR #18 merged into panel-isp-twopio at 17:09 on 2026-07-02 — twelve minutes after PR #15 had already snapshotted that branch into main (16:57). So the feature has been sitting merged-but-unreachable ever since; panel-fw-v1.0.0 and v1.1.0 both lack it. This PR cherry-picks 23d66af cleanly onto post-#22/#19 main.

What it does

During an arena ISP push the panel draws a sweeping progress bar (central 10 rows) as pages stage, a full bar at OTA commit, and — via a /just_flashed LittleFS marker — a smiley on the new image's first boot, held until the first real host display command retires it. A technician watching a wall of panels sees per-panel success at a glance; failed panels show no smiley.

Validation status

Already hardware-validated 2026-07-02 on the live arena: panels 18 and 11, double ISP flash each — bar sweeps during staging, smiley right-side-up on first boot, operator-confirmed. (webDisplayTools' handoff doc still says "arena ISP demo pending" — that line predates the 7/2 session and is stale.) Panels 18 + 11 have been running this build (23d66af) in the fleet since.

At this head (cherry-pick onto v1.1.0 main): clean pick — no conflicts with #22's messenger.cpp changes — and clean-rebuilds pass for pico_v021, pico_v031, pico_v021_spidiag, pico_v031_spidiag.

Suggested cheap re-confirmation before the next tag: one arena ISP double-flash of a panel with this build's -isp.bin (first flash silent → smiley; second flash shows bar → smiley), per the original test plan.

🤖 Generated with Claude Code

…miley

While an ISP session stages pages, the panel shows a progress bar across the
central 10 rows (Persistent Gray_2, duty 128), pushed into the display queue
exactly like Messenger patterns (drain-to-latest keeps it shallow; ~20 pushes
per flash, only on column changes). The panel doesn't know the image total up
front, so the bar is scaled to a nominal 512 pages (~128 KiB); larger images
wrap and refill — determinate for real images, indeterminate fallback for
free. COMMIT pushes a full bar before the OTA staging writes.

On COMMIT success the panel drops a /just_flashed marker in LittleFS before
rebooting. The freshly-installed image's first boot detects the marker and
shows a smiley (Persistent) — a stronger success signal than "flash written":
it proves the new image boots and drives the display. The marker is retired
on the first host display command (COMM_CHECK and ISP opcodes don't count),
so the smiley persists across power cycles until the panel is actually used.

Safety notes: no display push in the ISP_ENTER handler (tight phase-A/B
window); boot_indicator_check runs from the production loop, never setup()
(LittleFS ops idle the other core), and mounts with autoFormat OFF so a
never-ISP'd panel's FS region stays untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mbreiser
mbreiser marked this pull request as ready for review July 17, 2026 13:24
@mbreiser
mbreiser requested a review from floesche July 17, 2026 14:48
Review fixes on the LAB-44 visual indicator (stacked on #26):

- Retire the boot-indicator flag AFTER arming the CIPO confirmation:
  the one-shot LittleFS erase (tens of ms, core 1 parked) no longer
  delays the triggering command's reply. It can still stall the NEXT
  transaction; after an ISP flash, send a throwaway display command
  before starting a stream.
- Exclude ERROR_DISPLAY (0xC2) from retiring the flag; only content
  commands count (retires_boot_indicator).
- kNominalPages 512 -> 576: real images (up to ~140 KB / ~550 pages)
  now fill the bar near-linearly without wrapping.
- Failed ISP_COMMIT (status 8) shows a sad smiley instead of leaving
  the misleading full progress bar.
- WRITE_PAGE: bounds-check the 24-bit page index itself so a huge
  index cannot wrap the 32-bit offset math past the staging limit.

Testability:

- Extract the pure logic (staging constants, bar math, retire filter,
  glyphs) into Arduino-free src/isp_logic.h, shared by isp.cpp,
  messenger.cpp, and the tests.
- protocol.h no longer includes Arduino.h (stdint/stddef suffice) so
  the tests can compile it host-side.
- New Unity tests (panel/test/test_isp_logic, 14 cases) on the native
  platform via `pixi run test`, using the system C++ compiler
  (conda's cxx-compiler breaks on the space in this repo's path).
- New ci.yml runs the tests + the release catalog build on every PR
  and push to main.

All 10 firmware envs rebuild clean; 14/14 native tests pass.

@floesche floesche left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My suggestions how to improve this are in #27.

floesche added 2 commits July 21, 2026 15:47
The LAB-44 bar now spans every phase the panel can physically show,
weighted roughly by wall-clock:

- WRITE_PAGE upload fills columns 0..16 (progress_cols rescaled to a
  16-column segment; oversize images still wrap within it)
- VERIFY_STAGED entry lights column 17
- COMMIT's LittleFS staging animates 17..20, pushed between 4 KB flash
  chunks (core 1 scans between ops); full bar = staged, reboot
  imminent. The old pre-staging full-bar push is gone.
- The post-reboot OTA-copy window stays dark (the OTA stub runs before
  the framework); the boot smiley remains the success signal and the
  sad smiley the failure signal.

All pushes go through a shared push_progress_if_changed throttle;
segment constants and math live in isp_logic.h with the other pure
logic.

VERIFY_STAGED now CRCs the staged image through the same 4 KB
PSRAM->SRAM burst buffer the COMMIT path uses (hoisted to a shared
burst_buf_ with a crc32_psram helper), replacing the per-byte XIP scan
and its stale "memcpy bursts mis-read this PSRAM" warning; the bursts
are hardware-proven by post-flash CRC verification.

Tests: existing progress_cols cases retuned for the 16-column upload
segment; new cases cover segment layout and the commit-fill math
(endpoints, monotonic 4 KB sweeps over indivisible totals, degenerate
inputs). 18 cases pass via `pixi run test`.

Fleet-validated on the live 20-panel arena: three full batches were
flashed with this firmware on the receiving side (bar sweep, commit
animation, smiley/sad-smiley behavior), every panel CRC MATCH, verify
phase ~52 ms end to end.
Two sources of spurious PE02/PE03 error glyphs:

- The controller's CS lines each gate two panels (one per SPI bus), and
  single-panel ops like ISP clock only the target's bus, so the
  bus-paired panel (180 degrees opposite in the ring) sees ~1100
  clockless CS pulses per flash. Production builds treated the
  resulting 0-byte reads as messages and raised PE02/PE03.
- At arena power-on the controller's pins are high-Z until its SPI init
  runs, so floating CS/SCK can drift across logic thresholds and clock
  junk transactions into the panels.

Both are fixed panel-side:

- Messenger::update() returns on a 0-byte read in ALL builds
  (previously an SPI_DIAG-only path for the CS-idle timeout): a
  clockless envelope carries no data and is not a message. Real data
  loss is still caught controller-side (page CRC + CIPO confirmation).
- The CS and SCK inputs get internal pull-ups so an undriven bus parks
  at its idle state (CS high: the PL022 ignores everything; SCK high:
  MODE3 idle level).

A companion controller fix (Arena-Firmware) preloads the CS output
registers HIGH before pinMode(OUTPUT), removing the boot-time CS-low
pulse.

Hardware-confirmed for the flash-time case: a full 20-panel batch with
this build on the fleet showed no opposite-panel glyphs. The power-on
case awaits confirmation across a few arena power cycles.
@floesche floesche mentioned this pull request Jul 21, 2026
6 tasks
@mbreiser

Copy link
Copy Markdown
Author

Integration pass before merge: folded in the stacked review PRs #27 and #28 (as floesche's #26 review intended), then merged current main — which now carries the EINT_ACTIVE_LOW trigger-polarity switch from #29 — into this branch (clean merge, no conflicts; the [env:native] block and the pico_v0*_eintlow envs coexist).

Verified on the integrated head bba1ab5:

  • pixi run test: 18/18 native isp_logic cases pass
  • pico_v021, pico_v031, pico_v021_eintlow, pico_v031_eintlow all build clean

Hardware validation was already done on the arena per #26/#28 (three full 20-panel batches, CRC MATCH, smiley/sad-smiley confirmed), and the active-low EINT build was rig-confirmed separately today. Merging.

🤖 Generated with Claude Code

@mbreiser
mbreiser merged commit 9014b5b into main Jul 24, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants