+++
title = "Rust on the Ferris Sweep"
date = 2025-03-10T17:28:15+01:00
draft = true
[cover]
image = "keyboard-with-rust"
+++

The other day, I stumbled upon [RMK](https://github.com/haobogu/rmk),
a keyboard firmware written in Rust.
Given that my [Ferris Sweep](../ferris-sweep-keyboard/) has a Ferris the crab logo on the silkscreen,
it felt only fitting that I flash it with RMK.

However, RMK is a much newer project than [QMK](https://qmk.fm/).
QMK provides premade configurations for almost keyboard out there (or at least, every keyboard that allows flashing your own firmware),
allowing you to build someone else's design and get straight to designing a keymap, without having to fuss about matrices or pin mappings.
RMK has no such definitions, just documentation of how to write them and a few example projects,
making this journey to put it on my Ferris Sweep interesting enough to write about.

## Configuring the firmware

Unlike QMK, you don't clone the entire RMK repo, rather you make your own repo,
which uses Rusts package manager, `cargo`, to depend on RMK.
Setup of this repo is aided by the `rmkit` command line tool.

I started by installing a few tools via cargo, running

```
cargo install rmkit flip-link elf2uf2-rs probe-rs-tools
```

Then I ran `rmkit init` and answered a few questions about my keyboard, which generated a template for me to further modify.
I modified `.cargo/config.toml` to use `elf2uf2-rs` as documented in the templates README, as I cant exactly set up a debug link on an already assembled keyboard.
The docs say I need to modify the `memory.x` file for my microcontroller, but I found the `rmkit` tool had already set it with the right contents.

Then came the hard work, configuring `keyboard.toml`.
I followed [the docs](https://haobogu.github.io/rmk/keyboard_configuration.html),
carefully started porting my [Ferris Sweep layout](https://github.com/gabevenberg/qmk_firmware/blob/personal/keyboards/ferris/keymaps/almost_default/keymap.json) to the `keyboard.toml`.

First off, for the keyboard metadata, I simply took from [QMK's Ferris Sweep info.json](https://github.com/gabevenberg/qmk_firmware/blob/personal/keyboards/ferris/sweep/info.json).

Then for the tricky part. We have to define our pin mappings using the peripheral names as [defined by embassy](https://docs.embassy.dev/embassy-rp/git/rp2040/struct.Peripherals.html)
This is hard because the QMK mappings given in the [keyboard.json](https://github.com/qmk/qmk_firmware/blob/master/keyboards/ferris/sweep/keyboard.json)
are for the Aurdino pro-micro pin names,
and I wasn't able to figure out what magic QMK does to convert those into the RP2040 [Elite-pi](https://keeb.io/products/elite-pi-usb-c-pro-micro-replacement-rp2040) equivalents.
In the end, I just cloned the Ferris sweep [repo](https://github.com/davidphilipbarr/Sweep) and opened the files in Kicad,
cross referencing the schematic with the elite-pis [usage guide](https://docs.keeb.io/elite-pi-guide)
One stumbling block is that the Ferris sweep ran out of pins to use full-duplex UART, and therefore only uses half-duplex.
To solve this, RMK supports (only on the RP2040) a PIO driver for half duplex, gated behind a crate feature in `cargo.toml`.
With it, we can set our Tx and Rx pins to the same pin.
In order to do this, however, I had to set the `rmk` dependency to a direct link to the Github repo,
because as of the time of writing, the `rp2040_pio` feature had not made it into a release.

After the painstaking process of tracing all the pins and putting them in the matrix, we can define the keymap.
My [keymap](https://github.com/gabevenberg/qmk_firmware/blob/personal/keyboards/ferris/keymaps/almost_default/keymap.json)
is a bit complex, so it took some time to port.
It was mostly tedium rather than anything truly head scratching, however.

## `vial.json`

Next, and the most complicated step, was creating a `vial.json`.
The JSON file [provided by the via project](https://github.com/the-via/keyboards/blob/master/src/ferris/sweep/sweep.json) was wrong,
as it laid out the keyboard as a 8x5, rather than a 4x10 that I had done in RMK.
So I had to take that file, load it into the [keyboard layout editor](https://www.keyboard-layout-editor.com/),
and follow [vials guide](https://get.vial.today/docs/porting-to-via.html) to remake the `vial.json` in the right layout.

## Flashing and troubleshooting

Finally, I could flash my keyboard.
I flashed it, and... It didn't work.
The left hand side, the one plugged into USB, worked fine, all keys worked.
But the right hand side, connected to the main by TRRS, did nothing.
A day or 2 of investigation later revealed that the half-duplex serial implementation
only used the RP2040's internal pull-up resistors, which for my keyboard and TRRS cable,
were insufficient for a baud rate of 115200.
This was (temporarily) fixed by setting a lower baud rate,
but for the long term, I've made a PR mirroring [QMKs solution](https://github.com/qmk/qmk_firmware/blob/6d0e5728aa61b442885d48caf49d29e5c60e8197/platforms/chibios/drivers/vendor/RP/RP2040/serial_vendor.c#L133) to this problem. (Ill update this article when it gets merged.)

But, after all that, I had a Ferris Sweep running Rust,
just as the silkscreen demands.
Granted, until the PR gets merged and a new release is cut,
its running a modified version of RMK from my own repo, but Rust is Rust.

The final firmware repo is [here](https://github.com/gabevenberg/ferris-sweep-rmk),
if you want to use it for your own RP2040, wired Ferris Sweep,
or just want an example to help you along.

## Using RMK.

I then proceeded to use RMK for a few tasks, including writing this article.
It did not feel the exact same as QMK,
I think some of the timings on tap-hold and debouncing might be different,
but, apart from a few repeated keys (which should be fixed once configurable debouncing [lands](https://github.com/HaoboGu/rmk/issues/289),
it was a very serviceable keyboard firmware.
The next release is focusing on input devices,
so in the future RMK will also support keyboards with encoders and pointing devices like trackballs and touchpads.

## Final thoughts

Due to the difficulty I had in setting up RMK,
I would not recommend it to anyone who is not already familiar with configuring another keyboard firmware,
and you should have at least some knowledge of microcontrollers, such as how to figure out your keyboards pinout.
However, if you are interested and have some prior experience, it has some really cool ideas,
such as the use of an async runtime in a microcontroller (thanks to embassy),
and compile time parsing of its `keyboard.toml` file with a proc-macro to generate code for the firmware without forcing the user to write Rust.
It is also a great example of a complex Rust codebase using Embassy.

As for myself, I plan on continuing to use RMK for my Ferris Sweep,
and will probably use it for any other keyboard I build that it supports.
I also plan on continuing to contribute to the project, fixing issues as I find them.
