Open-source project
torvalds/ScrollWheel avatar
torvalds/ScrollWheel

torvalds/ScrollWheel is an RP2350 knob you have to rewire first

Minimalist RP2350 magnetic sensor scroll wheel toy project

385 stars14 forksCGPL-2.0

At a glance

What is it?
Linus Torvalds' ScrollWheel turns an AS5600 magnetic angle sensor plus a few switches into a USB HID volume knob or scroll wheel. The firmware is four Makefile targets, two PIO programs and a warning that the shipped pin assignments will not match your board.
Who is it for?
This suits someone who already owns a Pimoroni Tiny 2350 or a similar RP2350 board, wants a hardware volume knob, and is prepared to move pin assignments before the first build. It does not suit anyone who expects a finished peripheral: there are no releases, the shipped board file points i2c0 at GPIO 4 and 5 while the recommended Pimoroni layout uses 20 and 21, and the LED path assumes a WS2812 that the Pimoroni does not have.
Can I use it commercially?
Yes, with conditions. GPL-2.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 164 days ago.
What is it written in?
Mainly C, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 13, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • torvalds/ScrollWheel385 ★ · +9
Star history
Repository2026-09-212026-11-13
torvalds/ScrollWheel376385

9 Stars in the last 53 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

An AS5600 for rotation, switches for the clicks

The hardware premise is small: an AS5600 magnetic angle sensor for rotation and a couple of switches for clicks. The switches are wired in the simplest way that works, with one side on a GPIO and the other side on ground. The intended result is a hardware knob, either a volume control or a scroll wheel, and the author is explicit that the same base can be taken somewhere else instead.

The typical board is a Pimoroni Tiny 2350, picked because it has a Qwiic connector for i2c, and it needs one to four GPIOs left free for the switches. Nothing in the firmware checks that those pins exist on the board you actually have, which is why the GPIO story takes up as much of the README as the feature list does.

Both modes produce a binary still called blink

MODE is the only thing that separates the two behaviours, and it is passed when the build directory is configured rather than when code is compiled:

code
scrollwheel:
	cmake -B build -DMODE=SCROLLWHEEL
	cmake --build build --target blink

volume:
	cmake -B build -DMODE=VOLUME
	cmake --build build --target blink

Both targets configure into the same `build` directory and both build the same CMake target named `blink`, so the artifact is `build/blink.elf` either way. That name is a leftover from a blinking sample, and it matters because the plain build target never runs a configure step:

code
build:
	cmake --build build --target blink

Run that on a clean checkout and there is no `build` directory behind it, which is the fastest way to meet a cmake error about a missing build tree.

prep pulls the submodules and pins the platform

The tree depends on pico-sdk and tinyusb as git submodules, and a plain clone leaves both of those directories empty:

code
prep:
	git submodule update --recursive --init
	cmake -DPICO_PLATFORM=rp2350 -S . -B build

The platform is chosen here rather than hard-coded somewhere in the CMake files, so `-DPICO_PLATFORM=rp2350` is what turns the configure step into RP2350 firmware rather than an RP2040 build. `prep` also creates the `build` directory that every later target assumes, which is why a first run has an order: `prep`, then one of the two mode targets, then `flash`. Skipping `prep` and going straight to a mode target gets a different failure, because the submodule contents are still missing when the toolchain looks for them.

Flashing is picotool load chained to a reboot

code
flash:
	picotool load build/blink.elf && picotool reboot

`picotool` is the only flashing tool named anywhere in the Makefile, and the load is chained to the reboot with `&&`, so a failed load does not leave the board reset with whatever was there before. The rest of the flashing story is not covered here: there is no erase target, no target that puts the chip into its special boot state, and nothing that reads the board back. Whatever that sequence is, you do it by hand before this target does anything useful.

The WS2812 PIO program assumes a smart LED

The LED path is the assumption that costs the most time when it turns out wrong. There is a `ws2812.pio` program that sends pixel data, and the README states plainly that this particular setup assumes a WS2812 RGB LED, that this is only somewhat common with the Waveshare RP2350 Zero given as an example, and that it is by no means universal. Some boards have a single LED on a single GPIO instead, and then a PIO program written for a smart pixel has nothing to talk to.

The Pimoroni Tiny 2350 is the awkward case, because it does have an RGB LED and it is not a WS2812. Its three colour channels are ordinary GPIOs, 18, 19 and 20 for red, green and blue, driven individually and normally with PWM to set the level of each channel. Driving three channels by PWM is a different exercise from pushing a frame out over PIO, so the LED on the recommended board is exactly the part that does not match. The README also notes that no LED is needed for any of this, while still being useful for visual debugging.

Debounce PIO plus a USB HID device in plain C

`debounce.pio` is the second PIO program in the tree, named for the debouncing it does on the switch side. What is left in C is the plumbing around it: `blink.c` is the file the `blink` target is built from, `hid-device.c` and `hid-device.h` hold the USB HID device that lets a host treat the knob as a scroll wheel or a volume control, and `include/` holds headers.

How those two PIO state machines are wired, and what HID reports they emit, is not written down anywhere in the README, so the source files are where you read next. The same goes for the sensor: the AS5600 is named, but no register addresses, scaling or calibration values appear in the README.

The shipped board file assumes i2c0 on GPIO 4 and 5

The sensor is reached over i2c, and the pin numbers in the included board file do not match the board the README recommends. The shipped setup uses the more normal i2c0 on GPIO 4 and 5, while the Pimoroni board puts i2c0 on GPIO 20 and 21. The same caution is extended to everything else: all the other GPIO assignments are described as random based on the board.

So the honest description of this repository is a starting point rather than a finished peripheral. The README says not to expect to build and use it as-is and not to expect anything polished, calling it one of the author's early RP2350 project trials with minimal updates to bring it up to snuff again. Taken together with the WS2812 mismatch and the pin difference, that is three separate edits before the thing does anything on hardware.

No releases, no version, GPL-2.0

There is no tagged release and no version to pin, because the firmware is configured by hand with MODE and PICO_PLATFORM rather than installed as a package. The licence is GPL-2.0, which matters if the plan is to reuse this code in firmware for something you sell rather than building it for your own desk.

The last commit on the default branch landed on 2 June 2026, and the README carries the author's own framing of it as an early trial with minimal updates. The closing paragraph of that file even spells the chip as rp2354 in one sentence while the heading above it says rp2350, which is a fair measure of how little editing has gone through the text.

Editorial conclusion

This suits someone who already owns a Pimoroni Tiny 2350 or a similar RP2350 board, wants a hardware volume knob, and is prepared to move pin assignments before the first build. It does not suit anyone who expects a finished peripheral: there are no releases, the shipped board file points i2c0 at GPIO 4 and 5 while the recommended Pimoroni layout uses 20 and 21, and the LED path assumes a WS2812 that the Pimoroni does not have. Before flashing, check that MODE matches the behaviour you want, run `prep` so the pico-sdk and tinyusb submodules are populated, and read the pin numbers in your board file rather than in the README.

Frequently asked questions

Which board does torvalds/ScrollWheel expect?

The typical target is a Pimoroni Tiny 2350 with a Qwiic connector for i2c, plus one to four GPIOs wired to switches whose other side goes to ground. The Waveshare RP2350 Zero is named as an example of a board whose smart LED fits the ws2812.pio program.

What i2c pins does the torvalds/ScrollWheel board file use?

The included board file is set up for the more normal i2c0 on GPIO 4 and 5, while the Pimoroni board puts i2c0 on GPIO 20 and 21. The README describes all other GPIO assignments as random depending on the board.

How do I flash the torvalds/ScrollWheel firmware?

Run make flash, which loads build/blink.elf with picotool and then reboots with picotool reboot. The elf keeps the name blink whether MODE is VOLUME or SCROLLWHEEL, and there is no erase or boot mode target in the Makefile.

Official sources

  1. Issues
  2. License: GPL-2.0
  3. README
  4. torvalds/ScrollWheel on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/torvalds-scrollwheel.svg)](https://hysenlabs.com/projects/torvalds-scrollwheel)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/torvalds-scrollwheel.svg)](https://hysenlabs.com/projects/torvalds-scrollwheel)

Was this useful?