Skip to content

Firmware Autogen

QMK Firmware Generation

If you’re using the Lemon Microcontrollers, Cosmos can automatically generate your keyboard firmware. For the Wired Lemon, Cosmos will generate QMK firmware. For the Wireless Lemon, it will generate ZMK firmware. These are the most popular and feature-complete firmware for wired and wireless keyboards respectively.

Keyboards created in Cosmos need custom firmware because every keyboard firmware must be adjusted for the number of keys you chose to use, how they’re wired, how they’re mapped to letters/actions, and what extra peripherals you are using (like trackballs, encoders, etc). Typically you will adapt a configuration for QMK or ZMK to your keyboard then compile the firmware and configuration together, either by installing the toolchains yourself or using GitHub Actions to compile in the cloud.

Despite the fact that some peripherals are not yet supported (you can track the implementation status at Are we Programming yet), Cosmos is still the easiest starting point for creating firmware for your board. You can use Cosmos to to generate what it can, start typing on your keyboard, and refer to the documentation and online examples to configure the remaining peripherals. It’s like buying a car with a speaker system that still needs to be installed, versus piecing together an engine, drivetrain, chasis, etc.

Support for More Microcontrollers

Right now only the Lemon microcontrollers are supported because the VIK and flex PCB connectors ensure there is only one way to connect everything to your microcontroller pins, which greatly simplifies the generation process.

The priority right now is to make this process as dead simple as possible. There’s still a lot of work to tackle, and if you’re interested you can read the roadmap on the PeaMK repository. Only after this is completed will the focus be expanding to other microcontrollers. However, you are more than welcome to fork Cosmos and adapt the code to the microcontroller you’re using. Pull requests are always welcome :)

Key Labeling

Cosmos will by default label keys according to the QWERTY layout. These labels will be used in the firmware to assign keys. If you use a different layout or have added keys beyond the typical QWERTY setup, you can change key labels by clicking a key in the 3D view, cliking “Edit Key”, then changing the “Letter” field.

Changing the Key Letter

If you are editing the upper keys jointly, you will only be able to change the labels on the right split. The left split will use the QWERTY layout to derive its labels from the right split. If you aren’t using QWERTY, you will need to click “Edit Separately”.

Cosmos supports a few different options for labeling keys:

  • Letters (a–z, A–Z). The letter case in Cosmos doesn’t matter.
  • Numbers (0-9)
  • Function keys (F1–F12)
  • Special characters (-, :, <, ~, @, !, etc.)
  • Names of non-character keys (esc, escape, enter, tab, bspc, backspace, etc.)
  • QMK only: Verbatim keys (KC_X, QK_BOOT, MO(1), MT(MOD_LSFT, KC_UP), etc.)
  • ZMK only: Verbatim behaviors (&kp X, &bootloader, &mo 1, &mt LSFT UP)

If a key label isn’t in one of these formats, it will be replaced with the space key.

Matrix Mapping Generation

If you’ve selected a Lemon Microcontroller in Cosmos, then you’ll see instructions asking you to download PeaMK onto your microcontrollers. Right now PeaMK is a firmware that prints the matrix position for each key you press, but the plan is to make it a fully configurable keyboard firmware.

Matrix mapping in Cosmos programming interface

One by one you should press your keys in the order they are shown on screen. If you accidentally press the same key on your keyboard twice, the keys will be highlighted red. There is a undo button at the top of the interface to undo the previous assignment so you can correct the error.

Once all keys have been pressed, you’ll be presented with configuration and buttons for downloading your autogenerated keyboard firmware.

QMK (Wired) Generation

I recommend using GitHub Actions to build your firmware since it requires installing no software and therefore is much faster to set up. However, you can also choose to build the firmware locally. Once you spend the time to set up the local environment, local builds will be much faster.

When you download the firmware from Cosmos, you’ll get a zipped folder with a few directories. Here’s what you should see after unzipping and turning on hidden folders in Mac/Linux (in Windows you don’t need to turn on anything):

.github, keyboards, and qmk.json folders

The actual firmware code is within the keyboards/<your-keyboard> folder. The .github and qmk.json files are generated to help you set up GitHub Actions.

Hardware Limitations

  • For OLED displays, the display size is set to 128×32. If your display has some other size, make sure to change it in config.h.

Building

