Skip to content

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

Draft
i-am-logger wants to merge 3 commits into
Keychron:2025q3from
i-am-logger:feature/openrgb-k2-he-2025q3
Draft

feat(k2_he): add OpenRGB protocol for per-key RGB control#476
i-am-logger wants to merge 3 commits into
Keychron:2025q3from
i-am-logger:feature/openrgb-k2-he-2025q3

Conversation

@i-am-logger

@i-am-logger i-am-logger commented Jun 10, 2026

Copy link
Copy Markdown

Description

Adds OpenRGB per-key RGB control to the Keychron K2 HE over raw HID, on the ansi/default keymap (the keychron keymap keeps VIA / Keychron Launcher unchanged).

  • Per-key direct control via a custom RGB_MATRIX_OPENRGB_DIRECT effect.
  • Built-in animations are exposed correctly: OpenRGB assigns mode values in its own fixed order, so an explicit OpenRGB↔QMK table (Direct last) translates GET_ENABLED_MODES / SET_MODE / GET_MODE_INFO instead of trusting QMK's effect numbering — so the mode 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. The core raw_hid_send() is silently dropped by the active wireless host driver (NULL send_raw_hid) when the side switch is on BT/2.4G, so OpenRGB must send USB-direct.
  • Host-side unit tests (keyboards/keychron/common/tests) compile openrgb.c against mocks and prove both the mode mapping and the USB-reply routing.

Supersedes #447 (which targeted the deprecated hall_effect_playground branch).

Types of Changes

  • Core
  • Bugfix
  • New feature
  • Keyboard (addition or update)

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 2025q3 base (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 ~783 keyboard_aliases.hjson entries whose target keyboards were removed from this fork, so ci-validate-aliases passes (Keychron aliases preserved).
  • fix(wear_leveling): remove stray unlock — removes a wear_leveling_unlock() not present upstream that broke the WearLeveling*_OOB unit tests.

Checklist

  • My code follows the code style of this project
  • I have read the CONTRIBUTING document
  • I have added tests to cover my changes
  • I have tested the changes and verified that they work and don't break anything (built, lint-clean, host unit tests pass; OpenRGB protocol exercised on a real K2 HE over raw HID)

Copilot AI review requested due to automatic review settings June 10, 2026 23:01

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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_EPSIZE overridable 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.

Comment thread keyboards/keychron/common/openrgb.c Outdated
Comment thread keyboards/keychron/common/openrgb.c
Comment thread keyboards/keychron/common/openrgb.c
Comment thread keyboards/keychron/common/openrgb.h
Comment thread keyboards/keychron/k2_he/ansi/config.h Outdated
Comment thread keyboards/keychron/common/openrgb.c
Comment thread quantum/rgb_matrix/animations/openrgb_direct_anim.h Outdated
Comment thread tmk_core/protocol/usb_descriptor.h
@i-am-logger i-am-logger force-pushed the feature/openrgb-k2-he-2025q3 branch from c76969d to 6b2763d Compare June 10, 2026 23:22
@github-actions github-actions Bot added the dd label Jun 11, 2026
@i-am-logger i-am-logger force-pushed the feature/openrgb-k2-he-2025q3 branch from 253e10b to 746b545 Compare June 11, 2026 00:30
@i-am-logger i-am-logger marked this pull request as draft June 14, 2026 21:40
@i-am-logger i-am-logger force-pushed the feature/openrgb-k2-he-2025q3 branch 2 times, most recently from 3e3d0d6 to d682e9f Compare June 14, 2026 22:06
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.
@i-am-logger i-am-logger force-pushed the feature/openrgb-k2-he-2025q3 branch from d682e9f to b979f4e Compare June 14, 2026 22:44
@i-am-logger i-am-logger marked this pull request as ready for review June 14, 2026 23:10
i-am-logger added a commit to i-am-logger/mynixos that referenced this pull request Jun 15, 2026
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.
i-am-logger added a commit to i-am-logger/mynixos that referenced this pull request Jun 15, 2026
* 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>
@CalcProgrammer1

Copy link
Copy Markdown

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.

@i-am-logger i-am-logger marked this pull request as draft June 20, 2026 04:44
@i-am-logger

i-am-logger commented Jun 20, 2026

Copy link
Copy Markdown
Author

Thanks @CalcProgrammer1 — agree, native is the right direction. The K2 HE speaks the same native 0xA8 group as the Q1 HE 3290 targets, so I'm glad to test 3290 against it and add the K2 HE (0x3434:0x0E20) device entry — I've got the hardware.

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 0xA8; the wired Q2 is the older OpenRGB-QMK one. Worth dumping its PID + checking for usage_page 0xFF60 before testing 3290 on it.

@CalcProgrammer1

Copy link
Copy Markdown

Do you know if it's possible to build a firmware for the wired Q2 that has the new protocol?

@i-am-logger

i-am-logger commented Jun 20, 2026

Copy link
Copy Markdown
Author

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.

@i-am-logger

Copy link
Copy Markdown
Author

@CalcProgrammer1 here is a confirmation

arm-none-eabi-nm .build/keychron_q2_ansi_keychron.elf | grep -E 'kc_rgb_matrix_rx|via_command_kb'
image

@CalcProgrammer1

Copy link
Copy Markdown

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?

@CalcProgrammer1

Copy link
Copy Markdown

Hmm, the build fails for me:

Compiling: quantum/rgb_matrix/rgb_matrix.c                                                         In file included from quantum/rgb_matrix/animations/rgb_matrix_effects.inc:30,
                 from quantum/rgb_matrix/rgb_matrix.c:50:
quantum/rgb_matrix/animations/pixel_rain_anim.h: In function 'PIXEL_RAIN':
quantum/rgb_matrix/animations/pixel_rain_anim.h:16:25: error: variable 'region_mask' set but not used [-Werror=unused-but-set-variable=]
   16 |     static uint8_t      region_mask  = 0;
      |                         ^~~~~~~~~~~
cc1: all warnings being treated as errors
 [ERRORS]
 | 
 | 
 | 
make: *** [builddefs/common_rules.mk:362: .build/obj_keychron_q2_ansi_keychron/quantum/rgb_matrix/rgb_matrix.o] Error 1

This is with the default branch (2025q3).

@i-am-logger

Copy link
Copy Markdown
Author

i'll take a look, i used my own branch so will check later

@i-am-logger

Copy link
Copy Markdown
Author

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?

yes, it does

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.

3 participants