blog.lazkani.io/content/posts/qmk-firmware.md

347 lines
22 KiB
Markdown

+++
title = "QMK Firmware"
author = ["Elia el Lazkani"]
date = 2021-09-23
lastmod = 2021-09-23
tags = ["qmk", "firmware", "mechanical-keyboard", "qmk-firmware"]
categories = ["misc"]
draft = false
+++
Over the years, I have owned a few mechanical keyboards. I'm quite fond of them.
I've also built my own keyboard from scratch years ago. Hot-swappable back then
was still in its easy stages and the sockets weren't that good. Alas, we're in
2021 and I've recently purchased the **Keychron Q1** keyboard.
I've chosen this keyboard for many reasons, but the one you most care about is
the topic that brought you here. It's a **QMK Firmware** compatible keyboards. Do
you know what that means ?
That means that we're going to be digging into `qmk_firmware`. Tag along !
<!--more-->
## Quantum Mechanical Keyboard Firmware {#quantum-mechanical-keyboard-firmware}
The [**QMK Firmware**](https://github.com/qmk/qmk%5Ffirmware) is
> a keyboard firmware based on the tmk\_keyboard firmware with some useful features
> for Atmel AVR and ARM controllers, and more specifically, the OLKB product line,
> the ErgoDox EZ keyboard, and the Clueboard product line.
It goes beyond saying, the **QMK Firmware** is open sourced. So let's hack it.
## Building QMK Firmware {#building-qmk-firmware}
The first step to flashing your keyboard starts here. We need to get the source
code of `qmk_firmware` from Github.
```shell
$ git clone https://github.com/qmk/qmk_firmware.git
# Wait a while...
# Yup, I know !
# Okay finally...
Cloning into 'qmk_firmware'...
remote: Enumerating objects: 295442, done.
remote: Counting objects: 100% (34/34), done.
remote: Compressing objects: 100% (27/27), done.
remote: Total 295442 (delta 13), reused 17 (delta 5), pack-reused 295408
Receiving objects: 100% (295442/295442), 178.92 MiB | 7.10 MiB/s, done.
Resolving deltas: 100% (178414/178414), done.
Updating files: 100% (27916/27916), done.
$ cd qmk_firmware
```
Once the repository is clone, we can start with installing the dependencies to
build `qmk`.
I'm not a big fan of auto-installers or installers scripts
(`util/install/arch.sh`), and here's why.
```bash
python3 -m pip install --user -r $QMK_FIRMWARE_DIR/requirements.txt
```
This is how the installer of the `qmk_firmware` concludes the round. I would
hate to use pip to install willy nilly like that.
Otherwise, I don't have objections to what it does on `arch` at least.
It does the following, I see no reason not to follow it.
```shell
$ sudo pacman -S \
base-devel clang diffutils gcc git unzip wget zip python-pip \
avr-binutils arm-none-eabi-binutils arm-none-eabi-gcc \
arm-none-eabi-newlib avrdude dfu-programmer dfu-util
$ sudo pacman -U https://archive.archlinux.org/packages/a/avr-gcc/avr-gcc-8.3.0-1-x86_64.pkg.tar.xz
$ sudo pacman -S avr-libc # Must be installed after the above, or it will bring in the latest avr-gcc instead
$ sudo pacman -S hidapi # This will fail if the community repo isn't enabled
```
Now that all the dependencies required by the system are installed, let's
install the `python` dependencies.
```shell
$ git checkout 0.14.9 # Checkout the latest version
$ vf new qmk_firmware # Create a new python virtualenv and activate it
$ pip install -r requirements.txt # Install python requirements
$ pip install qmk
$ make git-submodule
```
Finally, we can build our keyboard firmware.
```bash
$ qmk compile -kb keychron/q1/rev_0100 -km default
Ψ Compiling keymap with make --jobs=1 keychron/q1/rev_0100:default [31/494]
QMK Firmware 0.14.16
Making keychron/q1/rev_0100 with keymap default
avr-gcc (GCC) 11.2.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Compiling: keyboards/keychron/q1/q1.c [OK]
Compiling: keyboards/keychron/q1/rev_0100/rev_0100.c [OK]
Compiling: keyboards/keychron/q1/rev_0100/keymaps/default/keymap.c [OK]
Compiling: quantum/quantum.c [OK]
Compiling: quantum/send_string.c [OK]
Compiling: quantum/bitwise.c [OK]
Compiling: quantum/led.c [OK]
Compiling: quantum/action.c [OK]
Compiling: quantum/action_layer.c [OK]
Compiling: quantum/action_macro.c [OK]
Compiling: quantum/action_tapping.c [OK]
Compiling: quantum/action_util.c [OK]
Compiling: quantum/eeconfig.c [OK]
Compiling: quantum/keyboard.c [OK]
Compiling: quantum/keymap_common.c [OK]
Compiling: quantum/keycode_config.c [OK]
Compiling: quantum/logging/debug.c [OK]
Compiling: quantum/logging/sendchar.c [OK]
Compiling: quantum/bootmagic/bootmagic_lite.c [OK]
Compiling: quantum/bootmagic/magic.c [OK]
Compiling: quantum/matrix_common.c [OK]
Compiling: quantum/matrix.c [OK]
Compiling: quantum/debounce/sym_defer_g.c [OK]
Compiling: quantum/main.c [OK]
Compiling: quantum/color.c [OK]
Compiling: quantum/rgb_matrix/rgb_matrix.c [OK]
Compiling: quantum/rgb_matrix/rgb_matrix_drivers.c [OK]
Compiling: lib/lib8tion/lib8tion.c [OK]
Compiling: drivers/led/issi/is31fl3733.c [OK]
Compiling: quantum/process_keycode/process_rgb.c [OK]
Compiling: quantum/led_tables.c [OK]
Compiling: quantum/dip_switch.c [OK]
Compiling: quantum/process_keycode/process_space_cadet.c [OK]
Compiling: quantum/process_keycode/process_magic.c [OK]
Compiling: quantum/process_keycode/process_grave_esc.c [OK]
Compiling: platforms/avr/drivers/i2c_master.c [OK]
Archiving: .build/obj_keychron_q1_rev_0100_default/i2c_master.o [OK]
Compiling: tmk_core/common/host.c [OK]
Compiling: tmk_core/common/report.c [OK]
Compiling: tmk_core/common/sync_timer.c [OK]
Compiling: tmk_core/common/usb_util.c [OK]
Compiling: tmk_core/common/avr/platform.c [OK]
Compiling: tmk_core/common/avr/suspend.c [OK]
Compiling: tmk_core/common/avr/timer.c [OK]
Compiling: tmk_core/common/avr/bootloader.c [OK]
Assembling: tmk_core/common/avr/xprintf.S [OK]
Compiling: tmk_core/common/avr/printf.c [OK]
Compiling: tmk_core/protocol/lufa/lufa.c [OK]
Compiling: tmk_core/protocol/usb_descriptor.c [OK]
Compiling: lib/lufa/LUFA/Drivers/USB/Class/Common/HIDParser.c [OK]
Compiling: lib/lufa/LUFA/Drivers/USB/Core/AVR8/Device_AVR8.c [OK]
Compiling: lib/lufa/LUFA/Drivers/USB/Core/AVR8/EndpointStream_AVR8.c [OK]
Compiling: lib/lufa/LUFA/Drivers/USB/Core/AVR8/Endpoint_AVR8.c [OK]
Compiling: lib/lufa/LUFA/Drivers/USB/Core/AVR8/Host_AVR8.c [OK]
Compiling: lib/lufa/LUFA/Drivers/USB/Core/AVR8/PipeStream_AVR8.c [OK]
Compiling: lib/lufa/LUFA/Drivers/USB/Core/AVR8/Pipe_AVR8.c [OK]
Compiling: lib/lufa/LUFA/Drivers/USB/Core/AVR8/USBController_AVR8.c [OK]
Compiling: lib/lufa/LUFA/Drivers/USB/Core/AVR8/USBInterrupt_AVR8.c [OK]
Compiling: lib/lufa/LUFA/Drivers/USB/Core/ConfigDescriptors.c [OK]
Compiling: lib/lufa/LUFA/Drivers/USB/Core/DeviceStandardReq.c [OK]
Compiling: lib/lufa/LUFA/Drivers/USB/Core/Events.c [OK]
Compiling: lib/lufa/LUFA/Drivers/USB/Core/HostStandardReq.c [OK]
Compiling: lib/lufa/LUFA/Drivers/USB/Core/USBTask.c [OK]
Compiling: tmk_core/protocol/lufa/usb_util.c [OK]
Linking: .build/keychron_q1_rev_0100_default.elf [OK]
Creating load file for flashing: .build/keychron_q1_rev_0100_default.hex [OK]
Copying keychron_q1_rev_0100_default.hex to qmk_firmware folder [OK]
Checking file size of keychron_q1_rev_0100_default.hex [OK]
* The firmware size is fine - 23302/28672 (81%, 5370 bytes free)
```
Look at tha, easy as pie ! You got yourself a compiled firmware.
Before we move on, let's look at the command again and figure out what the hell
I did, just in case you're running a different keyboard.
If you look into the `keyboards/`, you'll be able to find a big list of
keyboards supported. The `keychron/q1/rev_0100` is simply a directory in there
that matches my keyboard. Inside that directory, we can find the `keymaps/`
directory. This is where all the keymaps live. We chose the `default` keymap
which is a directory in there as well.
## Remapping the keyboard {#remapping-the-keyboard}
At this stage, we were able to succesfully compile the keyboard firmware. But
the whole point of this is to modify the layout of the keyboard so let's go
right ahead.
There are commands suggested on the `QMK` docs but I didn't go that far, I
simply copied the `default` directory and went down to business. For the sake of
this blog post, I'll assume I called the directory `functions`.
The `keymap.c` file looks as follows.
```c
#include QMK_KEYBOARD_H
enum layers{
MAC_BASE,
MAC_FN,
WIN_BASE,
WIN_FN
};
#define KC_TASK LGUI(KC_TAB)
#define KC_FLXP LGUI(KC_E)
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[MAC_BASE] = LAYOUT_ansi_82(
KC_ESC, KC_BRID, KC_BRIU, KC_F3, KC_F4, RGB_VAD, RGB_VAI, KC_MPRV, KC_MPLY, KC_MNXT, KC_MUTE, KC_VOLD, KC_VOLU, KC_DEL, KC_INS,
KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_PGUP,
KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS, KC_PGDN,
KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT, KC_HOME,
KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP,
KC_LCTL, KC_LALT, KC_LGUI, KC_SPC, KC_RGUI, MO(MAC_FN),KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT),
[MAC_FN] = LAYOUT_ansi_82(
KC_TRNS, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
RGB_TOG, RGB_MOD, RGB_VAI, RGB_HUI, RGB_SAI, RGB_SPI, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, RGB_RMOD, RGB_VAD, RGB_HUD, RGB_SAD, RGB_SPD, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS),
[WIN_BASE] = LAYOUT_ansi_82(
KC_ESC, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, KC_DEL, KC_INS,
KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_PGUP,
KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS, KC_PGDN,
KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT, KC_HOME,
KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP,
KC_LCTL, KC_LGUI, KC_LALT, KC_SPC, KC_RALT, MO(WIN_FN),KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT),
[WIN_FN] = LAYOUT_ansi_82(
KC_TRNS, KC_BRID, KC_BRIU, KC_TASK, KC_FLXP, RGB_VAD, RGB_VAI, KC_MPRV, KC_MPLY, KC_MNXT, KC_MUTE, KC_VOLD, KC_VOLU, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
RGB_TOG, RGB_MOD, RGB_VAI, RGB_HUI, RGB_SAI, RGB_SPI, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, RGB_RMOD, RGB_VAD, RGB_HUD, RGB_SAD, RGB_SPD, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS)
};
```
If you read this you will understand that the keyboard originally comes with 4
layers. Two for windows and two for Mac. The `0` and `1` layers are toggled
using a physical switch. The rest are toggled with the `Fn` key.
Now let's change the Mac layout to have the `Function` keys to be on the main
layer while the media keys to be toggled with the `Fn` key. The final version
should look like the following.
```c
#include QMK_KEYBOARD_H
enum layers{
MAC_BASE,
MAC_FN,
WIN_BASE,
WIN_FN
};
#define KC_TASK LGUI(KC_TAB)
#define KC_FLXP LGUI(KC_E)
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[MAC_BASE] = LAYOUT_ansi_82(
KC_ESC, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, KC_TRNS, KC_TRNS,
KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_PGUP,
KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS, KC_PGDN,
KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT, KC_HOME,
KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP,
KC_LCTL, KC_LALT, KC_LGUI, KC_SPC, KC_RGUI, MO(MAC_FN),KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT),
[MAC_FN] = LAYOUT_ansi_82(
KC_TRNS, KC_BRID, KC_BRIU, KC_F3, KC_F4, RGB_VAD, RGB_VAI, KC_MPRV, KC_MPLY, KC_MNXT, KC_MUTE, KC_VOLD, KC_VOLU, KC_DEL, KC_INS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
RGB_TOG, RGB_MOD, RGB_VAI, RGB_HUI, RGB_SAI, RGB_SPI, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, RGB_RMOD, RGB_VAD, RGB_HUD, RGB_SAD, RGB_SPD, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS),
[WIN_BASE] = LAYOUT_ansi_82(
KC_ESC, KC_F1, KC_F2, KC_F3, KC_F4, KC_F5, KC_F6, KC_F7, KC_F8, KC_F9, KC_F10, KC_F11, KC_F12, KC_DEL, KC_INS,
KC_GRV, KC_1, KC_2, KC_3, KC_4, KC_5, KC_6, KC_7, KC_8, KC_9, KC_0, KC_MINS, KC_EQL, KC_BSPC, KC_PGUP,
KC_TAB, KC_Q, KC_W, KC_E, KC_R, KC_T, KC_Y, KC_U, KC_I, KC_O, KC_P, KC_LBRC, KC_RBRC, KC_BSLS, KC_PGDN,
KC_CAPS, KC_A, KC_S, KC_D, KC_F, KC_G, KC_H, KC_J, KC_K, KC_L, KC_SCLN, KC_QUOT, KC_ENT, KC_HOME,
KC_LSFT, KC_Z, KC_X, KC_C, KC_V, KC_B, KC_N, KC_M, KC_COMM, KC_DOT, KC_SLSH, KC_RSFT, KC_UP,
KC_LCTL, KC_LGUI, KC_LALT, KC_SPC, KC_RALT, MO(WIN_FN),KC_RCTL, KC_LEFT, KC_DOWN, KC_RGHT),
[WIN_FN] = LAYOUT_ansi_82(
KC_TRNS, KC_BRID, KC_BRIU, KC_TASK, KC_FLXP, RGB_VAD, RGB_VAI, KC_MPRV, KC_MPLY, KC_MNXT, KC_MUTE, KC_VOLD, KC_VOLU, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
RGB_TOG, RGB_MOD, RGB_VAI, RGB_HUI, RGB_SAI, RGB_SPI, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, RGB_RMOD, RGB_VAD, RGB_HUD, RGB_SAD, RGB_SPD, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS,
KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS, KC_TRNS)
};
```
Now that that's done, we need to compile to check that we didn't forget
anything.
```shell
$ qmk compile -kb keychron/q1/rev_0100 -km functions
```
We seem to have successfully compiled our now keyboard layout.
## Flashing your keyboard {#flashing-your-keyboard}
If you're reached this stage, you'll need to locate the `reset` button on your
keyboard. Once located, follow your keyboard's manual on how to **reset** the
board and getting ready it for flashing.
Once the keyboard is ready to be flashed, you basically change one thing in your
previous command.
```shell
$ qmk flash -kb keychron/q1/rev_0100 -km functions
```
If this step succeeds, your keyboard should be ready to use in the newly
configured layout. Check it out !
## Conclusion {#conclusion}
It's pretty awesome to see keyboards like these hit the market. Whether you're
fan of the mechanical switches they come with or not, one thing is certain. You
cannot deny the fact that they are very customisable. If you don't like
something with your keyboard, simply change it. The beauty of it all is that the
firmware is open sourced. The community delivers, yet again !