Configuring QMK firmware for your keyboard
- Tutorial
- NixOS
- Keyboards
Configuring QMK on linux is very easy, especially on NixOS. Here is a short guide showing the steps required to do so. As always, it’s a good idea to read the documentation. This is just some of the snippets I found the most useful and a quick-start guide.
Creating a fork
To configure your own keymaps, you need a fork of QMK. Head to qmk/qmk_firmware and create a fork, in my case called LilleAila/qmk_firmware
, then clone it to your local machine and set up the upstream remote:
git clone [email protected]:LilleAila/qmk_firmware ~/qmk_firmware
cd ~/qmk_firmware
git remote add upstream https://github.com/qmk/qmk_firmware
git fetch upstream
git push -u origin master
Setting up your environment
QMK requires some udev rules, which can be enabled in your NixOS configuration using
hardware.keyboard.qmk.enable = true;
Additionally, QMK provides a nix-shell, so all you need to do is use the included shell.nix
, for example with direnv
echo "use nix" > .envrc && direnv allow
Now, run the following command to fetch all submodules and dependencies required by QMK.
qmk setup
That’s it. Now you have a fully working environment for configuring your QMK keymaps. Keep reading if you want to learn how to keep it up to date and create your own keymap.
Keeping your fork updated
You want to occasionally rebase your fork on qmk/qmk_firmware
so that it stays up to date. To do that, run the following commands:
git fetch upstream
git merge upstream/master
git push
If you run into problems when compiling, you might have to run qmk setup
again.
Compiling and flashing a keyboard
Use the qmk
command to compile a keymap for your keyboard. Replace beekeeb/piantor_pro
with your keyboard, and default
with your keymap.
qmk compile -kb beekeeb/piantor_pro -km default
The keyboard can also be flashed directly, using this command:
qmk flash -kb beekeeb/piantor_pro -km default
Make sure your keyboard is in DFU mode. There is usually a button somewhere on the keyboard, or you can use the QK_BOOK
key.
Adding your own keymap
Locate your keyboard in keyboards/
, and create your own keymap in a subfolder with the name you want to give it. In my case, i used my name at keyboards/beekeeb/piantor_pro/keymaps/olai/
. It can be beneficial to copy the default layout of your keyboard, then make the changes you want. The important files here are keymap.c
, which is where you define your keymap, and rules.mk
, which has extra settings.
Keymap syntax
Keymaps are usually defined at the end of the file, using syntax similar to this:
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[0] = LAYOUT_split_3x6_3( // split with 6 columns of 3 keys, plus 3 at the thump
// First layer (default)
),
[1] = LAYOUT_split_3x6_3(
// Other layers
)
}
Keys are under the KC_
prefix, for example
KC_A
KC_B
KC_C
KC_TAB
KC_LCTL
Home-row modifiers allow you to press the key regularly, but act as a modifier when held. They can be used like this:
CTL_T(KC_A), OPT_T(KC_R), GUI_T(KC_S), SFT_T(KC_T)
Layers can be named using an enum, like this:
enum layers {
_MAIN,
_QWERTY,
_FN
}
The first layer in the enum becomes the default layout, and the names can be used instead of integers, like this:
const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
[_MAIN] = LAYOUT_split_3x6_3(),
[_QWERTY] = LAYOUT_split_3x6_3(),
[_FN] = LAYOUT_split_3x6_3()
}
Layers can be switched to when held using MO(_FN)
, and set to default using DF(_QWERTY)
. Layers should generally be in an order such that the main keyboard layout with alphabetical keys is the lowest, and layers stacked on top of it are higher.
Auto-detection of operating system
MacOS uses a different layout than other systems, so a snippet like this can be used. Add this to rules.mk
:
OS_DETECTION_ENABLE = yes
DEFERRED_EXEC_ENABLE = yes
And add this to keymap.c
:
#if defined(OS_DETECTION_ENABLE) && defined(DEFERRED_EXEC_ENABLE)
#include "os_detection.h"
os_variant_t os_type;
uint32_t detect_os(uint32_t trigger_time, void *cb_arg) {
if (is_keyboard_master()) {
os_type = detected_host_os();
if (os_type) {
switch (os_type) {
case OS_MACOS:
layer_move(_MAIN_MAC);
break;
case OS_IOS:
layer_move(_MAIN_MAC);
break;
case OS_LINUX:
layer_move(_MAIN_LINUX);
break;
case OS_WINDOWS:
layer_move(_MAIN_WINDOWS);
break;
case OS_UNSURE:
layer_move(_MAIN_LINUX);
break;
default:
layer_move(_MAIN_LINUX);
break;
}
}
}
return os_type ? 0 : 500;
}
void keyboard_post_init_user(void) {
defer_exec(100, detect_os, NULL);
}
#endif
Linux and windows generally use the same layouts, so that can be used as the default if MacOS was not detected.