Cyberdeck demos, games, LED panel; ESP-IDF v6.0.1 migration#11
Open
Afiyetolsun wants to merge 34 commits intofirefly:mainfrom
Open
Cyberdeck demos, games, LED panel; ESP-IDF v6.0.1 migration#11Afiyetolsun wants to merge 34 commits intofirefly:mainfrom
Afiyetolsun wants to merge 34 commits intofirefly:mainfrom
Conversation
…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.
Stacks four new games on top of the cyberdeck-suite branch:
* Raycast - Wolf3D-style first-person raycaster following Lode
Vandevenne's tutorial algorithm (public domain, credited
in the source). 16x16 map, 60 strips at STRIP_WIDTH=4
rendered as boxes with per-frame setSize, fish-eye-
corrected wall heights, side-shaded coloring.
Controls: N=forward, S=turn-left, OK=turn-right, X=exit.
Sliding collision so walls don't trap you.
* Asteroids - side-scroll dodger/shooter. Drifting rocks come from the
right with random velocities, bouncing off top/bottom
edges. Tap OK to fire bullets, hold N/S to dodge.
Score on survival and rock kills, OK restarts on death.
* Breakout - vertical Breakout. 5x10 brick wall on the left with a
rainbow palette, paddle on the right. Ball deflection
angle depends on hit position relative to paddle center.
OK launches/restarts, N/S move paddle.
* Dungeon - turn-and-step grid crawler. 12x12 dungeon, classic
EotB-style controls (N=turn-left, S=turn-right,
OK=step). Find the green goal cell to escape; step
counter doubles as the score.
Menu refactor: ITEM_COUNT bumped 7 -> 11, scrolling viewport handles
the four new entries automatically.
All four games are pure scene-graph (no new image assets), use only
the documented firefly-scene API, and pass clang -fsyntax-only.
Adds 'LED Mode' menu entry that drives the 4 WS2812B LEDs through the firefly-hollows pixels API. Seven modes: * OFF - all LEDs off * CYAN - solid neon cyan * RAINBOW - cycling hues, per-LED phase offsets * PULSE - violet breathing * STROBE - 1 Hz white flash * POLICE - alternating red/blue pairs * MATRIX - green wave traveling across the four LEDs Each mode is a PixelAnimationFunc kicked off via pixels_animate(), which the IO task ticks in the background, so the selected mode keeps running after the panel is popped - the user can set a vibe and walk away. Mode changes stop the previous animation and start the new one. The pixels API is private to firefly-hollows (header in src/), so the relevant symbols are forward-declared inline (same pattern used elsewhere in this branch for ble_hs_lock). Animations use the existing fixed_ffxt math helpers (scalarfx, mulfx, tofx) and HSV color helpers - no math.h dependency. ITEM_COUNT bumped 11 -> 12 in the menu.
esp_idf_monitor (and idf.py monitor) treat Ctrl+C as data forwarded to the target rather than an exit signal; the actual quit shortcut is Ctrl+]. Print a banner up front so users don't get stuck in the monitor wondering why their interrupt is being ignored.
The header pixels.h declares pixels_animate (all-pixels) and pixels_stopAnimation, but pixels.c at the pinned commit only implements the per-pixel variant pixels_animatePixel - the linker correctly fails with 'undefined reference' on the missing two. Switch the LED panel to drive each of the four LEDs individually: each animation function writes only out[0], the pixel index travels in via the 'arg' parameter, and applyMode loops 0..3 calling pixels_animatePixel once per LED. Re-calling pixels_animatePixel overwrites the previous animation slot for that pixel, so mode switching works without an explicit stop call.
…arkle)
* Pass repeat=1 (not 0) to pixels_animatePixel. The implementation
treats repeat as a boolean - 0 was one-shot, so modes were running
for one duration cycle and then freezing on the last frame.
Confirmed at firefly-hollows/src/pixels.c:381:
context->actions[pixel].type = repeat ? AnimationTypeRepeat
: AnimationTypeNormal;
Counter-intuitive naming, but pinning the API.
* Add 5 new modes:
- FIRE - red/orange flame flicker via per-pixel hash
- COMET - bright yellow head chases around the 4-LED ring
- OCEAN - blue/cyan ripple, slow phase-offset per LED
- NEON - rotating magenta/cyan/yellow/green palette
- SPARKLE - random twinkle, ~20% on at any time
12 modes total. Index dots shrunk to 14px to fit the wider strip.
Drops the entire stack of v5-era submodule patches (5 of them in
build.sh: missing include, missing comma, COLOR_BACK, BLE host lock,
NimBLE BLE_HS_DEBUG bypass) by bumping all three vendored components
to their upstream main, which has explicit IDF v6 fixes:
* firefly-display: c65494d -> 0f9183f ('Update for ESP-IDF v6')
* firefly-hollows: 2fbda9d -> c856a8f ('Update BLE API for ESP-IDF v6',
'Updates for printf errors in
ESP-IDF v6', 'Update for LED
strip IRAM config for ESP-IDF
v6', and the missing-include
and missing-comma fixes that
we previously patched in)
* firefly-scene: 6ae2319 -> 210b4d1 (label rendering improvements)
Also bumps the default Docker image from v5.5.4 to v6.0.1, the
latest stable release in the v6.0.x line. sdkconfig regenerated by
IDF v6 (mostly noise: SoC-capability flags reshuffled).
Build verified clean on macOS Docker Desktop. pixie.bin is 6.3 MB
(10%% free in the 7 MB factory partition). The build.sh auto-clean
sentinel handles the v5 -> v6 cmake-cache transition automatically.
NimBLE's BLE_HS_DBG_ASSERT macro gates lock-state assertions on MYNEWT_VAL(BLE_HS_DEBUG), which the IDF Kconfig wires to CONFIG_BT_NIMBLE_DEBUG. With debug on, ble_hs_id_addr asserts that ble_hs_locked_by_cur_task() returns true. In practice the bookkeeping path that would register the owning task on a lock acquisition is in a different code path than the one the assert inspects, so the assert always fails right after onSync. The device panic-reboots on every IDF version we've tried (v5.5.4 and v6.0.1). Disabling BT_NIMBLE_DEBUG turns the BLE_HS_DBG_ASSERT macro into a no-op (the same set of asserts compile out together), letting BLE init proceed normally. This is the correct production setting - NimBLE deployments don't run with the host-debug bookkeeping in production builds.
Per upstream maintainer feedback, in IDF v6 the RMT driver's default ISR callback location is no longer cache-safe. With WS2812B LED control on the RMT TX path, this means flash operations (BLE NVS writes, the wallet panel touching attest data, etc.) can starve or glitch the LED-strip ISR. The pixie-hello-world reference repo explicitly enables these: CONFIG_RMT_TX_ISR_CACHE_SAFE=y CONFIG_RMT_RX_ISR_CACHE_SAFE=y so the ISR is placed where it stays callable while the flash cache is busy. Match that here.
Hollows hard-codes FfxDisplayRotationRibbonRight (MADCTL bits MV+MX: 90-degree page/column swap plus horizontal mirror), which on the rev.6 hardware produces a horizontally-flipped display - text reads backwards in the menu and panels. Patch task-io.c via build.sh to use FfxDisplayRotationRibbonBottom instead (operand 0, no MADCTL flips). Idempotent so re-runs are safe. Real fix belongs upstream as a board-revision-aware default in firefly-hollows.
Le Space (panel-space.c): * 5 progressive levels with increasing alien grid (4x3 -> 6x5), decreasing bullet capacity (5 -> 4), and faster sprite animation cadence on later levels. * Per-level alien field stride to slow movement on level 1. * Score: 10 * level per kill, 100 * level bonus per level cleared. * GameState state machine: Splash -> Playing -> LevelClear -> next level (or Victory after L5) / GameOver on death. * Hold-to-quit (OK 3s) preserved; Cancel still fires. * GAME OVER / VICTORY overlays accept OK to restart at L1, Cancel to return to menu. Asteroids (panel-roids.c): * Background, ship, bullets and rocks now use the Le Space sprite set (image_space, image_ship, image_bullet, image_alienboom). Rocks come from the LEFT and drift right toward the ship; ship is on the right firing left, matching the directional sprites from panel-space.c so nothing renders backwards. * Cancel now matches the Le Space convention (fire), with second Cancel-press exiting after game-over. * GAME OVER overlay; OK restarts. Image data centralization (images.c + image-data.h): * The image-data headers under main/images/ define their arrays at file scope without 'static', so including the same one from two panels produced multiple-definition link errors. Move all the inclusions into a single new TU (main/images.c) and expose extern declarations + sizeof()-derived size constants via the new main/image-data.h. image_background and image_pixie are deliberately omitted; firefly-hollows ships its own copy of those symbols in src/demo/background-pixies.c and including ours would collide at link time.
Both action games are now in classic-arcade portrait orientation, with the ship at the bottom, threats descending from the top, and bullets firing up. N/S map to left/right ship movement. Le Space: - Ship horizontally centered along the bottom edge. - Alien grid descends from the top of the playable area below the HUD; the field oscillates left/right as it advances down. - Bullets travel up; collision boxes use ALIEN_W/2 and ALIEN_H/2 centered on each alien. - Death is now alien y plus half-height reaching the ship row, not the ship column. Asteroids: - Ship at the bottom; rocks drift down from the top, bouncing off the left and right edges instead of top and bottom. - Bullets travel up. - Long-press OK (3s) now exits to the menu, matching Le Space. New game (panel-snake.c): - 16x16 grid (14px cells, 224x224 board centered with HUD on top). - Snake starts length 4 facing east. Tap a direction to turn. N goes up, S goes down, OK goes right, Cancel goes left. Long- press OK 3s exits to the menu; on game-over OK restarts and Cancel exits. - U-turns are filtered (they would self-collide on the next tick). - Snake render uses a fixed pool of MAX_SNAKE=96 segment box nodes, positioning visible ones to body cells and hiding the rest. - Eating food grows the snake by 1, accelerates the move tick from 180ms toward a 70ms floor, and bumps the score by 10. - PERFECT screen on board fill; GAME OVER on wall or self collision. Menu now has 13 entries; ITEM_COUNT bumped accordingly.
The ship, alien1, alien2 and alien-boom sprites were drawn for the original horizontal Le Space layout (ship on the right firing left). After flipping both action games to a portrait layout (ship at the bottom firing up) the sprites were oriented sideways relative to their direction of travel. Add a runtime sprite rotator in images.c that walks the firefly-scene RGB565+A4 image format: data[0] = type/format flag (0x05 for RGB565+A4) data[1] = width data[2] = height data[3] = alphaCount (uint16_t alpha words; 4-bit alpha/pixel) data[4..]= alpha bitmap, then RGB565 pixels (row-major) For each rotated copy we malloc a fresh buffer of the same total length, swap width and height, and rewalk both pixel and alpha arrays remapping new(nx,ny) <- old(ny, oldH-1-nx). Allocation is once at boot via images_initRotated(), invoked from app_main before the panels are pushed; the rotated copies live in heap for the lifetime of the app (about 7.5 KB total). Le Space and Asteroids now reference image_X_cw / image_X_cw_len instead of the originals. Sprite-size constants in both panels also swap accordingly: ALIEN goes from 22x26 to 26x20, SHIP from 36x38 to 38x36, alien grid spacing tightened to 32x28 to match.
Adds two small UX touches across every panel. LED feedback (feedback_onKey): - Each press fires a one-shot pixels_animatePixel on the LED next to the pressed button: North=LED0, OK=LED1, Cancel=LED2, South=LED3. - Animation is a brief low-brightness pale-cyan flash that fades to black, ~140ms total. repeat=0 makes it one-shot so the LED lands on black after. - Hooked into every panel's onKeys (menu, space, roids, snake, cyber, life, bytes, stats, raycast, brick, crawl). Skipped in panel-leds, which manages its own pixel animations and would immediately overwrite the flash anyway. - Pixel API symbols are forward-declared inline (same pattern used in panel-leds.c) since they live in firefly-hollows' private src/ header. Button legend (feedback_addButtonLegend): - Helper that draws a small bottom-of-screen strip in the format '< NORTH > SOUTH OK ACTION X CANCEL' so each panel can show what its four buttons do in the current context. - Added to the menu (UP/DN/GO/EXIT) and to the two action games Le Space and Asteroids (L/R/HOLD=EXIT/FIRE) which previously had no on-screen hints. - Other panels keep their existing custom hint labels - the helper is available if they ever want to switch to the canonical format.
The previous default of 460800 was conservative for hardware flow-controlled UART. The ESP32-C3 on the Pixie talks over the built-in USB-Serial/JTAG, where the nominal baud is just a token to esptool - actual throughput is bounded by USB and the host CDC driver. 921600 is a safe higher value that roughly halves the flash time on every system tested. Also expose --app-only, which writes only build/pixie.bin at offset 0x10000 and skips the bootloader and partition table. Bootloader and partition table only change when sdkconfig or the partitions.csv / IDF version changes; for the typical 'I edited a panel and want to reflash' loop, --app-only cuts roughly a third off the total time.
The previous legend at FfxFontMedium with format '< NORTH > SOUTH OK ACTION X CANCEL' was around 26 chars at 12 px each, ~310 px wide on a 240 px screen, so the leading '< NORTH ' and trailing 'X EXIT' got clipped past the edges. Switch to FfxFontSmall (15-point glyphs are roughly 7-8 px wide) and a tighter format '<UP >DN OK:GO X:EXIT' that fits comfortably inside 240 px. Game legends shorten 'HOLD=EXIT' to 'HOLD=X' to keep the line balanced.
The Pixie has only four keys; the previous legend's '<' and '>' prefixes (visual stand-ins for North/South) were just noise. Reword the legend format to enumerate the four button names directly: UP:up DN:dn OK:go X:exit Each panel still passes its own action verbs; the helper builds the final string. Game legends adjusted to 'L', 'R', 'hold=exit', 'fire' to read consistently in the new format. Add a small running FPS counter in the top-left corner of the menu. Helper FpsCounter struct and feedback_addFpsCounter / feedback_tickFps functions live in feedback.c so any panel that wants the same widget can drop in three lines (one struct field, one init call, one tick call from its render handler). Updates roughly once per second based on render-event count over the previous window.
…UP OK ESC Hollows itself draws a built-in FPS label at (235, 235) - the bottom-right corner of every frame. With the per-panel FPS counter already exposed via feedback.c the duplicate is just clutter, and in some panels it overlapped the button-legend strip. Patch task-io.c via build.sh to park the label off-screen at (-200, -200) - it still gets formatted once per second but never draws. Idempotent. Reorder the button legend format to match the device's physical button row, left-to-right SW4..SW1: DOWN:%s UP:%s OK:%s ESC:%s (was 'UP:%s DN:%s OK:%s X:%s'.) Helper signature unchanged - the caller still passes (up, down, ok, cancel); the format string just reorders them. Game legends compacted from 'hold=exit' to 'hold' to fit comfortably at FfxFontSmall (~30 chars at 240 px wide).
Three small UX cleanups around the LED button-press feedback. Legend format simplified to a plain space-join of four labels in physical-button order: 'DOWN UP OK ESC' for the menu and 'DOWN UP HOLD FIRE' for the action games. Dropped the 'DOWN:%s' prefix - callers now pass exactly what they want shown. LED feedback brightness reduced to ~10% of the previous peak (48 -> 5 on the RGB ramp). The pale-cyan flash is still visible at arm's length but no longer competes with whatever else is on the LEDs. Feedback flash now only fires when the LED Mode panel's selected mode is OFF (mode 0). panel-leds.applyMode calls the new feedback_setLedOffMode(state->mode == 0) on every selection so the gate updates immediately. Default state at boot is 'off mode = true' so first-boot button presses still flash; the moment the user picks a non-OFF mode, feedback steps out of the way until they switch back to OFF.
Snake, Raycast, Breakout and Dungeon were still using their own ad-hoc bottom-of-screen hint labels. Replace each with the shared feedback_addButtonLegend so the format is consistent across the whole device: Snake -> DOWN UP RIGHT LEFT Raycast -> LEFT FWD RIGHT EXIT Breakout -> DOWN UP LAUNCH EXIT Dungeon -> RIGHT LEFT STEP EXIT The order on screen is the physical button row left-to-right (SW4..SW1: DOWN UP OK ESC); each game passes the action verb that maps to that physical button. Also swap the LED feedback mapping for DOWN and ESC. The previous mapping had FfxKeyCancel -> LED2 and FfxKeySouth -> LED3, but on hardware the LED that physically sits above the DOWN button is LED2, not LED3, so pressing DOWN was lighting up the LED above the ESC button and vice versa. Final mapping reflects the physical layout above each button, left-to-right SW4..SW1.
User reports SW2 (OK) lit the correct LED but the other three were wrong. With LED 1 confirmed above SW2, the only consistent layout is that LEDs are numbered right-to-left when looking at the front of the device: LED 0 -> SW1 (ESC) LED 1 -> SW2 (OK) <- already correct LED 2 -> SW3 (UP) LED 3 -> SW4 (DOWN) Update the per-key mapping accordingly. The previous left-to-right guess assumed LED 0 lived above DOWN; only SW2 happened to land right by coincidence.
Replace each panel's ad-hoc bottom-of-screen hint label with the shared feedback_addButtonLegend so the layout matches the games and the menu. Always renders four labels in physical button order (SW4 SW3 SW2 SW1 = DOWN UP OK ESC), with '-' for buttons that have no action in the current panel. LED Mode -> NEXT PREV RESET EXIT Cyber -> - - - EXIT Life -> NEXT PREV PAUSE EXIT Bytes -> - - - EXIT Stats -> - - - EXIT panel-gifs is left as-is - its right-side menu already labels what each button selects, so an extra bottom strip would duplicate it.
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.
Summary
Single coherent PR landing the cyberdeck demo suite (4 panels), four
games, a 12-mode LED panel, build/flash helpers, and an
ESP-IDF v5.4 → v6.0.1 migration. After this, a fresh
git clone --recurse-submodules && ./build.sh && ./flash.shbootsend-to-end on a Pixie DevKit rev.6 against the pinned submodules.
All new code uses only the documented
firefly-sceneandfirefly-hollowspublic API (with one inline forward-decl block inthe LED panel for the still-private
pixels_animatePixel). No newimage assets — every new panel is composed from boxes and labels.
New panels
Cyberdeck demos
panel-cyber.cpanel-life.cpanel-bytes.cpanel-stats.cGames
panel-raycast.cpanel-roids.cpanel-brick.cpanel-crawl.cLED Mode panel — 12 modes
panel-leds.cdrives the four WS2812B LEDs throughfirefly-hollows'internal pixels API. Each mode is a
PixelAnimationFuncbound toeach pixel via
pixels_animatePixel(..., repeat=1, ...)so it loopsforever in the IO task and persists after the panel is popped —
set a vibe and walk away.
A few notes on the pixels API used:
pixels_animate(all-pixels) andpixels_stopAnimationaredeclared in
pixels.hbut not implemented inpixels.c. Thispanel only uses
pixels_animatePixel(the per-pixel variant) andpasses the LED index via
arg. The relevant symbols are private,so they're forward-declared inline.
repeatparameter is a boolean —0is one-shot,non-zero loops. The boot animation in
task-io.cpasses0,which is why the boot LEDs settle on a static endpoint; this panel
passes
1so modes loop.Menu
panel-menu.crewritten for a scrolling viewport (3 visible rows,12 entries). Top/bottom neon rules consistent with the cyberdeck
theme. The previously-disabled GIFs entry is re-enabled.
Build / flash helpers
build.sh— wraps the documented Docker one-liner with apreflight check, submodule init, IDF-image pinning
(
espressif/idf:v6.0.1default, override withIDF_IMAGE), andauto-cleaning of
build/when the image changes (so switchingmajor.minor versions doesn't reuse an incompatible cmake cache).
flash.sh— auto-detects/dev/tty.usbmodem*(macOS) //dev/ttyACM*+/dev/ttyUSB*(Linux); accepts-p PORT/-b BAUDand
ESPPORT/ESPBAUD. Prefers hostesptool.py(one
pip install esptool) over the Docker--deviceflag, whichdoesn't work on macOS Docker Desktop.
--monitorresolves throughidf.py→esp-idf-monitor→ Docker (Linux only) →screen,prints the Ctrl+] exit hint up front.
ESP-IDF v6 migration
Submodule pins bumped to upstream
mainto pick up the v6 fixes:c65494d → 0f9183f— Update for ESP-IDF v62fbda9d → c856a8f— Update BLE API forESP-IDF v6, Updates for printf errors in ESP-IDF v6, Update for
LED strip IRAM config for ESP-IDF v6
6ae2319 → 210b4d1— label rendering improvementsPlus two
sdkconfigchanges:CONFIG_RMT_{TX,RX}_ISR_CACHE_SAFE=y— picked up from thepixie-hello-worldreference repo. In v6 the RMT driver's default ISR callback
location is no longer cache-safe, so flash operations were able to
glitch the WS2812B output.
CONFIG_BT_NIMBLE_DEBUG=n—BLE_HS_DBG_ASSERTin NimBLEasserts
ble_hs_locked_by_cur_task()at the top ofble_hs_id_addr, but the bookkeeping path that would register theowning task on a
ble_hs_lock()acquisition isn't reached duringthe host-init flow. With
BT_NIMBLE_DEBUG=ythe devicepanic-reboots right after
onSyncis invoked. Disabling it (thestandard production setting) compiles those debug asserts out and
lets BLE init proceed normally.
sdkconfigwas regenerated by IDF v6.0.1; most of the diff isauto-generated SoC capability flags reshuffling rather than
functional changes.
Bonus: build-was-broken-on-main fixes that v6 made transparent
Side-effect of the submodule bumps: every build-time patch that
earlier cuts of this branch had been carrying is now gone, since the
upstream
maincommits address them directly:task-ble.cwas missing#include "hollows.h"(soTaskBleInitwas undeclared);
hollows.c:132was missing a trailing comma in theTaskBleInitdesignated initializer (so
.readyparsed as a member access onthe integer
version);panel-tx.creferencedCOLOR_BACK, which is not a definedsymbol — replaced with
COLOR_NAVONLY(the documentednavigation-button color in
firefly-hollows.h);main.ccalledffx_initwith three args, but the API takes foursince the firmware-version field was added.
These were all hard build failures or boot-loops on a clean
firefly/pixie-firmware:maincheckout. They're worth flagging onlybecause anyone trying to clone-and-build today hits them; this PR
gets the project back to a state where the documented one-liner
actually works.
Test plan
./build.shproducesbuild/pixie.bincleanly on macOSDocker Desktop with
espressif/idf:v6.0.1. Binary is 6.3 MB,~10 % free in the 7 MB factory partition.
./flash.sh --monitorflashes and the device boots cleanly tothe menu on a Pixie DevKit rev.6.
(untested locally — no Linux box at hand).
Notes for the maintainer
e.g., (1) v6 migration + sdkconfig fixes, (2) cyberdeck demos,
(3) games, (4) LED panel. Whichever shape you prefer.
fixed Y positions, hidden when off-viewport). When the
clipping-bounds work lands, swapping the menu over to a single
scrolling group inside a clip rect will be a one-liner — happy to
follow up with that PR once it ships.
pixels_animatePixelis the onlything reaching into
firefly-hollows's private API. Once thepixel API is finalized in the public header (the
// @TODO: Pixel APIblock infirefly-hollows.h) I'll drop the forward decls.