I recently bought the wonderful Keychron Q6 Max mechanical keyboard, and have happily jumped down the rabbit hole of custom switches and keycaps. One of the features of the Q6 is its open source QMK firmware. While QMK, with the Via gui, is absolutely amazing at customizing the keymaps, it is limited in its built-in RGB effects. There are 22 builtin animations, but surprisingly there is no builtin way to set simple static zone lighting colors, let alone full per key RGB effects. You can set a static color over the entire keyboard, as shown above, but not separately for different keyboard zones.
No problem - with QMK you can program whatever RGB effects you want, so long as you don't mind a bit of coding and reflashing the keyboard. Unfortunately this is a bit tricky, mostly because it is so poorly documented. The following is a tutorial on modifying, compiling and flashing QMK on the Q6, from Fedora 40 Linux. This should also work for other Keychron keyboards and other versions of Linux.
I. Some initial key lessons:
1. The master QMK github repo appears to have support for the keychron Q6, but this upstreamed version is dated and does not work for the Q6 MAX. Keychron's cloned QMK github master branch also does not support the Q6 Max - you have to switch to its "wireless_playground" branch. That code base works perfectly.
2. Reflashing and configuration with Via must be done over a USB cable (not a wifi or bluetooth connection.) Once reflashed and configured, you can use any connection.
3. To enter dfu mode on the keyboard (to allow reflashing):
Normally, just unplug the USB cable to the keyboard, hold 'ESC', and plug the cable back in, and release 'ESC'. This will work as long as the keyboard already has working firmware.
If this does not work, the backup is to pop off the 'space' keycap. Press and hold the pushbutton under the spacebar, while plugging the usb cable. This should always work. (And yes, I corrupted the firmware really well once, and had to do this.)
You can tell what mode the keyboard is in with lsusb. In keyboard mode lsusb will show:
Bus 003 Device 010: ID 3434:0860 Keychron Keychron Q6 Max
In dfu mode lsusb will show:
Bus 003 Device 012: ID 0483:df11 STMicroelectronics STM Device in DFU Mode
Note the IDs shown, which are in vendorid:productid format, as we will need them later.
4. If compiling QMK on Fedora 40 Linux, you really want to use the "docker" build support, but this has a bug with selinux permissions, with a workaround listed below.
II. Compiling and loading an unmodified QMK
(Make sure this works before getting fancy).
This example is for Fedora 40. YMMV. Unless otherwise noted, all commands are run as a user.
1. Setup udev for user level access. Use lsusb to get the vendor and product id codes as shown above, and modify the lines below as needed. As root add the file /etc/udev/rules.d/99-keychron.rules with the two lines:
KERNEL=="hidraw*", SUBSYSTEM=="hidraw", ATTRS{idVendor}=="3434", ATTRS{idProduct}=="0860", MODE="0660", GROUP="wheel", TAG+="uaccess", TAG+="udev-acl"
ATTRS{idVendor}=="0483", ATTRS{idProduct}=="df11", MODE="0666", GROUP="wheel", TAG+="uaccess", TAG+="udev-acl"
As you read this the two lines probably have wrapped, but in the rules file, they must be entered as two lines.
Then as root, run "udevadm --trigger".
2. Compile unmodified OEM QMK for Keychron Q6 Max. It is recommended to create the qmk_firmware directory in your home directory. (You can put it elsewhere, but then you have to do something clever with qmk setup...)
Install prerequisites:
sudo dnf install git python3-pip dfu-util
python3 -m pip install --user qmk
Install dfu-programmer using the instructions at https://github.com/dfu-programmer/dfu-programmer
(Fedora does not have this package in its repo.)
Clone the correct Keychron QMK firmware directory:
cd
git clone --recurse-submodules https://github.com/Keychron/qmk_firmware.git
cd qmk_firmware
git switch wireless_playground
qmk git-submodule
Edit util/docker_build.sh, add a ":z" to the existing line to be
-v "$dir":/qmk_firmware:z \
(On Fedora 40, this labels the mounted docker volume and makes selinux happy.)
Run the actual compile:
util/docker_build.sh keychron/q6_max/ansi_encoder:via
This should build the binary qmk_firmware/keychron_q6_max_ansi_encoder_via.bin in the qmk_firmware top directory.
3. Flash the new binary:
Enter dfu mode: hold 'ESC' and connect keyboard with usb cable.
Flash the firmware with:
dfu-util -a 0 --dfuse-address 0x8000000 -D keychron_q6_max_ansi_encoder_via.bin
Note in dfu mode, the keyboard does not work as a keyboard, so you either need a second keyboard, or onscreen keyboard to press enter for the dfu command. The simplest way is to setup the dfu-util command with the Q6, then enter dfu-mode, and use your mouse to turn on the onscreen keyboard (in gnome settings -> Accessability -> Typing -> Screen Keyboard), and use the onscreen keyboard to press enter on the command line.
Once flashed, power cycle the Q6 (unplug it and plug it back in), and it should work exactly as from the factory.
III. Modifying QMK to add zone colors:
The preferred method of adding a custom rgb matrix effect is to add a user defined effect specific to the keyboard. This requires no modification to the base QMK code, and is both simpler and safer, but limited to one keyboard. You can optionally add the effect to the base QMK RGB Matrix code, so that it covers all keyboards, but that is more complex. In this example we want to define keyboard zones, and colors for these zones, so it makes sense to do it as a user custom effect for just the target Q6 Max keyboard. Other keyboards would have to define their own zones anyway.
A user defined rgb_matrix mode is defined in a keyboard's rgb_matrix_user.inc file. Any mode in this file will be added to the end of the existing global rgb matrix modes, and can be controlled with the RGB keys and Via.
The basic approach is to define the led zones in the keyboards encoder file (where all other details of that keyboards led layout are already defined). For simple zones we define a g_led_zone matrix, which selects one of eight zones (0..7) for each key. For simplicity, we will treat these zone numbers as an RGB bitmask. If the one's bit is on, allow Red from the current color. If the two's bit is set, allow Green, and if the four's bit is set, allow Blue.
So the masks result in:
0 - black
1 - red
2 - green
3 - yellow
4 - blue
5 - magenta
6 - cyan
7 - white
These masks are and'ed with the current color and brightness, so you can get different final colors at runtime. You could do a more complex system to get true RGB per key, but then you would have to do a lot of work to get Via to understand how to configure them all.
Then we code an rgb_matrix_user.inc file, with simple code to set the color for each led, based on its zone and the current global color map.
Modify the code:
1. Modify your keyboard's encoder.c file, e.g. qmk_firmware/keyboards/keychron/q6_max/ansi_encoder/ansi_encoder.c
to add the following keyboard zone definitions just before the last line's "#endif":
// Eight zones (0..7), where low three bits represent R, G, B enabled for that key.
// I'm not using 0, as that's black, or 3 (yellow) just because.
uint8_t g_led_zone[RGB_MATRIX_LED_COUNT] = {
// RGB zone map
5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2, 2, 2, 4, 4, 4,
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 1, 2, 2, 2, 4, 4, 4,
1, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 1, 2, 2, 2, 4, 4, 4,
1, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 1, 4, 4, 4, 4, 4, 4, 4,
1, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 1, 6, 4, 4, 4,
1, 1, 1, 1, 1, 1, 1, 1, 6, 6, 6, 4, 4,
};
Note that this matrix layout does not look exactly like the real keyboard, which is 6 rows by 21 keys. Keychron had to modify the led wiring to stay under 20 columns due to a lack of I/O pins, so the right most four keys are folded into the normally blank spaces above the arrow keys. (The first four '4's on line four are actually for the four keys in the right most physical keyboard column). Details and pretty pictures are are at: https://github.com/Keychron/qmk_firmware/issues/233
2. Modify your keyboard's top level rules.mk (e.g. qmk_firmware/keyboards/keychron/q6_max/rules.mk) to add this line at the end of the file:
RGB_MATRIX_CUSTOM_USER = yes
3. Add the following file rgb_matrix_user.inc to your keyboard top level directory.
(e.g. qmk_firmware/keyboards/keychron/q6_max/rgb_matrix_user.inc):
// rgb_matrix_user.inc
RGB_MATRIX_EFFECT(ZONE_RGB)
#ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
// use user zone extension of led_config to set solid colors
// Lower bits of zone uint8_t are bitmsks to turn on R, G, and B respectively.
extern uint8_t g_led_zone[RGB_MATRIX_LED_COUNT]; // in G6 Max's ansi_encoder.c
static bool ZONE_RGB(effect_params_t* params) {
RGB_MATRIX_USE_LIMITS(led_min, led_max);
RGB rgb = rgb_matrix_hsv_to_rgb(rgb_matrix_config.hsv);
for (uint8_t i = led_min; i < led_max; i++) {
rgb_matrix_set_color(i, rgb.r & (g_led_zone[i] & 1 ? 255 : 0),
rgb.g & (g_led_zone[i] & 2 ? 255 : 0), rgb.b & (g_led_zone[i] & 4 ? 255 : 0));
}
return rgb_matrix_check_finished_leds(led_max);
}
#endif // RGB_MATRIX_CUSTOM_EFFECT_IMPLS
4. Compile the modified code with
util/docker_build.sh keychron/q6_max/ansi_encoder:via
5. Flash the new firmware as above in II-3.
6. edit the Via json file (e.g. qmk_firmware/keyboards/keychron/q6_max/via_json/q6_max_ansi_encoder.json) to add
["Zone RGB", 23]
to the end of the effects matrix (being sure to add a comma at the end of the prior line.) In the Via gui "design" page (https://usevia.app/design), "load" this modified json file so that Via understands the new mode. The new user rgb matrix mode will appear at the end of the Via modes menu, and can also be selected with the RGBMode button (fn-Q), and it will be preserved across power cycles.
The Via lighting configuration page should look like:
You probably want to configure this effect's color as white (0xffffff) as shown, so that all the zones show up as primaries. Changing the primary color can lead to interesting effective color sets.
And the keyboard should look like: