title: Trackball description: 3D printed mouse replacement with Raspberry Pi Pico and PMW3360 parent: input_devices git: https://git.xythobuz.de/thomas/Trackball github: https://github.com/xythobuz/Trackball date: 2023-02-12 comments: true --- For some years I have been using Trackballs from Logitech, first the [M570](https://amzn.to/3XhSuRO) and then the [MX Ergo](https://amzn.to/3xdp3pd). But of course we could also build our own! So because [Philipp](https://www.phschoen.de/) and I were already looking into building a keyboard with a trackball included, I decided to first get my feet wet by building a stand-alone version. It's made with a PMW3360 optical mouse sensor and a Raspberry Pi Pico. The case is 3D printed and was designed in OpenSCAD. As usual the project is released as free and open-source software / hardware. You can find everything you need to build it yourself in [the git repository](https://git.xythobuz.de/thomas/Trackball)! ### Table Of Contents * [Part Selection](trackball_v1.html#part_selection) * [3D Design](trackball_v1.html#3d_design) * [Firmware Development](trackball_v1.html#firmware_devel) * [Wiring](trackball_v1.html#wiring) * [Sensor Problems](trackball_v1.html#sensor_problems) * [First Prototype](trackball_v1.html#first_prototype) * [Improvements](trackball_v1.html#improvements) * [User Experience](trackball_v1.html#user_experience) * [License](trackball_v1.html#license) * [More Pictures](trackball_v1.html#more_pictures) ## Part Selection Before embarking on this project some decisions and orders had to be made. To control everything I decided to go with the [Raspberry Pi Pico](https://www.raspberrypi.com/products/raspberry-pi-pico/) / [RP2040](https://www.raspberrypi.com/products/rp2040/) microcontroller board. It is the hot new thing on the block and I wanted to try out both the hardware and the SDK. It's also used extensively in the DIY keyboard community and already has great support in lots of open source software. Another big question was which mouse sensor to use. There are different types of sensor, either "optical" using an IR LED, or "laser". Common choices for DIY projects are the [PMW3360](https://www.tindie.com/products/jkicklighter/pmw3360-motion-sensor/) or [PMW3389](https://www.tindie.com/products/jkicklighter/pmw3389-motion-sensor/) optical sensors, or the [ADNS-9800](https://www.tindie.com/products/jkicklighter/adns-9800-laser-motion-sensor/) laser sensor. All of them are available on breakout boards from JACK Enterprises on Tindie. Unfortunately the shipping costs to Europe are very high, so I had to find another solution. Apparently the requirements for trackballs are not as high as for gaming mouses, they are normally used with much lower resolutions. Because of this, and also because of price and availability, I decided to go for the PMW3360. The chip can be bought on [AliExpress](https://www.aliexpress.com/w/wholesale-pmw3360.html), including the lens assembly, for around 15€. To properly use it also requires some kind of breakout board with a voltage regulator, as the sensor needs ~2V supply voltage. Fortunately the IOs can be used with 3.3V devices like the RP2040. Many designs can be found [on GitHub](https://github.com/search?q=pmw3360), like ["Ogen" from JeremyBois](https://github.com/JeremyBois/Ogen) or ["PMW3360" from kbjunky](https://github.com/kbjunky/PMW3360). I decided to go with ["pmw3360-breakout" by jfedor2](https://github.com/jfedor2/pmw3360-breakout). It is the most minimalistic and it also includes all required files to get the board pre-assembled from [JLCPCB](https://jlcpcb.com/). With their usual promo I got 5 boards produced and assembled for just 15.65€, including shipping. This is unbelievable to me. To be able to call this a trackball, we also need some kind of ball or sphere of course. Many people go with the smallest regulation billard balls with a diameter of 38mm. These can be sourced relatively easily and are usualy flat enough, so I got some. This turned out to be a bit of a problem, however, more on that later. To get the ball rolling smoothly we also need some kind of mount or bearing surface. There are different options for this, nicely detailed on [Reddit](https://www.reddit.com/r/ErgoMechKeyboards/comments/yyu4ra/trackball_bearings_a_comparison_of_cheap_rollers/) and [GitHub](https://github.com/Wimads/Trackball-mousekeys-add-on-for-Skeletyl) by "Wimads". Because of this I decided to go with Si3N4 static bearing balls with a diameter of 3mm. I got 20 [from eBay](https://www.ebay.de/itm/304376943632?var=603428120198) for ~5€. For the switches I decided to go with Cherry MX keyboard switches, simply because I had them available. They are not optimal with their long key travel, I plan to change it in the future, but you also kinda get used to them. *Note*: some parts are only sold in bulk. Therefore, if you only want to build a single trackball, it will be more expensive than the sum listed above. What you see on top is the actual price I paid for the parts, reduced to the amount required for a single device. Also some parts, like screws or 3D printing materials, I consider as normal parts of a workshop, so they are not added to the cost either. ## 3D Design The most important part of the 3D design is the mounting of the sensor and lens assembly in relation to the tracking surface, in our case the ball. The datasheet has lots of dimensional drawings which kind of hide all the important measurements somewhere in there. I took great care and tried to design everything according to the specifications. The static bearing balls are push-fit mounted inside the "roller holders". Printing this part is a bit tricky. Including the ball it should have a total height of 10mm. With my FDM printer it was 0.1mm too small, so I added an adjustment parameter in the design. Later iterations were always printed on SLA printers, where the adjustment had to be set back to zero. So if you want the distance to be perfect, measure the printed and assembled part and adjust accordingly. Although I think small deviations shouldn't matter too much. The sensor has a relatively large range of useable distances. The roller holders are kept in place with a grub screw for each. Then the sensor can be screwed into the top housing first, followed by the Pi. The bottom part is just a lid, without any parts screwed into it. Unfortunately the design is relatively unwieldy in OpenSCAD. Even the preview render takes dozens of seconds, with a very sluggish UI afterwards. Rendering takes ¾ of an hour on my machine. For development the `$fn` parameter can be set to a lower value. That helps somewhat. ## Firmware Development Before designing and printing the complete device I made a small test bed to hold the sensor and ball. With this initial "engineering sample" built I could continue with development of the firmware. This was basically my christmas holiday project in 2022. Interfacing with the PMW3360 chip is a bit more difficult than I initially expected. Of course everything is confidential and proprietary, but you can find [leaked datasheets on the internet](https://d3s5r33r268y59.cloudfront.net/datasheets/9604/2017-05-07-18-19-11/PMS0058-PMW3360DM-T2QU-DS-R1.50-26092016._20161202173741.pdf). It describes everything pretty well, including pinout and electrical characteristics, which were already taken care of in the breakout board. And it also describes the SPI communication interface. The only problematic part is the so-called SROM. The chip requires a binary blob to be loaded on every power up, otherwise it won't work. Fortunately this was also leaked or sniffed (I'm not sure), version 4 is available [here](https://github.com/mrjohnk/PMW3360DM-T2QU/blob/master/Arduino%20Examples/PMW3360DM-polling/SROM_0x04_Arduino.ino) or [here](https://github.com/SunjunKim/PMW3360/blob/master/src/PMW3360.cpp), for example. So I've included it in my firmware as well. Not all of the fancy functionality is included in this V4 firmware blob, but it is enough for normal mouse operation. Contrary to that I'm very happy with the RP2040 / Raspberry Pi Pico ecosystem. The hardware seems well documented without having to go completely in-depth into the CPU datasheets. And the SDK similarly seems well thought-out and implemented. I was especially delighted with the ability to simply use a second Pico as a debugging dongle instead of needing an ST-Link or similar hardware. For the USB device implementation the Pico SDK includes [TinyUSB](https://github.com/hathach/tinyusb). From the Pico SDK I based my work on their [HID Composite example](https://github.com/raspberrypi/pico-examples/tree/master/usb/device/dev_hid_composite) ported from TinyUSB, as well as the [CDC MSC example](https://github.com/hathach/tinyusb/tree/master/examples/device/cdc_msc) directly from TinyUSB. This was very comfortable to use. From [a past project](2015_12_20_serialgamepad.html) I'm familiar with the pain of writing custom USB HID descriptors. Fortunately this is all handled by TinyUSB. You just need to pass the appropriate delta values for the mouse axes. I combined the examples to have the device act simultaneously as a human input device, and serial port and mass storage device for debugging. Of course I only developed and tested the device on my Linux machines initially. But I have now also tested it with Windows, and it works fine there as well. All functionality can be accessed without any driver installation. For the debug mass storage I also had to add some kind of file system. To do this I used [the FatFs library](https://github.com/abbrev/fatfs). I simply reserved a bunch of memory in RAM that acts as the disk device. Before it is mounted by the user, the disk is formatted and filled with the required data. Then the host can mount it and read the files. ## Wiring Wiring up the device is very easy, especially because the RP2040 provides great flexibility in its use of the GPIOs and the hardware periphery, in our case SPI. The SPI interface of the PMW3360 needs to be connected to one of the SPI interfaces of the Pico. But which one you use is not important and can easily be changed in the code. By default it uses the standard SPI0 pins.
GPIO 16 (pin 21) MISO -> MISO on PMW3360 board GPIO 17 (pin 22) CS -> NCS on PMW3360 board GPIO 18 (pin 24) SCK -> SCK on PMW3360 board GPIO 19 (pin 25) MOSI -> MOSI on PMW3360 board GPIO 20 (pin 26) -> MOTION on PMW3360 board 3.3v (pin 36) -> VCC on PMW3360 board GND (pin 38) -> GND on PMW3360 board GPIO 21 (pin 27) -> Switch (back button) GPIO 22 (pin 29) -> Switch (middle button) GPIO 26 (pin 31) -> Switch (left button) GPIO 27 (pin 32) -> Switch (right button)