-
Notifications
You must be signed in to change notification settings - Fork 5
Protocol LIGHTSYNC
The RS50's faceplate strip has 10 individually addressable RGB LEDs used as an engine-RPM / shift indicator. LED control spans two HID++ features working as a pair. Part of the Protocol Specification; the user-facing controls are in the Sysfs API Reference and Configuring the Wheel.
Rim differences: this page describes RS50 rim hardware (in both native and compat enumeration; the rim does not change with the PID). The real G PRO rim uses the same 0x807A feature page for a level-based rev-light protocol with no per-LED RGB: after a one-time arm burst, the host repeats a SHORT fn2 + LONG fn6 pair whose byte 9 carries a 0-10 "LEDs lit" level; colors and direction belong to the wheel's onboard profile. The RS50 also accepts the level command (hardware-verified): the fill renders the selected custom slot's stored colors and direction, which is what makes a live RPM display work on the RS50 too (
wheel_rev_level). Caution: bursting level writes starves the wheel's shared HID++ command processor; pace them at G HUB's ~160 ms cadence (the driver does this coalescing itself).
| Feature | Page | Purpose | Functions used |
|---|---|---|---|
| 0x807A | LIGHTSYNC | effect/slot select and commit | fn3 (set effect), fn6 (pre-config/commit), fn7 (enable/refresh) |
| 0x807B | RGBZoneConfig | per-slot RGB data and names | fn2 (set colors), fn1 (get config), fn3 (get name), fn4 (set name) |
Feature indices are discovered at runtime (typically 0x0B and 0x0C). fn6/fn7 only exist on 0x807A; sending them to 0x807B returns error 7 (InvalidFunctionId).
| Value | Effect |
|---|---|
0x01 |
Inside to out animation |
0x02 |
Outside to in animation |
0x03 |
Right to left animation |
0x04 |
Left to right animation |
0x05-0x09
|
The five custom slots: 0x05 = CUSTOM 1 .. 0x09 = CUSTOM 5 |
Effect values 5-9 ARE the custom slots: there is no separate "activate
slot" command; selecting a slot is selecting its effect number. fn3 only
STAGES the effect; the strip repaints on the following zero-parameter fn6
commit. (0x807B fn3 is GET_NAME, a pure read; an earlier driver mistook it
for an activate call.) fn2 GET_STATE returns the current effect, so a value
of 5 or more means custom slot effect - 5 is selected.
The primary command for colors and animation direction:
Byte Field Description
---- ----- -----------
0 Report ID 0x12 (very long)
1 Device Index 0xFF
2 Feature Index [discovered]
3 Function 0x2C
4 Slot Index 0x00-0x04 (CUSTOM 1-5)
5 Direction 0x01-0x04 (wire value, see below)
6-8 LED10 RGB R, G, B
9-11 LED9 RGB ...
...
33-35 LED1 RGB
36-63 Padding
LED order is REVERSED on the wire: LED10 first, LED1 last. A driver must reverse user-facing LED1-10 to protocol order.
Byte 5 carries a 1-based wire value, not the driver's 0-3 enum:
| Wire | Effect | Driver enum |
|---|---|---|
0x01 |
Inside to Outside (expand) | 2 |
0x02 |
Outside to Inside (contract) | 3 |
0x03 |
Left to Right sweep | 0 |
0x04 |
Right to Left sweep | 1 |
(An earlier driver encoded byte 5 as enum + 2, which mislabelled the
sweeps and sent an out-of-range 5 for Outside-In; the firmware NAKs that and
the write surfaced as -EIO. All four values are now hardware-verified via
live rev-fill sweeps.)
For the two symmetric directions (Inside-Out, Outside-In) G HUB collapses the 10 LEDs into 5 mirrored pairs (LED1-LED10, LED2-LED9, ...), sending a palindrome. This is application behavior, not protocol enforcement; the wire still carries all 10 colors, but a UI mirroring these directions (as G HUB and logi-dd do) must send a palindrome for the animation to look right.
fn3 [slot] returns [slot][len][ASCII name]; fn4 [slot][len][name] sets
it (max 8 characters; defaults "CUSTOM 1" through "CUSTOM 5"). Names are
stored on the device.
Every color change runs a 5-step sequence spanning both features:
Step Feature Function Purpose
---- ------- -------- ----------------------------------------
1 0x807A fn3 Select the slot's effect ([0x05 + slot])
2 0x807A fn6 Pre-config (LONG report)
3 0x807B fn2 RGB data (64-byte very-long)
4 0x807A fn6 Commit (LONG report)
5 0x807A fn7 Enable / refresh display
The fn6 pre-config/commit pair is an SW/FW ownership arbitration: Logitech's published x8070/x8071 specs (this feature pair's documented ancestors) define explicit "software takes control" calls, and re-arbitrating before every update is the officially expected pattern. That also explains the early bring-up mystery where the first LED update worked and later ones silently did not: the firmware reclaims ownership, and G HUB re-runs the arbitration per change.
The wheel broadcasts effect changes (from G HUB-style tools or the wheel
itself) as 12 ff <idx> 00 <effect> with SW_ID 0; the driver consumes these
and updates wheel_led_effect, notifying poll()ers. Brightness changes
made on the wheel's OLED menu broadcast via the x8040 brightnessChangeEvent
and are tracked the same way.
Global LED brightness is a separate feature (fn2 SET, 0-100). Note 0x8040
is brightness only; G HUB's steering "Sensitivity" slider is an 0x80A4
response-curve upload, not this feature.
The level-based rev-light command (see the rim note at the top): a one-time arm burst, then SHORT fn2 + LONG fn6 pairs with byte 9 = 0-10. Two hardware-verified behaviors on the RS50:
- The fill renders the selected custom slot's stored colors and follows its direction.
- The arm burst's "fn3 param 0x02" is a plain SET_EFFECT, so arming force-switches the strip to Outside-In; the driver re-asserts the pre-arm effect immediately after the one-time burst.
A written level holds until the next write (the wheel eventually reverts an
unrefreshed level); writing wheel_led_effect restores the idle pattern.