This is my personal ZMK firmware configuration build upon excellent config from urob. It consists of a 42-keys base layout used on my Corneish-Zen and Kinesis Advantage 360 Pro layout.
- "Timeless" homerow mods
- Magic thumb quadrupling as Sticky-shift/Capsword/Shift
- Arrow-cluster doubles as home, end, begin/end of document on long-press
- Simpler Devicetree syntax using helper macros from zmk-helpers
- Fully automated, nix-powered local build environment
-
Install the
nix
package manager:# Install Nix with flake support enabled curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install --no-confirm # Start the nix daemon without restarting the shell . /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh
-
Install
direnv
(and optionally but recommendednix-direnv
1) using your package manager of choice. E.g., using thenix
package manager that we just installed2:nix profile install nixpkgs#direnv nixpkgs#nix-direnv
-
Set up the
direnv
shell-hook for your shell. E.g., forbash
:# Install the shell-hook echo 'eval "$(direnv hook bash)"' >> ~/.bashrc # Enable nix-direnv (if installed in the previous step) mkdir -p ~/.config/direnv echo 'source $HOME/.nix-profile/share/nix-direnv/direnvrc' >> ~/.config/direnv/direnvrc # Optional: make direnv less verbose echo '[global]\nwarn_timeout = "2m"\nhide_env_diff = true' >> ~/.config/direnv/direnv.toml # Source the bashrc to activate the hook (or start a new shell) source ~/.bashrc
-
Clone your fork of this repository. I like to name my local clone
zmk-workspace
as it will be the toplevel of the development environment.# Replace `urob` with your username git clone https://github.com/urob/zmk-config zmk-workspace
-
Enter the workspace and set up the environment.
# The first time you enter the workspace, you will be prompted to allow direnv cd zmk-workspace # Allow direnv for the workspace, which will set up the environment (this takes a while) direnv allow # Initialize the Zephyr workspace and pull in the ZMK dependencies # (same as `west init -l config && west update && west zephyr-export`) just init
After following the steps above your workspace should look like this:
zmk-workspace
├── config
├── firmware (created after building)
├── modules
├── zephyr
└── zmk
To build the firmware, simply type just build all
from anywhere in the
workspace. This will parse build.yaml
and build the firmware for all board and
shield combinations listed there.
To only build the firmware for a specific target, use just build <target>
.
This will build the firmware for all matching board and shield combinations. For
instance, to build the firmware for my Corneish Zen, I can type
just build zen
, which builds both corneish_zen_v2_left
and
corneish_zen_v2_right
. (just list
shows all valid build targets.)
Additional arguments to just build
are passed on to west
. For instance, a
pristine build can be triggered with just build all -p
.
(For this particular example, there is also a just clean
recipe, which clears
the build cache. To list all available recipes, type just
. Bonus tip: just
provides
completion scripts
for many shells.)
The build environment packages
keymap-drawer.
just draw <keyboard> <keymap>
parses relevant keymap for a given keyboard and
draws it to draw/keymap.svg
. For my two keyboards that is
just draw adv360 adv360
and just draw corne_rotated corne
.
To make changes to the ZMK source or any of the modules, simply edit the files
or use git
to pull in changes.
To switch to any remote branches or tags, use git fetch
inside a module
directory to make the remote refs locally available. Then switch to the desired
branch with git checkout <branch>
as usual. You may also want to register
additional remotes to work with or consider making them the default in
config/west.yml
.
To update the ZMK dependencies, use just update
. This will pull in the latest
version of ZMK and all modules specified in config/west.yml
. Make sure to
commit and push all local changes you have made to ZMK and the modules before
running this command, as this will overwrite them.
To upgrade the Zephyr SDK and Python build dependencies, use just upgrade-sdk
.
(Use with care -- Running this will upgrade all Nix packages and may end up
breaking the build environment. When in doubt, I recommend keeping the
environment pinned to flake.lock
, which is
continuously tested
on all systems.)
Using the same Nix-based environment, I have set up a drop-in replacement for the default ZMK Github Actions build workflow. While mainly a proof-of-concept, it does run moderately faster, especially with a cold cache.
Since I switched from QMK to ZMK I have been very impressed with how easy it is to set up relatively complex layouts in ZMK. For the most parts I don't miss any functionality (to the contrary, I found that ZMK supports many features natively that would require complex user-space implementations in QMK). Below are a few remaining issues:
- ZMK does not yet support "tap-only" combos (#544), requiring a brief pause when wanting to chord HRMs that overlap with combo positions. As a workaround, I implemented all homerow combos as homerow-mod-combos. This is good enough for day-to-day, but does not address all edge cases (eg changing active mods).
- Very minor:
&bootloader
doesn't work with stm32 boards like the Planck (#1086)
- The collection of ZMK modules used in this configuration.
- A ZMK-centric introduction to Git (useful for maintaining your own ZMK fork with a custom selection of PRs).
Footnotes
-
nix-direnv
provides a vastly improved caching experience compared to only havingdirenv
, making entering and exiting the workspace instantaneous after the first time. ↩ -
This will permanently install the packages into your local profile, forgoing many of the benefits that make Nix uniquely powerful. A better approach, though beyond the scope of this document, is to use
home-manager
to maintain your user environment. ↩