Skip to content

Add tools for the GP2040-CE Host Lighting add-on - #12

Open
djGLiTCH wants to merge 6 commits into
OpenStickCommunity:mainfrom
djGLiTCH:20260811-host-lighting-tools
Open

Add tools for the GP2040-CE Host Lighting add-on#12
djGLiTCH wants to merge 6 commits into
OpenStickCommunity:mainfrom
djGLiTCH:20260811-host-lighting-tools

Conversation

@djGLiTCH

@djGLiTCH djGLiTCH commented Aug 11, 2026

Copy link
Copy Markdown

Companion to OpenStickCommunity/GP2040-CE#1691, which adds
the Host Lighting add-on: a vendor HID interface for driving a board's RGB
LEDs from host software. These tools talk to that interface:

  • hlp-ping verifies the protocol handshake and measures the command
    round-trip
  • hlp-caps decodes the board's self-reported capabilities page by page
    (identity, runtime state, the LED map, animations, per-light positions)
  • hlp-fill runs a quick visual test and hands control back to the
    board's animations
  • hlp-input-mode, hlp-reboot-webconfig, hlp-reboot-bootsel manage
    the board over the same interface

Discovery is by usage page (0xFF47), never VID:PID, and multiple connected
boards are selected between with --board-id (the factory-unique ID from
the capability pages). The HID transport uses hidapi (new dependency) so
the tools work cross-platform; unit tests cover the framing and
reply-matching helpers, and everything was validated against two boards
(Haute42 COSMOX and COSMOX M-Ultra).

New hlp-* console tools talking to the add-on's vendor HID interface
(usage page 0xFF47) over hidapi: hlp-ping (handshake and round-trip),
hlp-caps (decode the board's self-reported LED capabilities), hlp-fill
(visual test), hlp-input-mode, hlp-reboot-webconfig and
hlp-reboot-bootsel. Multiple connected boards are selected between with
--board-id. Adds the hidapi dependency.

Signed-off-by: Jacob Simpson <28767380+djGLiTCH@users.noreply.github.com>
Reboot-style commands execute before their reply is sent, so the board
often drops off the bus before the acknowledgement can be read; the
reboot tools previously surfaced that as a raw read error even though
the reboot had succeeded. A vanished device or missing reply is now
treated as the reboot proceeding, while a rejection reply (wrong guard
magic) still raises, via a distinct HostLightingRejected error.

Found by flashing a board with hlp-reboot-bootsel; the fix is verified
against a live reboot cycle and covered by unit tests.

Signed-off-by: Jacob Simpson <28767380+djGLiTCH@users.noreply.github.com>
Page 1 byte [5] is a step index into the board's brightness steps, not a
0-255 level. Page 2's brightness maximum is a separate 0-255 ceiling, so
printing byte [5] as plain "brightness" beside it implies one is a
fraction of the other: "brightness 5" against "brightness maximum 200"
reads as nearly off.

It is neither. HLP does not report the step count, and that count varies
by firmware - mainline defaults to 5 and the web configurator can set 1
to 10, while the LED refactor fixes it at 10 - so a step number means
nothing without the board's total. On a refactor board, step 5 is half
brightness.

The firmware and the protocol reference both call it a step; only these
tools were vague. Wording only, no behaviour change.

Signed-off-by: Jacob Simpson <28767380+djGLiTCH@users.noreply.github.com>
hlp-caps decodes everything v1.1 added: the light table (capability
page 5), which names every individual light and the control that owns
it, including its grid position; and the feature bitmask, LED
framework, animation namespace and render rate appended to the runtime
state page. Paged reads re-check the page fingerprint and guard against
a board that stops making progress, so a table read across several
reports is known coherent rather than assumed.

Boards speaking v1.0 decode unchanged: fields they never sent report as
absent rather than guessed, and the light table reads as unsupported
rather than as an error. The suite grows to 60 tests covering the new
decoders, the stall guards and the sentinel values, and the README
samples are real output from the reference boards.
SET_LIGHT stages one light per entry by its light-table ordinal, the
only address that can name a single light of a control that owns
several. The tool takes ORDINAL:RRGGBB pairs, chunks them at the
15-entry report capacity, sums the board's applied and skipped counts
across reports, and refuses cleanly on firmware whose PING predates
v1.2. Ordinals a board does not have are counted as skipped rather
than erroring, matching the protocol's SET_BUTTONS semantics.
An eight-digit RRGGBBWW colour stages through the new SET_LIGHT_RGBW
command, twelve entries per report; boards without a white channel
ignore W. On v1.3 firmware the reply's per-entry outcome mask is
decoded to name any skipped ordinals instead of only counting them,
and set_lights grows a set_lights_rgbw sibling, both returning the raw
masks alongside the counts. The README states the white contract: send
the subtractive conversion (W = min(R,G,B), RGB reduced) to v1.3
firmware, and plain six-digit colours to older boards, whose achromatic
mapping never reads a supplied W. The suite grows to 66 tests, covering
the mask pass-through, the five-byte entry layout and the twelve-entry
chunking.
@djGLiTCH

Copy link
Copy Markdown
Author

These commits bring the hlp-* tools through Host Lighting Protocol v1.3, matching the firmware PR (GP2040-CE#1691), one commit per protocol version:

v1.1:
hlp-caps decodes everything the protocol added - the light table naming every light and its owning control with grid positions, plus the feature bitmask, LED framework, animation namespace and render rate on the runtime state page. Paged reads re-check the fingerprint and guard against a stalled board. v1.0 boards decode unchanged: absent fields report as absent rather than guessed.

v1.2:
hlp-set-light stages individual lights by their light-table ordinal via SET_LIGHT, chunking at the 15-entry report capacity and reporting the board's applied/skipped counts; it refuses cleanly on firmware that predates the command.

v1.3:
an eight-digit RRGGBBWW colour stages through SET_LIGHT_RGBW (12-entry chunking; boards without a white channel ignore W), and on v1.3 firmware any skipped ordinals are named from the reply's per-entry outcome mask instead of only counted. set_lights grows a set_lights_rgbw sibling, both returning the raw masks alongside the counts. The README carries the white contract: subtractive W to v1.3 firmware, plain RGB to older boards.

Validation:
The suite grows to 66 tests, and everything was verified live against a Haute42 COSMOX M Ultra Gen 2 running each protocol version on both LED pipelines; the README samples are real output from the reference boards.

Firmware to test against is on my GP2040-CE fork, one pre-release per protocol version: HLP v1.1, HLP v1.2, and HLP v1.3.

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