rewrote lare parts of the RMK on ferris sweep article.
This commit is contained in:
		
							parent
							
								
									487b164e43
								
							
						
					
					
						commit
						d793f724b6
					
				
					 1 changed files with 69 additions and 37 deletions
				
			
		| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
+++
 | 
			
		||||
title = "Rust on the Ferris Sweep"
 | 
			
		||||
date = 2025-03-10T17:28:15+01:00
 | 
			
		||||
date = 2025-03-25T17:28:15+01:00
 | 
			
		||||
draft = true
 | 
			
		||||
[cover]
 | 
			
		||||
image = "keyboard-with-rust"
 | 
			
		||||
| 
						 | 
				
			
			@ -28,67 +28,99 @@ which made the journey to put it on my Ferris Sweep interesting enough to write
 | 
			
		|||
 | 
			
		||||
## 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
 | 
			
		||||
Now, the setup between RMK and QMK is a bit different.
 | 
			
		||||
In QMK, you clone the entire QMK repo, make any keymap modifications you want,
 | 
			
		||||
and compile.
 | 
			
		||||
RMK, being written in a language with a decent package manager,
 | 
			
		||||
just has you make a repo from a small template that depends on the RMK crate.
 | 
			
		||||
RMK provides a tool, `rmkit`, to help you setup this repo.
 | 
			
		||||
 | 
			
		||||
I started by installing `rmkit` and a few embedded 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 I ran `rmkit init` and answered a few questions about my keyboard,
 | 
			
		||||
which generated my initial template.
 | 
			
		||||
From there, I modified `.cargo/config.toml` to use `elf2uf2-rs`,
 | 
			
		||||
as my keyboard does not have an exposed debug header, which would be needed for `probe-rs`.
 | 
			
		||||
The docs mentioned I may have to modify a file called `memory.x`,
 | 
			
		||||
which defines the microcontrollers memory map,
 | 
			
		||||
but I found the template already had the correct memory map for the RP2040.
 | 
			
		||||
 | 
			
		||||
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`.
 | 
			
		||||
### `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).
 | 
			
		||||
That was the easy part done.
 | 
			
		||||
The next step was to configure a file called `keyboard.toml`.
 | 
			
		||||
This file is used by RMK to configure everything from the type of microcontroller we use,
 | 
			
		||||
to defining which pins on the microcontroller correspond to which keys,
 | 
			
		||||
and even configuring the initial keymap,
 | 
			
		||||
all at compile time.
 | 
			
		||||
Similar to QMK, RMK does provide you with the option to configure your keyboard using Rust code directly,
 | 
			
		||||
but I wasn't doing anything fancy enough to justify that.
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
The first part of the `keyboard.toml` contains some basic metadata about your keyboard,
 | 
			
		||||
like its name, USB ID, and what microcontroller it uses.
 | 
			
		||||