GitHub Actions Build

  1. Create a new repository on GitHub.
  2. Clone the repository, copy the files shown above (including .github) into the repository, and commit and push. If you’ve never used GitHub before, my recommendation is to use the GitHub web editor by pressing . when on the repository page or replacing the .com in the URL with .dev. You can drag and drop these folders into the file explorerer then commit & push online.
  3. On the repository in GitHub, you’ll see “Latest QMK Firmware” listed under “Releases” on the right hand side. Click the release name then download the appropriate files. The UF2 files with via in their name support VIA, whereas those without do not support VIA.

Releases tab.uf2 file to the RPI-RP2 drive. Make sure to flash both halves of the split keyboard.

How is this working?

The GitHub action is set up to use the QMK External Userspace feature. Userspaces are meant for you to be able to add new keymaps and configurations for existing keyboards. However, every keyboard generated with Cosmos is a new keyboard, so the two don’t work great together out of the box. The Actions workflow generated by Cosmos copies your keyboard configuration into the QMK repository (just as the below local build instructions describe) to sidestep this limitation.

Local Toolchain Build

Unfortunately, QMK Toolbox hasn’t been updated to support flashing newer chips like the RP2040. Your best bet to compile firmware is to either use QMK through Docker or install the QMK tools directly. If you use docker, you don’t need to worry about USB setup for flashing your microcontroller because the RP2040 uses UF2 files. Once you put the microcontroller into bootloader mode, you’ll be able to drag the UF2 file into the virtual flash drive that appears.

Once you’ve downloaded the QMK repository, make a new folder for your boards. I like to create a cosmos directory under the keyboards folder and put my firmware inside keyboards/cosmos. Unzip the firmware file Cosmos generated for you and drag the folder inside keyboards/ into the new folder you just made. The directory structure should look something like:

keyboards/cosmos
└── cosmotyl
    ├── config.h
    ├── keyboard.json
    ├── keymaps
    │   ├── default
    │   │   └── keymap.c
    │   └── via
    │       ├── keymap.c
    │       └── rules.mk
    ├── rules.mk
    └── vik...

If you placed the config somewhere other than keyboards/cosmos (i.e. you used a different folder name), make sure to change the #include statements in config.h.

If you installed QMK directly, use

qmk flash -c -kb cosmos/cosmotyl -km via -bl uf2-split-left
qmk flash -c -kb cosmos/cosmotyl -km via -bl uf2-split-right

to flash the left and right sides. If you used docker then run

util/docker_build.sh cosmos/cosmotyl:via:uf2-split-left
util/docker_build.sh cosmos/cosmotyl:via:uf2-split-right

and make sure to copy out the .uf2 file after each command.

Setting Up Via

This step is quite easy. Download the Via config from Cosmos. Then navigate to Via and click the settings gear. Make sure that “Show Design tab” is enabled.

Show Design tab setting in Via

Then open the design tab and click Load next to “Load Draft Definition”.

Load button in Via Design tab

Select the JSON file you downloaded, and if all goes well your keyboard design will now be imported into Via. You can preview it by selecting it next to “Show Keyboard Definition”.

Now plug in your keyboard, and you’ll be able to change the key assignments!

Further Modification

QMK is a very unopinionated firmware. This means you get lots of flexibility to plug into QMK’s APIs and run whatever custom code you like on your keyboard firmware. The downside is that you’re required to implement some functionality yourself.

Pointing Devices (Trackballs and Trackpads)

You will very likely need to tune the rotation angle of your trackpad so that what’s left/right for you translates to left/right on the screen.

First add one of:

  • #define POINTING_DEVICE_ROTATION_90
  • #define POINTING_DEVICE_ROTATION_180
  • #define POINTING_DEVICE_ROTATION_270

to your config.h to coarsely rotate the pointing device to the right orientation. If you need to invert the X or Y axes, use POINTING_DEVICE_INVERT_X and POINTING_DEVICE_INVERT_Y.

Use the ROTATIONAL_TRANSFORM_ANGLE variable to fine tune the offset by adjusting it +/- 127 degrees.

The PMW and Cirque drivers also have lots of configuration options. I recommend you read through the QMK Pointing Devices docs.

OLED Displays

There are no built-in OLED display programs in QMK. It’s up to you to code your display. Firmware Autogen by default uses the OLED Driver to display a logo. This driver is very low level, so it’s great at showing images, animations, and very basic text output. The OLED Driver page in QMK has a full list of examples and methods.

If you’d like a more powerful graphics API, you can switch from the OLED Driver to the Quantum Painter LVGL driver. That said, most QMK code on the internet uses the OLED Driver.

For some inspiration:

P.S. QMK turns the display off after 1 minute of inactivity to help prevent burn-in. You can tune this delay or disable it entirely by changing OLED_TIMEOUT.

ZMK (Wireless) Generation

Hardware Limitations

  • For the Cirque trackpad, only SPI communication is supported! This is how they come out of the box. Don’t listen to guides that tell you to remove R1. SPI is what you want to use because it is faster than I2C.
  • Trackballs and trackpads are only officially supported (for now) on the central side. You can use a cirque trackpad on the peripheral side by changing vik_cirque_spi in your build.yaml to vik_cirque_spi_split. There’s no such thing for the pmw3610 yet. Dual trackballs/trackpads are not yet supported either.
  • For OLED displays, the display size is hardcoded to 128×32. If your display has some other size, make sure to change it in config.h. For now, you’ll need to set up the display yourself, similar to how I do it in the VIK modules.

Building

You can either follow ZMK’s instruction to set up a Local Toolchain or use GitHub Actions to build your firmware. I recommend GitHub Actions since it requires installing no software and therefore is much faster to set up.

When you download the firmware from Cosmos, you’ll get a zipped folder with a few directories. Here’s what you should see after unzipping and turning on hidden folders in Mac/Linux (in Windows you don’t need to turn on anything):

.github, build.yaml, zephyr, boards, and config folders

GitHub Actions Build

  1. Create a new repository on GitHub.
  2. Clone the repository, copy the files shown above (including .github) into the repository, and commit and push. If you’ve never used GitHub before, my recommendation is to use the GitHub web editor by pressing . when on the repository page or replacing the .com in the URL with .dev. You can drag and drop these folders into the file explorerer then commit & push online.
  3. Click the “Actions” tab on GitHub, click the top build, and you’ll be able to download the firmware under “Artifacts” once the build completes. ZMK has a detailed guide on this.
  4. Enter bootloader mode on the microcontroller and copy the .uf2 file to the LEMONBOOT drive. Make sure to flash both halves of the split keyboard.

Local Toolchain Build

If you’re using the local toolchain, copy the contents of boards/shields (there should be a single folder named after your keyboard) into ZMK’s app/boards/shields directory. The command you’ll use to build varies based on what modules you’re using. The nice thing about GitHub actions is it automatically fetches and sets up these modules. If you’re building locally, it’s expected you can figure this stuff out on your own.

Here’s how I build a keyboard with usb logging enabled:

west build -d build/right -b cosmos_lemon_wireless -S zmk-usb-logging -- \
  -DSHIELD="cosmotyl_right" -DZMK_EXTRA_MODULES="/path/to/zmk-fingerpunch-vik"

and one with a PMW3610 trackball:

west build -d build/right -b cosmos_lemon_wireless -S zmk-usb-logging -- \
  -DSHIELD="cosmotyl_right vik_pmw3610" \
  -DZMK_EXTRA_MODULES="/path/to/zmk-pmw3610-driver;/path/to/zmk-fingerpunch-vik"

The list of shields (-DSHILED option) and snippets (-S option) required to build your keyboard firmware will be listed in the downloaded build.yaml.

You’ll find the UF2 file under build/right, which you can then copy to the microcontroller.

Those extra modules come from these repositories, which you will need to clone onto your computer:

Further Modification

ZMK has very sane defaults for most features. My philosophy for firmware autogen is to use those defaults but inform you of which options you may wish to change.

OLED Displays

Some useful options from the ZMK display docs are:

  • CONFIG_ZMK_DISPLAY_BLANK_ON_IDLE: The display turns off on idle to save power and prevent burn-in. This is generally a good thing, but you can disable it.
  • CONFIG_ZMK_WIDGET_BATTERY_STATUS_SHOW_PERCENTAGE: set this to show a battery precentage instead of just the icon.
  • Widgest: CONFIG_ZMK_WIDGET_LAYER_STATUS, CONFIG_ZMK_WIDGET_BATTERY_STATUS, and CONFIG_ZMK_WIDGET_OUTPUT_STATUS are on by default. CONFIG_ZMK_WIDGET_WPM_STATUS is off by default.