215 lines
9.7 KiB
Markdown
215 lines
9.7 KiB
Markdown
+++
|
|
title = "Keyboard Lighting"
|
|
date = 2024-04-10
|
|
updated = 2024-07-30
|
|
+++
|
|
{% note() %}
|
|
**I am not a keyboard nerd**. Full respect to any of you out there, but I am not one, nor do I plan to become one anytime soon.
|
|
Don't take this as a "definitive guide" to anything, it was just me messing around.
|
|
{% end %}
|
|
|
|
# Introduction
|
|
Yesterday, I started using a new keyboard.
|
|
Well, it was yesterday as of when I started writing this - it certainly isn't yesterday anymore.
|
|
|
|
Previously, I'd been using a super mushy-feeling Incredibly Generic Office Supply Keyboard from Dell, which honestly sucked.
|
|
After a few years of heavy use it had gotten kinda nasty, and was basically uncleanable.
|
|
|
|
Considering how much time I spend typing, I figured it was worth getting an upgrade, so I ordered a
|
|
[Keychron C1 Pro](https://www.keychron.com/products/keychron-c1-pro-qmk-via-wired-mechanical-keyboard?variant=40615805583449)
|
|
(the variant with hot-swappable switches, and therefore also RGB lighting).
|
|
I'm not going to say it's the best keyboard ever and that you should go out and buy it immediately,
|
|
or conversely that it's the *worst* keyboard and you should avoid it at all costs.
|
|
It's fine for my purposes, and is a huge improvement over my old keyboard, but that's beside the point.
|
|
|
|
{% note() %}
|
|
...though I have since sold off the C1 Pro and replaced it with a Rainy 75. It's sooo much better, and I'm not going back.
|
|
{% end %}
|
|
|
|
I didn't buy it for the RGB effects.
|
|
To be honest, I don't even *like* RGB lighting most of the time. I find it kind of distracting,
|
|
and I usually try to avoid "gamer-branded" products stuffed to the brim with multicolored lights like the plague.
|
|
But, being me, and being told "yeah this keyboard has an ARM chip inside it" (a `STM32F402RC` to be precise) I had to see if I could customize it.
|
|
|
|
Aaaand so began an afternoon-long dive down a keyboard rabbit-hole.
|
|
|
|
# VIA
|
|
{% important() %}
|
|
I started writing this before [Keychron Launcher](https://launcher.keychron.com) came out.
|
|
I haven't tried to use it, and don't really know what it's capable of, but it's possible that
|
|
if it had existed at the time I wouldn't have gone down this rabbit hole at all.
|
|
{% end %}
|
|
|
|
From poking around on Keychron's website it looked like the simplest route to customize the lighting was to use [VIA](https://www.caniusevia.com/).
|
|
Firefox doesn't support [webhid](https://caniuse.com/?search=webhid), but Chromium-based browsers do, so I opened up Chromium...
|
|
and what's this? An error? Already? I haven't even *done* anything yet.
|
|
|
|
{{ image(src="/assets/keyboard/keyboard-via-error.png", alt="Screenshot of VIA displaying an error", caption="Extremely useful error message") }}
|
|
|
|
Ooookay?
|
|
|
|
Apparently you need to dig up a keyboard-specific config file (in my case [this json](/assets/keyboard/c1_pro_v2_ansi_rgb.json)) and load it.
|
|
Don't really know how I missed that.
|
|
|
|
In any case, it seemed to be working, and I could cycle through the various RGB modes, as well as customize the static color.
|
|
But, come on, that's *so dreadfully boring*. I wasn't interested in customizing keymaps or anything like that, and I felt... unsatisfied... with the lighting options.
|
|
Apparently their definition of "customizable" lighting doesn't quite align with mine.
|
|
There's a little computer inside there, there's bound to be *something* more I can do with it...
|
|
|
|
# Screwing around with a Windows VM
|
|
After googling around for a while, I stumbled upon [this reddit post](https://www.reddit.com/r/Keychron/comments/lbk2fk/a_very_odd_workaround_for_the_rgb_software/)
|
|
advertising a supposed way to program the lighting using somewhat sketchy-seeming third-party software.
|
|
|
|
Soo... (not knowing any better), I guess it's Windows VM time!
|
|
|
|
{{ image(src="/assets/keyboard/keyboard-windont.png", alt="Windows installation screen inside of QEMU", caption="Ah shit, here we go again") }}
|
|
|
|
Maybe it had something to do with the keyboard itself being unsupported, maybe it was the software,
|
|
maybe it was USB device passthrough being weird, but the app always quit with a "no gaming device found" popup and no other useful information.
|
|
I also tried VIA again and some other software, and had no real success with any of it.
|
|
|
|
Solid waste of an hour.
|
|
|
|
It felt like I was digging too deep into solutions for *my specific keyboard*, but how do people do this more generally?
|
|
|
|
# QMK Firmware
|
|
> Your computer keyboard has a processor inside of it, similar to the one inside your computer.
|
|
> This processor runs software that is responsible for detecting button presses and informing the computer when keys are pressed.
|
|
> QMK Firmware fills the role of that software, detecting button presses and passing that information on to the host computer. [...]
|
|
> QMK tries to put a lot of power into your hands by making easy things easy, and hard things possible.
|
|
>
|
|
> \- [QMK documentation](https://docs.qmk.fm/newbs)
|
|
|
|
Well, this certainly sounds more promising.
|
|
I'm kind of upset I didn't find this earlier, to be honest.
|
|
|
|
Apparently putting custom firmware on a keyboard is a perfectly normal thing? Having not looked into any of this before, it felt a little daunting.
|
|
|
|
> Open your `keymap.c` file in your text editor.
|
|
|
|
Wait what?
|
|
I need C?
|
|
I don't... I don't do C...
|
|
What am I even doing at this point?
|
|
How did I get here?
|
|
Help??? Anyone???
|
|
|
|
|
|
At least the [QMK Documentation](https://docs.qmk.fm/newbs) is fairly comprehensive, and flashing custom firmware isn't *that* much of a pain, all things considered.
|
|
They have it set up to be fairly friendly toward clueless people like me who have no idea what they're doing.
|
|
|
|
I wasn't interested in a custom keymap, I'm just here for the [RGB matrix effects](https://docs.qmk.fm/features/rgb_matrix#custom-rgb-matrix-effects).
|
|
|
|
The basic examples were pretty easy to follow, and it wasn't that hard to display a static pattern of lights.
|
|
Doing something dynamic sounded cool, but also like a project for The Distant Future(tm).
|
|
|
|
## HID Communication
|
|
Somehow, the VIA app was communicating with the keyboard to set the static color without flashing new firmware,
|
|
so I wanted to see if I could imitate that myself.
|
|
|
|
Turns out QMK makes it quite easy to [send data back and forth](https://docs.qmk.fm/features/rawhid)
|
|
between the computer and the keyboard, and after a little fiddling around, I had something promising.
|
|
|
|
## Final Code
|
|
```c
|
|
RGB_MATRIX_EFFECT(custom_colors)
|
|
RGB_MATRIX_EFFECT(pride_flag)
|
|
|
|
#ifdef RGB_MATRIX_CUSTOM_EFFECT_IMPLS
|
|
|
|
// yes I *know* these could be packed but no
|
|
static uint8_t color_r = 0xFF;
|
|
static uint8_t color_g = 0xFF;
|
|
static uint8_t color_b = 0xFF;
|
|
|
|
// I'm almost positive you're not *supposed* to have this in your rgb effect definitions,
|
|
// but uhh... it works... so, I'm not complaining
|
|
#include "raw_hid.h"
|
|
void raw_hid_receive(uint8_t* data, uint8_t length) {
|
|
// this probably isn't proper idiomatic C, but whatever
|
|
color_r = data[0];
|
|
color_g = data[1];
|
|
color_b = data[2];
|
|
}
|
|
|
|
static bool custom_colors(effect_params_t* params) {
|
|
RGB_MATRIX_USE_LIMITS(led_min, led_max)
|
|
for (uint8_t i = led_min; i < led_max; i++) {
|
|
rgb_matrix_set_color(i,
|
|
color_r,
|
|
color_g,
|
|
color_b
|
|
);
|
|
}
|
|
return rgb_matrix_check_finished_leds(led_max);
|
|
}
|
|
|
|
|
|
// simple static effect, just for fun
|
|
static bool pride_flag(effect_params_t* params) {
|
|
RGB_MATRIX_USE_LIMITS(led_min, led_max);
|
|
for (uint8_t i = led_min; i < led_max; i++) {
|
|
int16_t y = g_led_config.point[i].y;
|
|
// hardcoded y values found through trial and error,
|
|
// and are probably specific to my particular keyboard model
|
|
// there's almost certainly a better way, but this Works(tm)
|
|
//
|
|
// tbe colors are more or less completely eyeballed since the
|
|
// color accuracy of this keyboard is garbage
|
|
if (y <= 13) rgb_matrix_set_color(i, 0x00, 0x00, 0x00);
|
|
else if (y <= 26) rgb_matrix_set_color(i, 0x2B, 0x9E, 0xFA);
|
|
else if (y <= 38) rgb_matrix_set_color(i, 0xF0, 0x19, 0x18);
|
|
else if (y <= 51) rgb_matrix_set_color(i, 0xFF, 0xFF, 0xFF);
|
|
else if (y <= 63) rgb_matrix_set_color(i, 0xF5, 0x19, 0x18);
|
|
else rgb_matrix_set_color(i, 0x2B, 0x9E, 0xFA);
|
|
}
|
|
return rgb_matrix_check_finished_leds(led_max);
|
|
}
|
|
|
|
#endif
|
|
```
|
|
|
|
I then wrote this little command-line thing to let me change the keyboard color on the fly.
|
|
|
|
```rs
|
|
use hidapi::HidApi;
|
|
use std::env;
|
|
|
|
fn main() {
|
|
let color = u32::from_str_radix(&env::args().nth(1).expect("specify a color"), 16)
|
|
.expect("failed to parse color");
|
|
|
|
let api = HidApi::new().unwrap();
|
|
let device = api
|
|
.device_list()
|
|
.find(|device|
|
|
// this is... less than great
|
|
device.vendor_id() == 0x3434 &&
|
|
device.product_id() == 0x0510 &&
|
|
device.usage_page() == 0xFF60 &&
|
|
device.usage() == 0x61
|
|
).expect("failed to find keyboard")
|
|
.open_device(&api)
|
|
.expect("failed to open device");
|
|
|
|
let mut data = [0u8; 32]; // must be 32 bytes
|
|
data[1..4].copy_from_slice(&color.to_be_bytes()[1..4]);
|
|
device.write(&data).expect("failed to set color");
|
|
}
|
|
```
|
|
|
|
It works! Yay!
|
|
|
|
I'm actually surprised it was that straightforward; when the QMK docs first mentioned using C and flashing
|
|
custom firmware on the keyboard I expected it to be a *lot* worse than it actually ended up being.
|
|
|
|
# Conclusion
|
|
This was a bit of an adventure.
|
|
I only barely scratched the surface when it comes to QMK, but it's still super cool to see something like this actually *work*.
|
|
|
|
There's clearly a lot more you can do with custom firmware, especially considering HID communication.
|
|
I can imagine someone doing all sorts of cool things with different keys lighting up to signal different things.
|
|
Maybe a push-to-talk key (for e.g. Discord) with a colorful status indicator?
|
|
|
|
I'm curious what kind of crazy stuff people have come up with.
|
|
|