Intro

I've been using the Lily58 for quite a while now, specifically the Pro version that offers support for both Kailh hot swap sockets.

That's how I ended up with the current version of my Lily, Kailh Choc Pro Reds with MBK keycaps. It was a long (and expensive) journey, but I was confident that this was my endgame, no need to buy the next fancy MX switch or get the 10th GMK recolor.

I was happy with my plain low profile keyboard.

At least until I saw the nice!nano, it's a Pro Micro drop-in-replacement to make your keyboard wireless. Why wouldn't I want a wireless Lily58?

ZMK

I've been using QMK for all my keyboards until now, it's the most mature and feature rich firmware for custom keyboards.

Setting up the QMK dev environment isn't as straight forward, but the online configurator for QMK works great for the basic features. This tool will spit out your finished firmware which goes onto the MCU using QMK Toolbox, no need for a dev environment.

There is an issue with QMK when going wireless, and that's the Nordic NRF52 chip series inside most popular Bluetooth MCUs. The licensing of those chips are not compatible with QMKs licensing. That's why we have ZMK and BlueMicro and more or less well maintained QMK forks that add NRF52 support.

Why ZMK and not BlueMicro? I was taking a look at their feature comparison and the activity on their GitHub repos.
ZMK might lack some features, most significantly macros, but it makes it up with multi-device connections and battery usage.
The nice!nano docs have a small comparison as well, disabling the onboard LEDs (which face downwards on the Lily anyway) will save battery. And I have no doubt that missing features will find their way into ZMK fairly quickly with its active community.

Setting up ZMK

ZMKs getting started guide is pretty detailed, and quick if you're familiar with Git(hub).

  • Create an empty repo on GitHub
  • Execute their setup script
  • Select your MCU and keyboard, then fill out the repo questions

After finishing the setup the script will commit your local repo to GitHub where all the building happens within GitHub Actions.

Once the action is finished, you'll need to download and unzip the artifact. The artifact includes two uf2 files, one for the left side and one for the right.

Preparing the MCU

It's always a good idea to make sure the controller is working before you solder anything to it.

Plug the nice!nano into your PC, and it'll be recognized as a storage device, copy the uf2 file into it and once that's done the MCU will restart automatically. Done.
After flashing both sides you'll want to connect them and see if the Bluetooth connection is working, to do this short the ground (GND) and reset (RST) pins on both MCUs in a timely manner.
Both halves will see each other, connect and appear as a single Bluetooth keyboard. In the case of the Lily I saw a board called Lily left which my phone recognized as a keyboard and connected to.

Flashing the nice!nano is significantly easier than Pro Micros or Elite-C's with QMK, QMK toolbox rarely worked instantly for me after putting the MCU in reset mode.
Using Ubuntu to flash custom keymaps with advanced features as always a daunting task after googling some efuse error from avrdude because an Ubuntu modem-service was running and other weird issues when running the same keymap on Pro Micros and Elite-Cs.

In my opinion the ZMK process was a lot easier, but it requires the user to be familiar with Git whereas QMK with the toolbox and it's online configurator work reliable enough without having to dive into a command line.

Starting from scratch

While I own two Lilys (one at work and one at home), I didn't bother socketing the Pro Micros. Meaning desoldering them was going to be a pain, so I've decided to buy a new PCB and start from scratch.
This also allows me to get all keys working again, as both of my Lilys only have 57 keys after gluing (and eventually removing) a rotary encoder to them.

SMD soldering

We're going to keep this brief, soldering Surface Mounted Devices sounds scary, but it's actually fairly simple if you got the right technique.

The way I do it is by doing the following:

  • presolder one of the contact patches
  • place the diode
  • reheat the presoldered patch while making sure the diode is still properly placed
  • solder the other contact patch
SMD soldering image
Presolder one side, reflow while pressing the diode down, solder the other side

Presoldering one side means you don't have to worry about applying solder, you only need your soldering iron and tweezers to position the diode.
Not presoldering the second pad makes it easier to place the diode because you can focus on the one side without having to reflow the other side to align everything properly.

Kailh sockets

We'll solder them like SMD components: presolder one side, reflow and then the other pad

MCU sockets

I don't want to repeat the same mistake for a 3rd time, that's why this build will have a socketed nice!nano.

But the process of socketing your MCU is fairly straight forward, inserting the pins is a bit cumbersome since those pins bend fairly quickly.

  • Solder the MillMax hot swap socket
  • Carefully push the included pins into the socket
  • Place your controller on top and solder it down

