feat(k2_he): add OpenRGB protocol for per-key RGB control#476
feat(k2_he): add OpenRGB protocol for per-key RGB control#476i-am-logger wants to merge 3 commits into
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds OpenRGB support for the Keychron K2 HE by enabling 64-byte Raw HID reports, wiring an OpenRGB command handler, and introducing an RGB Matrix “direct” effect for per-LED color control.
Changes:
- Make
RAW_EPSIZEoverridable and set it to 64 for the K2 HE ANSI config for OpenRGB Raw HID reports. - Add an OpenRGB Raw HID protocol implementation (
openrgb.c/.h) and route USB Raw HID packets to it. - Add a new RGB Matrix effect (
OPENRGB_DIRECT) and enable OpenRGB for the K2 HE (default keymap sets the mode at startup).
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| tmk_core/protocol/usb_descriptor.h | Allows overriding RAW_EPSIZE so keyboards can opt into 64-byte reports. |
| quantum/rgb_matrix/animations/rgb_matrix_effects.inc | Registers the new OpenRGB direct animation header. |
| quantum/rgb_matrix/animations/openrgb_direct_anim.h | Implements the OPENRGB_DIRECT RGB Matrix effect that applies per-LED colors. |
| keyboards/keychron/k2_he/rules.mk | Enables OpenRGB for the K2 HE build. |
| keyboards/keychron/k2_he/ansi/keymaps/default/keymap.c | Sets the keyboard to OPENRGB_DIRECT mode after init when OpenRGB is enabled. |
| keyboards/keychron/k2_he/ansi/config.h | Overrides Raw HID endpoint size to 64 bytes for OpenRGB. |
| keyboards/keychron/common/openrgb.h | Declares OpenRGB protocol constants and handler API. |
| keyboards/keychron/common/openrgb.c | Implements OpenRGB command handling and Raw HID responses. |
| keyboards/keychron/common/keychron_raw_hid.c | Routes incoming USB Raw HID packets to the OpenRGB handler. |
| keyboards/keychron/common/keychron_common.mk | Adds build toggles and sources for OpenRGB support. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
c76969d to
6b2763d
Compare
253e10b to
746b545
Compare
3e3d0d6 to
d682e9f
Compare
Adds OpenRGB raw-HID support to the Keychron K2 HE on the ansi/default keymap (the keychron keymap keeps VIA / Keychron Launcher). - Per-key direct control via a custom RGB_MATRIX_OPENRGB_DIRECT effect. - Explicit OpenRGB<->QMK mode map: OpenRGB assigns mode values in its own fixed order, so a table (in OpenRGB's order, Direct last) translates GET_ENABLED_MODES / SET_MODE / GET_MODE_INFO instead of trusting QMK's effect numbering, so animation names line up with the real effects. - Replies are sent via kc_raw_hid_send(RAW_HID_SRC_USB, ...) like the Keychron Launcher's KC_* handlers, not the core raw_hid_send(): on the 2025q3 base the active host driver becomes the wireless transport (whose send_raw_hid is NULL) when the side switch is on BT/2.4G, which silently dropped OpenRGB replies and made the host time out. - Host-side unit tests (keyboards/keychron/common/tests) compile openrgb.c against mocks and prove both the mode mapping and the USB-reply routing.
`qmk ci-validate-aliases` fails on the 2025q3 base: data/mappings/keyboard_aliases.hjson carries ~783 aliases whose target keyboards were removed from this fork, so every PR whose lint workflow actually runs fails on them. Remove the dangling aliases so the check passes. This is unrelated to the OpenRGB feature and is isolated in its own commit — happy to split it into a dedicated PR.
wear_leveling_consolidate_force() called wear_leveling_unlock() before erasing, which is not in upstream QMK. It desynced the lock-state tracking: the following wear_leveling_write_consolidated() then saw the store as already unlocked, returned STATUS_UNCHANGED, and skipped its matching re-lock — leaving wear_leveling.unlocked inconsistent with the backing store. That made the WearLeveling*_OOB unit tests fail (a write reached the backing store while it was still locked). Remove the redundant unlock to match upstream. All wear_leveling unit tests pass (general, 2byte, 2byte_optimized, 4byte, 8byte). Unrelated to the OpenRGB feature; isolated in its own commit.
d682e9f to
b979f4e
Compare
The note claimed the K2 HE was intentionally NOT registered as a QMK OpenRGB device because OpenRGB management cycled the keyboard's Genesys Logic USB hub (disconnect/reconnect every ~4-10 min, dragging the Logitech receiver and YubiKey offline; observed 2026-04-14). Root cause was the K2 HE firmware: its OpenRGB command handler was never compiled in (a build-wiring bug), so the keyboard never answered the protocol and OpenRGB kept re-probing/resetting it. Fixed upstream in Keychron/qmk_firmware#476 -- OpenRGB now enumerates and manages it cleanly with no hub cycling. The K2 HE is registered via the vogix path (my.theming.vogix -> vogix.hardware.keychron-k2-he); the my.theming.openrgb line here stays commented to avoid double-registering the same device.
* fix(hardware/k2-he): correct the OpenRGB registration note The note claimed the K2 HE was intentionally NOT registered as a QMK OpenRGB device because OpenRGB management cycled the keyboard's Genesys Logic USB hub (disconnect/reconnect every ~4-10 min, dragging the Logitech receiver and YubiKey offline; observed 2026-04-14). Root cause was the K2 HE firmware: its OpenRGB command handler was never compiled in (a build-wiring bug), so the keyboard never answered the protocol and OpenRGB kept re-probing/resetting it. Fixed upstream in Keychron/qmk_firmware#476 -- OpenRGB now enumerates and manages it cleanly with no hub cycling. The K2 HE is registered via the vogix path (my.theming.vogix -> vogix.hardware.keychron-k2-he); the my.theming.openrgb line here stays commented to avoid double-registering the same device. * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Ido Samuelson <i-am-logger@users.noreply.github.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
|
There's an open merge request on the OpenRGB side to implement Keychron's RGB protocol. I think this is the better solution, the OpenRGB QMK protocol is pretty basic and pretty old at this point. While it's still supported (and I just made some QMK Community Modules for it at https://gitlab.com/OpenRGBDevelopers/OpenRGB-QMK), Keychron's own protocol appears to support all the capabilities that OpenRGB needs according to this merge request: https://gitlab.com/CalcProgrammer1/OpenRGB/-/merge_requests/3290 so it would be best to update OpenRGB to support what the keyboard already supports. I'm looking into merging this MR and trying to test it on my Keychron Q2, but I'm not sure the Q2 supports the same Keychron protocol. |
|
Thanks @CalcProgrammer1 — agree, native is the right direction. The K2 HE speaks the same native I'll keep #476 open as a working stop-gap (it uses the current OpenRGB-QMK protocol) until native K2 HE support lands in OpenRGB, then close it in favor of that. Note Side note on the Q2: it's actually a few different boards — only the Max/HE variants expose |
|
Do you know if it's possible to build a firmware for the wired Q2 that has the new protocol? |
Yes — it already exists in Keychron's QMK fork. The wired Q2 has a target with the native 0xA8 protocol; I just built it: $ qmk compile -kb keychron/q2/ansi -km keychron compiles clean (~59 KB). The keychron keymap gives both the native 0xA8 group and VIA on (the 0x07/0x08 channel 3290 uses for mode/brightness). Flash that on your wired Q2 (PID stays 0x0110/0x0111), add that PID to 3290, and you could test it. |
|
@CalcProgrammer1 here is a confirmation arm-none-eabi-nm .build/keychron_q2_ansi_keychron.elf | grep -E 'kc_rgb_matrix_rx|via_command_kb'
|
|
I'll look into that today. I was able to build custom firmware for my Q2 from the upstream QMK repo and include my community module for the OpenRGB protocol, but that route won't have support for Keychron's configuration tool. Does this PR allow both at the same time? |
|
Hmm, the build fails for me: This is with the default branch (2025q3). |
|
i'll take a look, i used my own branch so will check later |
yes, it does |

Description
Adds OpenRGB per-key RGB control to the Keychron K2 HE over raw HID, on the
ansi/defaultkeymap (thekeychronkeymap keeps VIA / Keychron Launcher unchanged).RGB_MATRIX_OPENRGB_DIRECTeffect.GET_ENABLED_MODES/SET_MODE/GET_MODE_INFOinstead of trusting QMK's effect numbering — so the mode names line up with the real effects.kc_raw_hid_send(RAW_HID_SRC_USB, …)like the Keychron Launcher'sKC_*handlers. The coreraw_hid_send()is silently dropped by the active wireless host driver (NULLsend_raw_hid) when the side switch is on BT/2.4G, so OpenRGB must send USB-direct.keyboards/keychron/common/tests) compileopenrgb.cagainst mocks and prove both the mode mapping and the USB-reply routing.Supersedes #447 (which targeted the deprecated
hall_effect_playgroundbranch).Types of Changes
Issues Fixed or Closed by This PR
Notes for @lokher
Three commits. The OpenRGB feature is self-contained in the first commit; the other two fix pre-existing CI failures on the
2025q3base (confirmed on a clean base checkout, independent of this feature) and are isolated so you can split either into its own PR:feat(k2_he): add OpenRGB protocol…— the feature (incl. the mode mapping, USB-reply routing, and unit tests).chore: prune dangling keyboard aliases— removes ~783keyboard_aliases.hjsonentries whose target keyboards were removed from this fork, soci-validate-aliasespasses (Keychron aliases preserved).fix(wear_leveling): remove stray unlock— removes awear_leveling_unlock()not present upstream that broke theWearLeveling*_OOBunit tests.Checklist