feat(lighting): enable Lightpad rendering and LUMI key colors - #7
Merged
Conversation
The VM resolves jumps and calls from the complete program binary, but label fixups omitted the header and function table. The bitmap renderer therefore jumped into unrelated bytes and failed after drawing one pixel in the upstream runner. Include the code base when resolving labels. Cover forward and backward branches, calls, multiple functions, and repeated builds. Independent upstream VM replay renders all 225 pixels correctly with the fix; physical hardware compatibility remains to be verified. Co-Authored-By: Nova (Codex) <noreply@openai.com>
Firmware clears initial pixel data while starting a program, and its status overlay can hide the requested colors. Upload code separately, disable the overlay, and require a nonce reply before publishing pixels. Restart that handshake when heap state is lost, including when identical code survives a reconnect. Add a 24-key renderer with default musical handling enabled, a key_frame API command, and a keys CLI pattern that waits for firmware discovery. Keep factory programs active until a lighting request arrives. Verified RGB on both devices, execution and host-state recovery on Lightpad M 1.1.0 and LUMI 1.3.9, 540 tests, native vendor VM vectors, independent review, and an installed wheel check. Physical MIDI/MPE and cable or power-loss exercises remain unverified. Co-Authored-By: Nova (Codex) <noreply@openai.com>
Replace the unsupported-opcode diagnosis with the reproduced jump, startup heap, and overlay failures. Document the execution challenge, LUMI key API and CLI, firmware requirements, and observed device results. Separate visible RGB and simulated host-state recovery evidence from pending physical reconnect, musical input, timing, and calibration work. Documentation builds and scoped formatting checks pass. Co-Authored-By: Nova (Codex) <noreply@openai.com>
15 tasks
New animation frames repeatedly dirtied the heap prefix, starving lower Lightpad rows while the API continued accepting frames. Pin each multipart transfer and coalesce newer targets separately so every surface completes without lowering the requested frame cadence. Reserve packet capacity for skip commands and the end marker. Decoded wire regressions cover continuous updates, slow ACKs, resets and final convergence rather than trusting optimistic host state alone.
The bitmap renderer could repaint while heap packets replaced its pixels, mixing upper and lower rows from different animation frames. Upload into an inactive bank and release the old bank only after the renderer acknowledges a complete repaint of its successor. Correlate presentations by session, sequence and bank. Recover ownership through readiness after heap loss, including unchanged program uploads, and retry lost presentation replies without rewriting a visible bank. Validate the assembled program in the vendor VM and cover host bank ownership, coalescing, stale replies and reset recovery in regressions.
Document the two-bank presentation handshake and distinguish queued frames, completed logical repaints and physical LED scanout. Explain snapshot starvation and tearing symptoms so successful API replies are not mistaken for complete display delivery.
Sleeping between receive batches delayed queued acknowledgements by up to 200 ms and limited complete Lightpad frame delivery. Wait for input until the next lifecycle deadline instead, processing each packet as it arrives while retaining the existing timer cadence. Yield cooperatively during preloaded bursts and check deadlines during continuous traffic so neither peer tasks nor keepalives are starved. Cover arrival between ticks, timer alignment and transport cleanup.
Match the SDK request layout by writing its 32-bit zero address before the requested item. The shorter packets prevented LUMI configuration readback, obscuring the actual hardware brightness and gamma values. Add C++ SDK packing vectors for the lowest, brightness, and highest config IDs. Corrected live requests returned brightness 100 and gamma 0. Co-Authored-By: Nova (Codex) <noreply@openai.com>
Firmware brightness can dim frames that already carry software scaling. Set the LUMI hardware brightness to its SDK maximum when the renderer initialises and answers a matching readiness challenge, including when unchanged bytecode reconnects without running initialise again. Keep brightness configuration knowledge inside the key renderer. Native VM verification covers both setup paths, rejected queries, unchanged pixel values, and preservation of the default musical key handler. Co-Authored-By: Nova (Codex) <noreply@openai.com>
Describe the renderer's full-scale hardware brightness setting and its reconnect behavior so clients keep dimming in their host RGB pipeline. Clarify that the renderer leaves gamma correction unchanged.
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.
Lightpad frames could be accepted without producing the requested colors, and LUMI had no per-key lighting API. This change restores device-side rendering for Lightpad grids and adds individual colors for all 24 LUMI keys.
The assembler relocates jump targets to their position in the complete program. Both renderers disable the firmware status overlay. The daemon uploads code separately from pixels, waits for the complete upload acknowledgement, then challenges the running renderer with a fresh nonce. Heap-state loss restarts that handshake, including when identical program bytes survive without another initialise callback.
LUMI exposes JSON
key_frame(72 RGB888 bytes), discoverykey_count = 24, andblocksd led keys [rainbow|HEX]. Firmware 1.3.0 or newer is required. The renderer enables default musical key handling while replacing key lighting. Factory programs remain active until a lighting request arrives; the existing Lightpad binary API stays compatible.🛠️ Complete animation frames
Continuous input previously replaced unfinished heap targets, repeatedly updating the prefix while starving lower Lightpad rows. Each multipart transfer now finishes an immutable snapshot while newer desired frames coalesce separately. The encoder also reserves capacity for skip commands and its end marker.
Lightpad uses two 450-byte pixel banks. The host uploads into the inactive bank, then requests presentation after its heap packets are acknowledged. The renderer latches one bank for the entire repaint and acknowledges only after all 225 pixel writes. Session, sequence, and bank checks prevent stale replies from releasing a bank; retries and identical-code reconnects preserve ownership. Startup and recovery may blank the display during resynchronisation.
MIDI reception is independent of the 200 ms lifecycle timer. Incoming acknowledgements are processed immediately, while absolute timer deadlines and cooperative yielding preserve keepalives and other tasks during continuous traffic.
🧪 Validation
just checkpasses on the final head: 625 Python tests, Ruff, ty, dashboard checks, docs build, packaging, and fresh installed-wheel validation.The hardware owner confirmed whole-pad animation after the snapshot fix. Final visual feedback on buffered alignment remains pending. Physical MIDI/MPE preservation, cable/power-loss recovery, and hardware operation on macOS remain unverified. The vendor VM verifies the musical-handler arguments; a live MIDI/MPE exercise is still needed.
The matching Rust bridge is in Hypercolor #263. Python retains ownership of MIDI and the device protocol; Hypercolor supplies effects and spatial mapping.
The LUMI renderer now claims hardware brightness at 100% during initialization and validated readiness challenges, including reconnects with unchanged code. Host RGB/API brightness still controls dimming, and gamma is unchanged. Hardware readback confirmed brightness 100 in its 0–100 range. Config requests also include the SDK-required 32-bit reserved address field; three upstream C++ wire vectors cover the correction. Independent native-VM checks verified brightness calls, readiness ordering, unchanged musical-handler arguments, and black/white/patterned key frames.