In the case of the nice!nano you'll see that there are not enough pins to slot into every port on the MCU. That's because the ports right next to the USB C port, B+ and B-, are for the battery. Align the MCU at the bottom and keep the top pins for the battery.
You'll want to mount the MCU facing down so the raw, ground, reset and VCC pins are on the left.

Battery

I couldn't get my hands on small 3.7V 100mAh batteries, so I went with something larger that I could mount above the MCU. I went with a really cheap 1100mAh battery, 4 of them costing me 22€. The downside is the size, at 47 x 28 x 7.8mm it's not even close to fitting underneath the MCU.
That's why I mounted it on top, using the acrylic OLED cover and some large standoffs to keep it in place, and wrapping the cover in carbon wrap to hide it a bit.

Then solder the battery to the nice!nano, the red positive wire goes on the B+ pin and the black negative one to B-. Be quick and careful soldering the batteries down, you don't want to heat the battery too much.

Troubleshooting

After soldering everything together, you'll want to make sure everything is working properly. Open a keytester and test every key. If one key isn't registering, check if the hot swap sockets make contact with the PCB and reflow the solder if necessary. You can bypass the hot swap socket by shorting the contact pads on the top of the keyboard with a pair of tweezers.

If your key is still not registering, it's probably the diode, either by being oriented wrong or because it's dead or not soldered properly.

Connecting devices

ZMK has profiles for each bluetooth connection, and each profile is initially empty. Empty profiles pair with every device that want to connect to it, and once a device is connected it'll be saved in that profile.

Swapping profile in done with the BT_SEL ? key, where ? is a zero based number for the profile. The default keymaps for the Lily has those keys on lower 1-5 on the numrow to change the bluetooth profile.

Resetting a profile (which means emptying it and forgetting the old device) is done with the BT_CLR key, which clears the current profile and allows new devices to connect in this profile. The default keymap has this key on lower + ESC

ZMK will keep all the profiles connected if the device is available to allow instant switching between devices. This means I can easily swap between my PC, Raspberry Pi and iPad.

Custom keymap

This is pretty straight forward, when following ZMKs installation you end up with a zmk-config repo. This repo isn't a complete fork of ZMK, it contains only the config of your MCU and keyboard with your keymap.

The GitHub Action will pull an up-to-date image from ZMK and run it against your config. All you need to do is edit your configs, commit the changes to your repo and download the artifact from the GitHub Action.

Like already mentioned in the Preparing the MCU-section, resetting the MCU after flashing the firmware will put it in pairing mode to connect both halves. To reflash a firmware, you'll either have to have a dedicated keybind on your keymap, or by hitting the reset button twice.
That'll put the nice!nano into flashing mode and display it once again as a storage device where you drop the uf2 file.

Layer Tap

All my layers are on the thumbcluster, QMK has a feature called layer-tap which momentarily toggles a layer when held and sends a keycode when tapped.

Luckily I can keep this part as ZMK has its own implementation of it, &lt 1 SPACE will send SPACE when tapped and toggle layer 1 when held.

Leader Key

The leader key is a feature that I used quite a bit, Thomas Baart wrote a great article about the feature. The TL;DR is: you can define sequences that execute specific macro's using the leader key.

I used it for special characters like ä and Ä, using the US keyboard in my OS means I get the ANSI keybindings which I prefer to the ISO ones where brackets and special chars are on weird keys. But changing the input language removes special chars, and I need those chars from time to time.

With the leader key feature I added them back in, hitting LEADER -> a would instruct QMK to send ALT + 132 which is ä. Hitting LEADER -> a -> a would send the uppercase Ä, LEADER -> e becomes and the list goes on.

ZMK currently has no leader key support, there is an issue tracking this already. For now, I'm back to using shortcuts to change the input language and remembering that ' in ANSI is ä in ISO-DE.

Macros

Another issue with ZMK, there is an open pull request for macros. Until this PR is merged, we need to build the keymap using Beta Testing.

With beta testing, we're not only using the latest ZMK build, but we also apply the pull request. That way we can test features like macros before they get merged.

As of now macros are basic sequences, defining your own keyup & keydown sequences isn't possible as of now. A more modular approach is a planned feature for macros v2.

Final Thoughts

I initially wasn't quite sure if this build will end up working, Bluetooth always sounded complex when two halves need to talk to each other.

But after this build I'm happy with giving it a shot, setting up the nice!nanos with ZMK was straightforward although some QMK features (namely macros and leader key) are missing right now.
I still can't get rid of the cables completely, but charging shouldn't occur that often with a 1100mAh battery when 100mAh last a week.