I lifted all this info from [QMK's Ferris Sweep info.json](https://github.com/gabevenberg/qmk_firmware/blob/personal/keyboards/ferris/sweep/info.json) for consistencies sake.
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
Then I had to configure the pin mappings, defining which pin correspond to each key (in the Ferris Sweeps case, as it is a direct wire, where each pin corresponds to exactly 1 key),
 | 
			
		||||
or defining which pins correspond to rows and columns of the keyboard matrix (in the case of most larger keyboards.)
 | 
			
		||||
This proved more difficult than expected,
 | 
			
		||||
as I planned to take the pin mappings from QMKs pin mapping configuration file,
 | 
			
		||||
the [`keyboard.json`](https://github.com/gabevenberg/qmk_firmware/blob/personal/keyboards/ferris/sweep/info.json).
 | 
			
		||||
However, it was unusable, as the `keyboard.json` gives pin mappings for a different microcontroller,
 | 
			
		||||
the ATmega32U4, specifically for the Aurdino Pro-Micro board.
 | 
			
		||||
QMK does some black magic at compile time in order to rewrite these pin mappings to their RP2040 equivalents,
 | 
			
		||||
but I was not able to figure out said magic in order to do the same by hand.
 | 
			
		||||
In the end, I cloned the [repo](https://github.com/davidphilipbarr/Sweep) for the Ferris Sweep itself and looked at the PCB design in Kicad,
 | 
			
		||||
Tracing where each pin went and cross referencing with the elite-pi (my specific RP2040 board) [usage guide](https://docs.keeb.io/elite-pi-guide) to get the pin number.
 | 
			
		||||
 | 
			
		||||
## `vial.json`
 | 
			
		||||
Another thing I had to do was define how the 2 microcontrollers in each half of the keyboard communicate with each other,
 | 
			
		||||
and define the pins that are used in that communication.
 | 
			
		||||
Due to being a 'direct wire' design,
 | 
			
		||||
the Ferris Sweep only has one IO pin on each micro that's not dedicated to a key.
 | 
			
		||||
This means it has to use whats called 'half-duplex' UART,
 | 
			
		||||
where a single wire is used for 2 way communication.
 | 
			
		||||
Luckily, RMK supports this mode of communication through the RP2040's [PIO](https://www.raspberrypi.com/news/what-is-pio/) feature.
 | 
			
		||||
Unfortunately, this ability to use half-duplex over PIO is only on RMK's master branch,
 | 
			
		||||
and has not made it into a stable release as of this writing.
 | 
			
		||||
This simply means I had to do some fiddling in the `Cargo.toml` file to instruct cargo to fetch the latest commit from git, rather than getting the latest published version of the package.
 | 
			
		||||
 | 
			
		||||
Next, and the most complicated step, was creating a `vial.json`.
 | 
			
		||||
After the *painful* process of tracing each pin and defining the pin mappings,
 | 
			
		||||
we can define the keymap.
 | 
			
		||||
My [keymap](https://github.com/gabevenberg/qmk_firmware/blob/personal/keyboards/ferris/keymaps/almost_default/keymap.json) is not especially fancy,
 | 
			
		||||
other than its extensive use of layers and tap-hold Keybinds.
 | 
			
		||||
(keybinds that do a different thing on being held down than they do on tapping them)
 | 
			
		||||
All the features I use are [well documented](https://haobogu.github.io/rmk/keyboard_configuration.html#layout) by RMK,
 | 
			
		||||
so while porting the keymap was tedious, it was not especially difficult or noteworthly.
 | 
			
		||||
 | 
			
		||||
### `vial.json`
 | 
			
		||||
 | 
			
		||||
The next and most complicated step, was creating a `vial.json`.
 | 
			
		||||
This file is used by the [VIAL](https://get.vial.today/) software to allow you to remap your keyboard without re-flashing the microcontrollers,
 | 
			
		||||
and for 'reasons', the keyboard will fail to boot if your `vial.json` does not match your keymap.
 | 
			
		||||
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.
 | 
			
		||||
Ill be honest, I still don't fully understand what I was doing, I just messed around until it worked.
 | 
			
		||||
 | 
			
		||||
## Flashing and troubleshooting
 | 
			
		||||
## Flashing and debugging
 | 
			
		||||
 | 
			
		||||
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,
 | 
			
		||||
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 in the RMK source code,
 | 
			
		||||
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.
 | 
			
		||||
its running a modified version of RMK from my own fork, but its still 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,
 | 
			
		||||
| 
						 | 
				
			
			@ -98,7 +130,7 @@ or just want an example to help you along.
 | 
			
		|||
 | 
			
		||||
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,
 | 
			
		||||
I think some of the timings on tap-hold and key 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.
 | 
			
		||||
Obviously, being a much newer project than QMK,
 | 
			
		||||
| 
						 | 
				
			
			@ -119,4 +151,4 @@ 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.
 | 
			
		||||
I also plan on continuing sto contribute to the project, fixing issues as I find them.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue