Existing user config repositories using Github Actions to build will pull down Zephyr 2.5 automatically,
and should work, fine as is. However, to upgrade to the newer Docker image, you should:
Open .github/workflows/build.yml in your editor/IDE
Change zmkfirmware/zmk-build-arm:2.4 to zmkfirmware/zmk-build-arm:2.5 wherever it is found
If you created your user config repository a while ago, you may find that your build.yml file instead references
a zephyr-west-action-arm custom GitHub Action instead. In this case, the upgrade is not as direct. We suggest that
instead you re-create your config repository to get an updated setup using the new automation
The initial combos work has landed! The amazing okke-formsma has once again delivered another powerful feature for ZMK. Combos are "position based", and are configured in a new toplevel node next to they keymap node in user's keymap files.
An example, that would send the ESC keycode when pressing both the first and second positions on your keyboard:
There has been lots of work to get display support complete enough for use by end users. Although not quite ready for prime time, it is incredibly close, and we are looking forward to having the last few items completed and the feature documented!
petejohanson added idle blanking for displays, which ensures they will go blank, and into low power mode, after a short period of inactivity from the user. This ensures we avoid burn-in for OLEDs, and helps improve battery life.
petejohanson implemented the first two complete, dynamic "widgets" for the displays for ZMK, adding a small battery indicator, which includes charging status, and a small output indicator, showing the currently active output (USB or BLE). When using BLE, the indicator also shows the active profile slot, as well as if the profile slot is open, awaiting connection from the paired host, or is actively connected to the host for that profile slot.
mcrosson has contributed the next display widget, showing the highest active layer in the keymap. petejohanson then added a small follow up to allow layers in keymaps to add a label property to each layer, e.g. label = "Nav"; and have that label be displayed in the widget instead of the numeric layer number.
innovaker is at it again with some crucial core fixes, helping prepare and test the upgrade of ZMK to Zephyr 2.4. The updated Zephyr release brings with it some key BLE stability fixes, as well as various other core improvements that improve ZMK. This was a huge undertaking!
petejohanson was heads down diagnosing and fixing a deadlock issue on BLE that was frustrating and plaguing many users. After finally pinpointing the underlying root cause, he developed a fix and roped in many testers on Discord to help stress test things before merging.
Since it's inception, quite a few users have inquired whether they could sponsor any of the contributors involved in ZMK. Although we are not intending to directly fund any individual contributors for their work on ZMK, there is good that can come from folks sponsoring ZMK.
You can see the full discussion on #497, but some items that are being considered with sponsorship funds:
Hiring a designer to complete the logo/mascot work.
Creating stickers to send as thank-yous to first time contributors.
innovakercompletely overhauled the set of available codes for keymaps, and simultaneously has created
beautifuldocumentation to help users visualize the codes, and also understand if they are supported on their particular operating system.
This also laid the foundation for the other keymap related changes that are now available.
okke-formsma added the ability to apply modifiers to a code, e.g.:
which sends Control + c when pressed. This feature is often used on smaller keyboards to achieve "shifted keycodes", e.g. LS(N1) to send a !.
To make this easier, in addition to all the normal codes, we now have defines for common shifted codes, e.g. EXCL for !, AT for @, etc.
To learn more, check out the Modifiers documentation.
In previous versions of ZMK, users needed to be careful to select between the &kp and &cp behaviors in their keymaps, depending on
whether the particular keycode they wanted to send was in the "HID consumer page" or not. Forcing users to understand the difference and get
this right was awkward and error prone.
petejohanson and innovaker have reduced this complexity. Users can now simply use &kp with all available codes and ZMK will
handle sending the right events to the connected host.
Nicell added the necessary driver and core code to send BLE battery level notifications to hosts that support displaying them. Testing seems to show this works with Windows and GNOME, but macOS does not display the battery info.
petejohanson has contributed the initial deep sleep support to ZMK. This work also
included some automatic power savings by switching to PORT events on the nRF52 chips, which reduces the idle power draw, even without deep sleep. Deep sleep is currently not turned on by default, but will be soon.
joelspadin added output selection to allow selecting whether to send output over USB or BLE if both are connected. This should now help avoid having "double keypresses" when your keyboard is plugged into a host.
Nicell has already blogged about this, but for those that missed it, a major, and incredibly difficult to pin down bug involving corruption of the bootloader on devices using the Adafruit nRF52 bootloader has been fixed by Nicell. If you've encountered this bug, flashing the latest firmware should prevent it from reoccurring. Unfortunately, due to the nature of this fix, you will need to re-pair your keyboard with your hosts, as the fix involves changing where settings are stored in the flash of the controller.
idan contributed VSCode devcontainer integration to make it easier for developers to build and develop ZMK without having to do complicated local toolchain setup and configuration. This also opens up some amazing future flexibility for things like GitHub Codespaces.
There's some follow up tweaks necessary for better supporting using this with user config repositories, which will be available soon.
There has been an amazing amount of testing from various users as we develop new features. In particular, we'd like to give a shout out to tominabox1 who has been tireless in providing detailed and thorough testing of
changes as they are being developed.
Recently I was able to fix the "stuck in the bootloader" issue in
#322 that had been plaguing us
for quite some time. I want to go over what the issue was, how the issue was
diagnosed, and how it was fixed.
What exactly is the "stuck in the bootloader" issue? Seemingly randomly, users'
keyboards would suddenly stop working and when they would reset their keyboard
they would get put into the bootloader instead of back into the firmware. This
would require the user to re-flash the firmware again to get into the firmware.
That wouldn't be so bad except for the fact that once this occurs, every reset
would require the user to re-flash the firmware again. The only way to really
fix this issue was to re-flash the bootloader itself, which is a huge pain.
Going into this, all we knew was that this issue was most likely introduced
somewhere in the #133, which
added Bluetooth profile management. We've had quite a few attempts at trying to
recreate the issue, but we never were able to get it to happen consistently.
This issue had been happening sporadically for the past month, and I finally
decided to dig in to see what was going on. We started in the Discord and
discussed what was common between all of the people who have experienced this
issue. Everyone who had this issue reported that they did quite a bit of profile
switching. This lined up with the possible connection to the Bluetooth profile
management pull request.
I had a hunch that this was related to the settings system. The settings system
is used by profile Bluetooth switching, and the settings system works directly
with the system flash. Based on this hunch, I tried spamming the RGB underglow
cycle behavior on my main keyboard. Sure enough after a couple minutes, I got
stuck in the bootloader. I was even able to reproduce it again.
This was an important discovery for two reasons. First, I was able to recreate
the issue consistently, which meant I could set up logging and more closely
monitor what the board was doing. Second, this more or less proved that it was
specifically the settings system at fault. Both Bluetooth profile switching and
RGB underglow cycling trigger it, and the one common piece is they save their
state to settings.
To understand what's going wrong, we first need to understand how the settings
system works. Here's a diagram to explain the flash space that the settings
system holds for our nRF52840 based boards (nice!nano, nRFMicro, BlueMicro).
The settings flash space lives at the end of the flash of the chip. In this case
it starts at 0xF8000 and is 0x8000 bytes long, which is 32KB in more
comprehensible units. Then due to the chip's architecture, this flash space is
broken into pages, which are 0x1000 bytes in size (4KB).
The backend that carries out the settings save and read operation in ZMK is
called NVS. NVS calls these pages sectors. Due to how flash works, you can't
write to the same bytes multiple times without erasing them first, and to erase
bytes, you need to erase the entire sector of flash. This means when NVS writes
to the settings flash if there's no erased space available for the new value, it
will need to erase a sector.
So first I enabled logging of the NVS module by adding
CONFIG_NVS_LOG_LEVEL_DBG=y to my .conf file. I repeated the same test of
spamming RGB underglow effect cycle and the resulting logs I got were this:
[00:00:00.000,671] <inf> fs_nvs: 8 Sectors of 4096 bytes[00:00:00.000,671] <inf> fs_nvs: alloc wra: 3, f70[00:00:00.000,671] <inf> fs_nvs: data wra: 3, f40// A bunch of effect cycle spam[00:02:34.781,188] <dbg> fs_nvs: Erasing flash at fd000, len 4096// A bunch more effect cycle spam[00:06:42.219,970] <dbg> fs_nvs: Erasing flash at ff000, len 4096// A bunch more effect cycle spam// KABOOM - bootloader issue
So at start up, we can see that the 8 sectors of 4KB are found by NVS properly,
however, I wasn't sure what the second and third lines meant, but we'll get back
to that. Nonetheless the next two logs from NVS showed erasing the sector at
0xFD000 and then erasing the 0xFF000 sector.
It's really odd that the third to last sector and the last sector are erased,
and then shortly after the bootloader issue is hit. I really had no explanation
for this behavior.
At this point, I nor anyone else working on the ZMK project knew enough about
NVS to explain what was going on here. Pete
Johanson, project founder, reached out on the
Zephyr Project's Slack (ZMK is built on top of Zephyr if you weren't aware).
Justin B and Laczen assisted by first explaining that those alloc wra and
data wra logs from earlier are showing what data NVS found at startup.
More specifically, data wra should be 0 when it first starts up on a clean
flash. As we can see from my earlier logging on a clean flash I was instead
getting f40. NVS is finding data in our settings sectors when they should be
blank! We were then given the advice to double check our bootloader.
Most of the boards the contributors of ZMK use have the Adafruit nRF52
Bootloader, which allows
for extremely easy flashing by dragging and dropping .uf2 files onto the board
as a USB drive. Every bootloader takes up a portion of the flash, and in the
README explains that the first 0x26000 is reserved for the bootloader with the
nRF52840, and we've properly allocated that.
However, there isn't a full explanation of the flash allocation of the
bootloader in the README. There's a possibility that the bootloader is using
part of the same flash area we're using. I reached out on the Adafruit Discord,
and Dan Halbert pointed me towards the linker
of the nRF52840. Let's take a look.
We've found the issue! As you can see from the red bar (representing our
settings flash area), we've put the settings flash area right on top of the
Adafruit bootloader's flash space. Oops!
This also shines some light on why NVS erased 0xFD000 and 0xFF000 sectors.
It's possible there was no flash written to 0xFD000 because the bootloader
didn't use up all of that space it has, and then there possibly weren't any
bootloader settings set yet, so 0xFF000 could be used and erased by NVS too.
After erasing 0xFF000, NVS probably next erased a rather important part of the
bootloader that resulted in this issue at hand. In my opinion, we're pretty
lucky that it didn't delete an even more vital part of the bootloader. At least
we could still get to it, so that we could re-flash the bootloader easily!
Now that we've found the issue, we can pretty easily fix this. We'll need to
move the settings flash area back so that it doesn't overlap with the
bootloader. First we calculate the size of the of flash area the bootloader is using.
0x100000 (end of flash) - 0x0F4000 (start of bootloader) = 0xC000 (48KB)
So the bootloader is using the last 48KB of the flash, this means all we need to
do is shift back the settings area and code space 0xC000 bytes. We'll apply
this to all of the .dts files for the boards that were affected by this issue.
Despite the flurry of activity, there are still some serious bugs and show stoppers that potential ZMK users should be aware of:
Bluetooth Related - There are several key bugs and fixes needed, including one complete show stopper:
Fully working split wireless is not working. In particular, both split halves can properly pair, but once they do so, pairing with the _central_ host will not work. Workaround: You can currently have both halves pair, and use USB on the central side (Left side right now for Kyria, Corne, Lily58) and receive HID events over USB. - Fixed in 8b61beb.
BT bond information is not currently stored to the devices, so after powering off or restarting your device, users need to re-pair
USB - There is one important USB related bug which is a showstopper:
The Zephyr USB stack does not have a built in queue for endpoint data being written. As a result, HID events sent by ZMK are sometimes dropped, or lost. - Fixed by careyk007 in #93.
There's plenty of places to go next! To help keep folks in the loop for what's next, I've created a Core Functionality project/kanban board in GitHub, where users should be able to get some visibility into items being focused on.
Of course, at the top of that list currently is the above bugs/showstoppers, and then from there, I hope to:
Work on power management.
Improve our documentation on various aspects of the system, mostly around:
End user documentation for understanding how to use ZMK, better installation docs, etc.
Developer focused documentation, so interested contributors can start building out more behaviors and ZMK functionality.
Implement true "hold" detection, useful for several behaviors such as Mod-Tap and Layer-Tap.
Hopefully acquire a proper and official USB Product ID for use for the project.