Carl Henriksson

Firmware, systems, web. Same problems, different layers.

MOTR is an open-source aftermarket firmware for the Polarity Works ROTR, built on upstream ZMK. It provides mouse scroll on the encoder, left, middle, and right click on the buttons, RGB underglow, and Bluetooth. Download · GitHub


The ROTR is a precision scroll knob. A high-resolution magnetic encoder, three buttons, and nothing else.

The hardware is well suited for mouse wheel input. Fine, continuous movement is exactly what a 16-bit absolute encoder is good at. Out of the box it ships with firmware that maps the encoder to keyboard keys — a reasonable default for many workflows, but one that leaves the mouse wheel use case on the table.

A keyboard key either fires or it doesn't, but an encoder with 65536 positions per revolution deserves better.

No aftermarket firmware existed in sync with current upstream ZMK, so MOTR was built to fill that gap.


The MA730 reports absolute position — it always knows where it is, not how far it has moved. ZMK's sensor path expects the opposite: relative movement from a quadrature encoder. Feeding the absolute angle directly into that path ties motion to the boot position. Hold the encoder anywhere other than where it was at power-on and it scrolls continuously, proportional to the angular distance from boot. The accumulator in ZMK's sensor pipeline keeps treating the angle as displacement to drain out.

There is no public API to reset or bypass that accumulator from driver code. The practical fix is to route encoder events around it entirely: the driver computes wrapped deltas locally and emits them as INPUT_REL_WHEEL events through Zephyr's input subsystem. A zmk,input-listener node in the board DTS routes those events into ZMK's pointing subsystem. This is the same mechanism used by ZMK trackball and touchpad drivers — it is the documented path for devices that produce pointing input rather than raw sensor data.

The MA730 driver ships as a self-contained Zephyr module alongside the config, avoiding downstream patches while the upstream encoder driver API continues to evolve.

An initial SPI read at boot seeds the baseline angle so the first poll does not compute a delta against zero. Wrap-around at the 0/65535 boundary is clamped correctly in both directions. Small opposing deltas below a configurable threshold are discarded so detent noise does not reset accumulated movement mid-scroll; only a confirmed reversal clears the accumulator.

The driver polls at 8ms. USB and Bluetooth have both been verified; scroll output, RGB underglow, and all three buttons work over both transports.