Skip to content

feat(k2_he): add OpenRGB protocol for per-key RGB control#447

Open
i-am-logger wants to merge 1 commit intoKeychron:hall_effect_playgroundfrom
i-am-logger:feature/openrgb-k2-he
Open

feat(k2_he): add OpenRGB protocol for per-key RGB control#447
i-am-logger wants to merge 1 commit intoKeychron:hall_effect_playgroundfrom
i-am-logger:feature/openrgb-k2-he

Conversation

@i-am-logger
Copy link
Copy Markdown

Summary

  • Adds OpenRGB protocol handler to the Keychron K2 HE, enabling per-key RGB control from Linux/Windows/macOS via the OpenRGB CLI and GUI
  • Protocol handler lives in keychron/common/ and is reusable for other HE boards — just set OPENRGB_ENABLE = yes in rules.mk
  • OpenRGB commands (IDs 1-9) are routed through the existing keychron_raw_hid.c default case — no conflict with Keychron's protocol (IDs 0xA0-0xAB)
  • Hall effect features (rapid trigger, adjustable actuation, SOCD) are fully preserved

Changes

File Change
keychron/common/openrgb.c / .h OpenRGB protocol handler (new)
keychron/common/keychron_common.mk OPENRGB_ENABLE build flag
keychron/common/keychron_raw_hid.c Route OpenRGB commands via default case
quantum/rgb_matrix/animations/openrgb_direct_anim.h OPENRGB_DIRECT effect (new)
quantum/rgb_matrix/animations/rgb_matrix_effects.inc Include openrgb_direct_anim.h
tmk_core/protocol/usb_descriptor.h Guard RAW_EPSIZE with #ifndef
k2_he/ansi_rgb/config.h Set RAW_EPSIZE 64 for OpenRGB compatibility
k2_he/ansi_rgb/keymaps/default/keymap.c Boot into OPENRGB_DIRECT mode
k2_he/info.json Enable all RGB matrix animations
k2_he/rules.mk Enable OPENRGB_ENABLE

Usage

After flashing, register the keyboard in OpenRGB (Settings > QMK OpenRGB) with:

  • VID: 0x3434
  • PID: 0x0E20
  • Name: Keychron K2 HE

Then control via CLI:

openrgb -d 1 -c FF0000        # all keys red
openrgb -d 1 -m Direct -c 00FF00  # all keys green

Test plan

  • Firmware builds cleanly with make keychron/k2_he/ansi_rgb:default
  • Keyboard detected by OpenRGB 1.0rc2 as "QMK OpenRGB Device (Protocol Version 13)"
  • Per-key RGB control working via OpenRGB and raw HID
  • All built-in RGB effects still functional
  • Hall effect features (rapid trigger, actuation points) unaffected
  • Keychron Launcher communication preserved (0xA0+ commands unaffected)
  • Tested on NixOS Linux with STM32 DFU flashing

Adds OpenRGB protocol handler to the Keychron K2 HE, enabling per-key
RGB control from Linux/Windows/macOS via the OpenRGB CLI and GUI.

Changes:
- Add openrgb.c/h protocol handler in keychron/common (reusable for other HE boards)
- Add OPENRGB_DIRECT RGB matrix effect for host-controlled per-key colors
- Route OpenRGB commands (IDs 1-9) through keychron_raw_hid.c default case
- Guard RAW_EPSIZE in usb_descriptor.h with #ifndef for overridability
- Set RAW_EPSIZE to 64 for OpenRGB protocol compatibility
- Enable all RGB matrix animations
- Boot into OPENRGB_DIRECT mode by default
- Add OPENRGB_ENABLE build flag gated in keychron_common.mk

The OpenRGB protocol (command IDs 1-9) does not conflict with Keychron's
existing raw HID protocol (command IDs 0xA0-0xAB). Hall effect features
(rapid trigger, adjustable actuation, SOCD) are fully preserved.

Tested on Keychron K2 HE ANSI RGB with OpenRGB 1.0rc2 on NixOS Linux.
Copilot AI review requested due to automatic review settings April 5, 2026 11:40
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds OpenRGB (QMK OpenRGB protocol) support for the Keychron K2 HE to enable per-key RGB control via Raw HID, while integrating with QMK’s RGB Matrix system via a new “direct” RGB matrix effect.

Changes:

  • Add a reusable OpenRGB protocol handler under keychron/common/ and route OpenRGB command IDs through Keychron’s Raw HID handler default case.
  • Add a new RGB matrix effect (OPENRGB_DIRECT) and include it in the compiled RGB matrix effects set.
  • Allow overriding RAW_EPSIZE and set it to 64 for the K2 HE ANSI RGB target; default keymap boots into the new direct mode; enable more animations in info.json.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tmk_core/protocol/usb_descriptor.h Makes RAW_EPSIZE overridable (defaults to 32 if not set).
quantum/rgb_matrix/animations/rgb_matrix_effects.inc Includes the new OpenRGB direct animation header in the effect list.
quantum/rgb_matrix/animations/openrgb_direct_anim.h Adds OPENRGB_DIRECT RGB matrix effect that renders per-LED colors provided by OpenRGB.
keyboards/keychron/k2_he/rules.mk Enables OPENRGB_ENABLE for the K2 HE build.
keyboards/keychron/k2_he/info.json Expands enabled RGB matrix animations for the board.
keyboards/keychron/k2_he/ansi_rgb/keymaps/default/keymap.c Switches to RGB_MATRIX_OPENRGB_DIRECT at boot when OpenRGB is enabled.
keyboards/keychron/k2_he/ansi_rgb/config.h Sets RAW_EPSIZE to 64 for OpenRGB compatibility.
keyboards/keychron/common/openrgb.h Declares OpenRGB protocol constants, IDs, and handler API.
keyboards/keychron/common/openrgb.c Implements OpenRGB command handling and per-LED color storage.
keyboards/keychron/common/keychron_raw_hid.c Dispatches OpenRGB commands from the Keychron Raw HID default case.
keyboards/keychron/common/keychron_common.mk Adds OPENRGB_ENABLE build flag handling, compiles OpenRGB sources, and forces RAW_ENABLE.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +208 to +213
const uint8_t number_leds = data[1];

for (uint8_t i = 0; i < number_leds; i++) {
const uint8_t data_idx = i * 4;
const uint8_t color_idx = data[data_idx + 2];

Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

openrgb_direct_mode_set_leds() trusts number_leds = data[1] and then indexes data[data_idx + 2..5] without clamping to the actual report length. A host can set number_leds large and trigger out-of-bounds reads (and potentially unpredictable behavior). Clamp number_leds to the maximum that fits in the received length (e.g., (length - 2) / 4) and/or return failure when the payload is too short.

Copilot uses AI. Check for mistakes.
Comment on lines +222 to +226
bool openrgb_command_handler(uint8_t *data, uint8_t length) {
switch (data[0]) {
case OPENRGB_GET_PROTOCOL_VERSION:
openrgb_get_protocol_version();
break;
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

openrgb_command_handler() (and the per-command helpers it calls) dereference fixed offsets in data (e.g., data[1], data[6], etc.) but never validates length. While some platforms always pass RAW_EPSIZE, others can pass the actual received length; malformed/short reports could cause out-of-bounds reads. Consider rejecting packets where length < the minimum for the specific command (or enforcing length == RAW_EPSIZE) before accessing data[].

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants