Skip to main content



Controls your vim cursor with a joystick. Yes, it’s the same picture as the Awful Mouse, because I re-used the circuit. I made this project as a joke, “A mouse for nano users” but also to dig around the documentation. On the Awful Mouse project I mostly just copied from the examples, not really understanding what stuff does. With this one, I challenged myself to not look at any examples (they where removed from the rp-hal repo anyways) and try instead to do it all on my own. I also used an embedded debugger for the first time, which was an awesome experience.

The first thing that had me digging through documentation were the imports. Most of the stuff was easy enough, it was documentated on the rp_pico docs. But one import was especially confusing, and it isn’t even about the HID part, it’s about reading the analog pins:

// adds .read() to adc pins
use embedded_hal::adc::OneShot;

It’s just confusing how a method from the generic embedded HAL is being used on a struct from the rp2040_hal which is being imported second hand by the rp_pico crate. If you couldn’t read that sentence, don’t worry, I can’t wrap my head around it either.

I don’t fully understand the USB interrupts and device drivers, and most of the usb stuff is done trough unsafe too. But USB is a massive rabbit hole I’m not willing to go into yet. The USB HID documentation is 432 pages alone, so I can’t begin to imagine how extense the whole usb device spec is. Talking about the USB HID docs, I had to do some digging there. Since the usbd_hid crate is the same as the last time, it uses a really similar structure as the MouseReport:

KeyboardReport {
    modifier: u8,
    reserved: u8,
    leds: u8,
    keycodes: [u8; 6],

And the codes for the keycodes array are documented on the HID Usage Tables for USB (page 88) Interesting how it holds only 6 values, I know that’s how USB keyboards are supposed to work, but it got me wondering about how n-key rollover is supposed to work. I also had to look for the device class codes:

usb_dev = UsbDeviceBuilder::new(bus_ref, UsbVidPid(0x16c0, 0x27da))

Which I got from here:

What was an absolute joy was working with the debugger. I had never worked with a debug probe for embedded devices, and it left me so amazed. I know these have existed for a long time, but I still couldn’t contain my excitement when working with one. Getting information from a microcontroller is always annoying, You have to open a serial port or get some kind of external display… It always adds extra annoying code. But with a debug probe it’s just

info!("Joystick reading: X = {}, Y = {}", x, y);

No need to open any serial port. No need for any extra code. Just use the macro provided by defmt::info. The output on the terminal looks like this:

**(HOST) INFO** flashing program (6 pages / 24.00 KiB)
**(HOST) INFO** success!
**INFO** Joystick reading: X = 2048, Y = 2048
└─ vimmouse::__cortex_m_rt_main @ src/

It also reports on any crashes, panics, errors, connection issues… Having this kind of information is incredible for me, because I’m used to working with a ‘black box’.

Check out the project on gitlab: