Skip to content

Cyberdeck demo suite, scrolling menu, build helpers, upstream fixes#9

Open
Afiyetolsun wants to merge 13 commits intofirefly:mainfrom
Afiyetolsun:feat/cyberdeck-suite
Open

Cyberdeck demo suite, scrolling menu, build helpers, upstream fixes#9
Afiyetolsun wants to merge 13 commits intofirefly:mainfrom
Afiyetolsun:feat/cyberdeck-suite

Conversation

@Afiyetolsun
Copy link
Copy Markdown

Summary

Adds a polished suite of cyberpunk-themed demo panels to the Pixie
menu, converts the menu to a scrollable viewport, ships build.sh /
flash.sh helpers, and lands six small fixes that were blocking any
clean build of main against the currently-pinned submodules.

After this PR, git clone --recurse-submodules && ./build.sh && ./flash.sh
boots end-to-end on a Pixie DevKit rev.6 with ESP-IDF v5.5.4.

New panels

All four panels are composed entirely from firefly-scene primitives
(boxes, labels, the existing arrow sprite). No new image assets, no
new flash usage beyond a few KB of code.

  • Cyber Pulse (panel-cyber.c) — 8 neon equalizer bars driven by
    per-column triangle-wave oscillators with unique phase/period offsets,
    plus a translucent CRT scanline sweep.
  • Life Grid (panel-life.c) — 16×16 Conway's Game of Life with
    toroidal wrap. Four seeds (glider / pulsar / R-pentomino / random),
    N/S cycles seed, OK pauses, Cancel exits.
  • Byte Stream (panel-bytes.c) — Matrix-style falling glyph rain.
    12 columns with random period and phase, opacity-faded trails, single
    riding glyph per head.
  • Sys Stats (panel-stats.c) — Live system telemetry (uptime,
    free heap, render FPS, chip rev, IDF version, build hash) with a
    60-sample free-heap timeline. Uses ffx_sceneLabel_setTextFormat
    for live value updates.

Menu refactor

panel-menu.c now supports a scrolling viewport (3 visible rows of
7 items) so the new entries fit alongside Wallet / GIFs / Le Space.
The previously-disabled GIFs entry is re-enabled. Top/bottom neon
rules give it a cyberpunk frame consistent with the new panels.

Build helpers

  • build.sh wraps the documented Docker one-liner with preflight
    checks, submodule init, and IDF_IMAGE pinning (default
    espressif/idf:v5.5.4; latest is now 6.x and won't bootstrap on
    this project). Auto-cleans build/ when the IDF image changes.
  • flash.sh auto-detects the serial port and prefers host
    esptool.py (one pip install esptool) over the Docker
    --device passthrough, which doesn't work on macOS. --monitor
    falls back through idf.pyesp-idf-monitor → docker (linux
    only) → screen.

Upstream fixes (these blocked any clean build today)

The pinned submodule SHAs and main had drifted apart in ways that
broke the build outright. None of these are speculative — each one
was a hard error or boot-loop encountered while bringing this branch
up on hardware:

  1. main.c calls ffx_init with 3 args but the pinned hollows
    API takes 4 (firmware version was added). Now passes
    FFX_VERSION(0, 2, 0).
  2. panel-tx.c references COLOR_BACK, which is not defined
    anywhere; replaced with COLOR_NAVONLY (semantically correct
    per firefly-hollows.h's "navigation-only" comment).
  3. firefly-hollows/src/task-ble.c is missing
    #include "hollows.h"
    at the pinned commit, so TaskBleInit
    is undeclared. Patched in place by build.sh.
  4. firefly-hollows/src/hollows.c:132 is missing a trailing comma
    in the TaskBleInit designated initializer; without it the next
    .ready = … line is parsed as a member access on the integer
    version. Patched in place by build.sh.
  5. NimBLE in IDF v5.5 has an internally inconsistent
    BLE_HS_DEBUG
    BLE_HS_DBG_ASSERT is active but the
    bookkeeping in ble_hs_lock_nested that would mark the owning
    task is gated separately, so ble_hs_locked_by_cur_task() always
    returns false and ble_hs_id_addr panics on the first call from
    onSync. The cyberdeck demos don't need BLE, so build.sh
    patches taskBleFunc to vTaskDelete(NULL) immediately after it
    signals ready. The wallet panel needs BLE but is non-functional
    on this commit regardless. A proper fix belongs in a hollows
    commit that adapts to the IDF NimBLE config.
  6. espressif/idf:latest is now 6.x and the component manager
    fails to bootstrap on a project that's never been built with 6.x.
    build.sh pins espressif/idf:v5.5.4 (the IDF version this
    project last targeted, per commit 547c1e1).

All build.sh patches against vendored submodule files are
sentinel-guarded and idempotent; re-runs of build.sh are no-ops
once the patches are in place.

What this PR deliberately does NOT touch

  • No new image assets or video frames.
  • No changes to existing panel-space.c, panel-gifs.c,
    panel-attest.c, or any wallet panel beyond the one-line
    COLOR_BACK fix in panel-tx.c.
  • No changes to submodule pins; vendored fixes are applied in place
    via build.sh so future git submodule update --remote is the
    natural path to dropping them.
  • The crypto and BLE wallet flows are unchanged in source — they're
    just bypassed at runtime via the BLE-skip patch.

Test plan

  • ./build.sh produces build/pixie.bin (~6.2 MB, 11% free in
    the 7 MB factory partition) on macOS with Docker Desktop.
  • ./flash.sh --monitor flashes and the device boots cleanly to
    the menu on a Pixie DevKit rev.6.
  • Original entries (Wallet / GIFs / Le Space) still launch and
    return to menu via Cancel.
  • All four new panels enter, animate, and exit via Cancel.
  • Sys Stats uptime, heap and FPS values update live; heap
    timeline scrolls.
  • Build verified on Linux with native ESP-IDF v5.5.4
    (untested by me — no Linux box at hand).

Afiyetolsun added 13 commits May 9, 2026 00:20
…stats)

Adds four new menu entries that showcase the firefly-scene engine and
turn the Pixie into a cyberpunk-styled cyberdeck:

* Cyber Pulse  - 8 neon equalizer bars driven by a triangle-wave
                 oscillator with per-column phase/period offsets,
                 plus a translucent CRT-style scan line.
* Life Grid    - 16x16 Conway's Game of Life with toroidal topology.
                 Four seeds (glider, pulsar, R-pentomino, random),
                 N/S to switch seed, OK to pause/step, Cancel to exit.
* Byte Stream  - Matrix-style falling-character stream, 12 columns
                 with random period/phase, opacity-faded trails, and
                 a single glyph riding each head.
* Sys Stats    - Live system telemetry (uptime, free heap, render FPS,
                 chip rev, IDF version, build hash) plus a 60-sample
                 free-heap timeline. Uses ffx_sceneLabel_setTextFormat
                 for dynamic value updates.

Menu refactor:
* panel-menu now supports a scrolling viewport (3 visible / 7 items),
  with the previously-disabled GIFs entry re-enabled.
* Top/bottom neon rules added for cyberpunk styling consistent with
  the new panels.

Other:
* main.c: pass firmware version to ffx_init (required by the
  current pinned hollows submodule API; main was previously
  out-of-sync with components/firefly-hollows).
* panels.h: add declarations for the new panels and fix
  pushPanelGifs return type (int, matching its definition).

All new code uses only the documented firefly-scene / firefly-hollows
public API. No new image assets - everything is composed from boxes,
labels and the existing arrow sprite.
build.sh wraps the documented Docker one-liner from README.md, with
preflight checks for docker availability and submodule init.

flash.sh handles the full flash without Docker passthrough quirks:
* auto-detects /dev/tty.usbmodem* (macOS) and /dev/ttyACM*/ttyUSB*
  (linux); -p PORT, -b BAUD, ESPPORT/ESPBAUD env vars also honored
* prefers host esptool.py (works without ESP-IDF installed); falls
  back to native idf.py if available
* writes bootloader, partition table and pixie.bin at the correct
  offsets matching partitions.csv
* --monitor flag opens a serial monitor after flashing
espressif/idf:latest now ships ESP-IDF 6.1, which fails to bootstrap
on a project last configured against 5.4.1 (component manager looks
for files that don't exist yet on a fresh 6.x build). Pin to the
project's actual IDF version. Override with IDF_IMAGE env var.
panel-tx.c referenced COLOR_BACK in three places (lines 125, 237, 338)
for the 'BACK' info-button color, but no such symbol exists in the
pinned firefly-color.h. The semantically-correct replacement is
COLOR_NAVONLY, which firefly-hollows.h documents as the back-button
color. Same upstream-vs-submodule drift as the ffx_init signature.

Also bump the default Docker image from v5.4.1 to v5.5.4 - the latest
v5.5 line builds the project cleanly and is better-supported.
Switching ESP-IDF minor versions (5.4 -> 5.5) leaves an incompatible
cmake cache pointing at the previous python_env, producing a 'currently
active env doesn't match' error. Track the image used for the last
build in build/.idf-image and wipe build/ on mismatch.
The pinned firefly-hollows commit (2fbda9d) has a real bug:
src/task-ble.c references TaskBleInit (defined in src/hollows.h) without
including it, so any clean build of this project fails inside the
submodule.

The next upstream commit (1f55b89) adds the missing include, but the
same commit also reshuffles the FfxKey bit assignments. Inheriting that
without a coordinated downstream update is a regression risk we want to
keep out of this PR.

build.sh now applies a narrow, idempotent in-place patch to add the
missing include before invoking docker. Real fix belongs upstream as a
PR against firefly/component-hollows pulling out just the include
change.
components/firefly-hollows/src/hollows.c line 132 reads:

    TaskBleInit init = {
      .version = version       <-- missing comma
        .ready = xSemaphoreCreateBinaryStatic(&readyBuffer)
    };

Without the comma the compiler parses '.ready' as a member access on
the integer 'version', producing 'request for member ready in something
not a structure or union' and the unused-but-set-parameter error on
'version'. Same upstream-bug class as the missing include - apply an
idempotent in-place fix from build.sh.
screen reads raw bytes and prints panic addresses unsymbolicated, which
is useless for debugging boot-time crashes. When neither host idf.py nor
esptool but docker is available, run the monitor inside the same pinned
IDF container - it reads build/pixie.elf and resolves PCs to source
locations.
ESP-IDF v5.5's NimBLE tightened locking on ble_hs_id_copy_addr - it
asserts the caller holds the host lock and panics otherwise. The
pinned firefly-hollows task-ble.c calls it bare from onSync(), so the
device boot-loops with:

  assert failed: ble_hs_id_addr ble_hs_id.c:295 (ble_hs_locked_by_cur_task())

Add ble_hs_lock()/ble_hs_unlock() around the call via build.sh patch.
ble_hs_lock and ble_hs_unlock exist in NimBLE's lib but are not in the
public ble_hs.h on this version, so add inline forward declarations.
Use 'extern void ble_hs_lock' as the idempotency sentinel so re-runs
of build.sh don't double-wrap the call.
Docker Desktop on macOS runs the engine in a Linux VM, so the
--device flag can't reach host /dev/tty.usbmodem* paths and the
container exits with 'no such file or directory'. esp-idf-monitor is a
standalone pip package that does the same panic symbolication as
'idf.py monitor' without needing the full IDF.

Resolution order is now native idf.py, then esp-idf-monitor, then
docker (Linux only), then screen as last resort.
The previous patch only wrapped ble_hs_id_copy_addr, but
ble_hs_id_infer_auto on the line above also goes through ble_hs_id_addr
internally and hits the same lock-held assert first. Move the lock to
cover the entire id-resolution sequence in onSync.
Replaced the (incorrect) host-lock wrapper patch with an early
vTaskDelete in taskBleFunc, just after the bootstrap semaphore is
given. NimBLE in IDF v5.5 has an internally inconsistent BLE_HS_DEBUG
configuration where BLE_HS_DBG_ASSERT is active but the
ble_hs_lock_nested bookkeeping that would mark the owning task is
gated differently, so ble_hs_locked_by_cur_task always returns false
and the assert in ble_hs_id_addr fires right after the BLE host task
starts.

The cyberdeck demos run entirely on the display/keypad path and do not
need BLE. The wallet panel needs it but is broken at this commit
regardless. Real fix belongs upstream as a NimBLE/IDF-version-aware
update of firefly-hollows.
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.

1 participant