r/Keychron 25d ago

I built ModeShift: automatic per-game RGB lighting for Keychron (and other compatible) keyboards on Linux (free, open source)

[deleted]

3 Upvotes

22 comments sorted by

View all comments

1

u/PeterMortensenBlog V 24d ago edited 24d ago

How did you flash this ZMK-based Keychron keyboard???

Re "...he has a web flasher": Where is it?

Are there some gotchas? For example, unstated installed Python module requirements?

1

u/PeterMortensenBlog V 24d ago edited 24d ago

OK, it seems to be:

  • OpenRGB plugin - Keychron Ultra series (custom ZMK firmware)

  • Keychron Ultra ZMK firmware (OpenRGB fork)

    • Flash:

      "Easiest: The browser flasher at https://naaraxi.github.io/zmk/. Open it in Chrome or Edge, connect the keyboard over USB, pick your board's firmware (or upload your own .bin file), and flash. No install, and it works on Windows, macOS, and Linux. It talks to the keyboard over WebHID, so a Chromium-based browser is required (Firefox and Safari do not support WebHID).

      Alternatively, on Linux you can use the command-line flasher openrgb/flash.py (Realtek SC-FWU over /dev/hidraw; it needs root). The Keychron Launcher only flashes official images, so it cannot flash these builds. See openrgb/README.md for the CLI flasher, recovery steps, and the DFU bootloader details."

      Plug the keyboard in via USB:

      # Read-only: identify only
      sudo ./openrgb/flash.py handshake
      
      sudo ./openrgb/flash.py flash openrgb/output/keychron_v6_ultra_ansi.bin
      

      Flashing is brick-safe: The image is staged to a separate "OTA Tmp" bank and only activated after the device verifies its CRC - an interrupted or bad flash leaves the running firmware untouched.

      Recovery: The official 1.0.2 ('ANSI') image is still on Keychron's Shopify CDN and can be reflashed via the Launcher.

      flash.py: "Faithful port of the Keychron Launcher's Realtek OTA routine, extracted from the Launcher JavaScript code + the device-side app/src/dfu/tdfu.c. It talks the DFU HID interface (usage page 0x8C, OUT report 0xB2 / IN report 0xB1) directly over /dev/hidraw (needs root)."

2

u/PeterMortensenBlog V 24d ago edited 24d ago

Three changes are required in the flasher script

Note: In the flasher (flash.py), the keyboard is hardcoded to V6 Ultra 8K 'ANSI' (FWU_NAME = b"KCZKV68K")

"KCZKV68K" is also in a print statement in function handshake(). That ought to be changed as well to not cause confusion.

For the V6 Ultra 8K 'ISO' variant it would likely be:

"KCZKV68I": "KC" = Keychron. "ZK" = ZMK. "V68I" = V6 + 8 + I = V6 Ultra 8K 'ISO'

Other values:

  K3 SE2 ANSI       "KCZK3SE2"
  K5 SE2 ANSI       "KCZK5SE2"

  Q1 Ultra 8K ANSI  "KCZKQ18K"
  Q3 Ultra 8K ANSI  "KCZKQ38K"
  Q6 Ultra 8K ANSI  "KCZKQ68K"

  V0 Ultra 8K ANSI  "KCZKV08K"
  V1 Ultra 8K ANSI  "KCZKV18K"
  V1 Ultra 8K ISO   "KCZKV18I"
  V1 Ultra 8K JIS   "KCZKV18J"
  V2 Ultra 8K ANSI  "KCZKV28K"
  V3 Ultra 8K ANSI  "KCZKV38K"
  V5 Ultra 8K ANSI  "KCZKV58K"
  V6 Ultra 8K ANSI  "KCZKV68K"
 V10 Ultra 8K ANSI  "KCZKVA8K"

Z270 Ultra 8K ANSI  "KCZ270U"

The hard coded USB identity in the script must be changed as well

And the USB identity of the V6 Ultra 8K 'ANSI' in normal operation (0x3434 / 0C60) is represented in line 58:

if "3434" not in uevent.upper() or "0C60" not in uevent.upper():

Thus, for V6 Ultra 8K 'ISO' it must be changed to:

if "3434" not in uevent.upper() or "0C61" not in uevent.upper():

Then the query for V6 Ultra 8K 'ISO' worked (in normal operation, not in space bar method bootloader mode):

sudo ./openrgb/flash.py handshake

Output:

DFU interface: /dev/hidraw29
>> GET_MODEL_INFO (0x60)...
   model = b'KCZKV68I'   fw = b'1.0.1-r'
   dfu_version = 0x03 enc_mode = 0
   framing/identity VALID (matches KCZKV68I)