Trackball Guide
Installation and QMK firmware setup
So! You've got a sweet trackball-ready case from Wylderbuilds?
Great!
You've searched the web for the basics on how to get it going and there's just not a lot out there that's clear and to-the-point?
This guide should help get you going.
We'll go over the hardware bits you need, wiring to your MCU, the QMK setup, and finally show off some of the niftier options available.
First, some side-details.
This guide will be specific to the 34mm sockets and trackballs generally available for keyboards these days. The only mouse/trackball sensor I'll detail is the PMW3360 sensor, specifically the model available from Tindie.
I'll use one of my own trackball Dactyl Manuform cases with a trackball socket meant for bearings. We also have ball-transfer unit sockets, but bearings versions are much more widely available.
I almost always build using rp2040-based controllers, so my examples will focus on the Pi Pico, but all of the popular QMK controllers will have a default SPI setup with pinout diagrams readily available.
Speaking of SPI, I'm no kind of expert on electronics so I won't go into schematics or any deeper details on how the sensors or controllers work their magic. My focus will be putting the bits together and editing the QMK code.
Core Parts
Mouse Sensor: PMW3360 Motion Sensor from Tindie. https://www.tindie.com/products/jkicklighter/pmw3360-motion-sensor/
Trackball: The Perixx Peripro-303 34mm trackball. https://www.amazon.com/Perixx-PERIPRO-303-Trackball-Replacement-PERIMICE/dp/B08DD6GQRV
Bearings: MR63ZZ 3x6x2.5mm steel bearings. https://www.amazon.com/gp/product/B00ZHSQX42
Steel rods: 3mm x 6mm dowel steel rods to use as axles for the bearings. https://www.amazon.com/gp/product/B07M63KXKS
Printed sensor holder: If you purchased a case print from Wylderbuilds, this should have been included. It helps seat the sensor in the proper position and allows for relatively easy removal via screws (depending on the print material.) Here's a link to the STL: https://github.com/bullwinkle3000/dactyl-keyboard/blob/wilder/src/parts/sensor_holder.stl
M2 brass inserts and screws. These hold the sensor with the sensor holder in position. (On a resin case, they're put into the sensor holder.)
M2 inserts: https://www.amazon.com/Cionyce-Embedment-Printing-Fastening-Injection/dp/B0B63CSBGX M2 screws: https://www.amazon.com/gp/product/B01HBFCALS
STORE PLUG You can get a trackball, bearings, rods, inserts, screws, and sensor holder all together in a Wylderbuilds trackball kit! https://wylderbuilds.com/shop/p/trackball-kit
Optional
Dupont ribbon cable: This is what I generally use to connect the sensor and the MCU. https://www.amazon.com/Solderless-Multicolor-Electronic-Breadboard-Protoboard/dp/B09FPJ9TSP
Angled headers: Soldered to the sensor, the cables just plug on. https://www.amazon.com/Uxcell-a15062500ux0349-Single-40-pin-Breadboard/dp/B01461DQ6S
You'll also need that case with a trackball socket, solder, and, of course, a Pi Pico (or whatever your MCU is.)
The PMW3360 Sensor
This is an imaging sensor using an infrared LED and essentially a tiny camera to take 12000 frames per second to detect motion very precisely. It's an excellent sensor suitable for high-quality gaming mice and trackballs. Its datasheet is available here:
https://pdf1.alldatasheet.com/datasheet-pdf/view/899001/PIXART/PMW3360.html
There's a big-brother version of the sensor, the PMW3389, which also works out-of-the-box with QMK. The only difference in the firmware setup would be to define it in your rules.mk, detailed further on. It's also available from Tindie here:
https://www.tindie.com/products/jkicklighter/pmw3389-motion-sensor/
Both packages available from Tindie allows for voltages from 3.3v to 5v, so it's a perfect solution for pretty much all the DIY keyboard controllers. The Pi Pico supplies 3.3v, so it pairs just fine.
Communication between the sensor and the controller is via SPI (Serial Peripheral Interface). There are eight pinouts on the sensor, but we'll only be using six. Here's how they connect to a Pico.
- GD (GND) -- Connects to any GND on the controller.
- VI (V In) -- Power connects to a power supply pin on the controller.
- MI (MISO) -- The SPI "Master In Slave Out" pin.
- MO (MOSI) -- The SPI "Master Out Slave In" pin.
- SC (SCLK) -- The serial clock pin to sync data.
- SS (Slave Select) -- The pin the controller uses to notify the sensor that it wants to talk.
For the Pi Pico and most popular controllers used with QMK, MISO, MOSI, and SCLK have default pinouts which can be used by default, but you can also configure the SPI communications to use different pins if you need to. We'll use the defaults here, which are:
Pi Pico Pin | Elite C Pin | |
---|---|---|
MISO | GP20 | B3 |
MOSI | GP19 | B2 |
SCLK | GP18 | B1 |
The SS pin (can also be refered to as CS, Chip Select) must be specifically configured in the QMK config.h file, which we'll do later. I generally use GP21 to keep it next to the other SPI pins. So, on a Pi Pico, I wire up the sensor pins like so:
Sensor Pin | Pi Pico Pin | |
---|---|---|
VI | 3v3 (out) <-- NOT the 3v3_EN pin | |
MI | GP20 | |
MO | GP19 | |
SC | GP18 | |
SS | GP21 (or however you've defined it) | |
MT (unused) | N/A | |
GD | GND (any available) | |
RS (unused) | N/A |
Attaching the Sensor to the Socket
For the actual wiring, I prefer to use the angled headers and use ribbon cable with dupont connectors for the connection to the controller. When soldering, I make sure to orient the headers so that the connector pins are pointing over the top of the sensor package, not facing out to one side. There should be room enough above the sensor chip for the dupont connectors. That way, the sensor is more likely to fit into the underside of the trackball socket and the pins shouldn't collide with the case somewhere.
If you do use headers, solder them on now.
On the sensor itself, there should be a clear plastic lens piece. If you look at it closely, you'll see the actual round lens in the center as well as small triangular bit poking up at one end. Now check the trackball socket. The hole at the bottom of the socket will usually have a round end and a flat end. The sensor should be oriented underneath so that the triangular bit abuts the flat end of the hole. You should clearly see the round lens through the hole.
Next, if your case is a Wylderbuilds print, the screw holes on the bottom should line up with the screw holes in the sensor PCB. Many other models will have a similar socket and the same holes. But if they don't line up, you'll need to glue things down and not use the brass inserts and screws, which should still be perfectly fine as long as the sensor is lined up well.
If you have a resin case, your best solution will be to seat the inserts with a soldering iron into the sensor holder and then glue the holder the bottom of the socket, using the screw holes to position everything correctly.
If you have a case printed in PLA, PETG, or ABS, then you can seat the brass inserts into the bottom of the socket itself. Just clear out any plastic that might push out into the inside of the socket.
Regardless of the material, if you do use the inserts, they must be flush so the sensor will be as flat as possible and they need to be centered as well as you can to the screw holes.
Go ahead and set the inserts, whether on the holder or the socket.
Now test the fit of the sensor holder to the sensor lens. The screw holes are a little offset at each end, so you'll need to orient the holder so that its screw holes are aligned with the sensor's. The round oval space of the holder should fit around the sensor lens and the lens should seat all the way down. You may need to jiggle things a bit, but they should ultimately fit together snugly. Now check the sensor with the holder against the bottom of the socket and ensure sensor is oriented properly towards the flat end of the socket hole.
If you've put the inserts into the bottom of the socket of the case, you should be able to screw the sensor into place at this point.
But if you've put the inserts into the holder, you'll need to note the orientation of the holder that you don't reverse it when gluing. Remove the sensor from the holder and put in the screws so they protrude just enough to slot into the screw holes on the bottom of the socket so you'll be sure to position it properly. Dab superglue around the edge of the sensor holder where it faces the socket bottom, fit the screws into the socket screw holes and hold it in place until it cures enough to stay. Let it fully cure, then go ahead and re-seat the sensor lens into the holder and screw the sensor PCB to the holder.
Don't over-tighten when screwing things down. The sensor should just be firmly in place without anything loose or shifting. Check the socket from above to ensure the lens is properly oriented with the triangluar bit against the flat end of the hole and the round lens clearly visible.
Prepare the Firmware
Now for some code!
Unless you're using a trackball-oriented setup already available in QMK (eg. tractyl_manuform) or you're relying on one of the setups in my fork of Vial-QMK for your build, you'll need to have your own configuration already set up in QMK and tweak the following files. This guide assumes you've got most of these files already in place and ready to edit.
rules.mk
Add or enable the following lines:
POINTING_DEVICE_ENABLE = yes
Includes pointing device support in your firmware.
POINTING_DEVICE_DRIVER = pmw3360
Specifies the sensor to use.
config.h
For a complete reference on the options available, definitely check out the QMK docs on pointing devices here:
https://github.com/qmk/qmk_firmware/blob/master/docs/feature_pointing_device.md
I won't cover all the options available, just the ones I often use. If you need something special, you can likely find it in that linked page.
Here's my standard setup:
#define SPLIT_POINTING_ENABLE
#define ROTATIONAL_TRANSFORM_ANGLE -25
#define POINTING_DEVICE_INVERT_Y
#define POINTING_DEVICE_RIGHT
#define PMW33XX_CS_PIN GP21
#define POINTING_DEVICE_TASK_THROTTLE_MS 1
#define PMW33XX_LIFTOFF_DISTANCE 0x02
There's a fair bit to cover here, so let's go line-by-line
#define SPLIT_POINTING_ENABLE
Set this if your keyboard is a split and the trackball is on the "slave" side, or the side which doesn't plug into USB.
#define ROTATIONAL_TRANSFORM_ANGLE -25
#define POINTING_DEVICE_INVERT_Y
These two settings account for the orientation of the trackball and sensor on most of the Wylderbuilds trackball models. You may find you need to play with them to get the behavior you want.
#define POINTING_DEVICE_RIGHT
This tells QMK the trackball is on the right half of the split. There's also POINTING_DEVICE_LEFT for a left-side trackball and POINTING_DEVICE_COMBINED if you have trackballs on both sides (dual trackballs!) Only one can be defined.
#define PMW33XX_CS_PIN GP21
Here's the pin definition I discussed above for the SPI setup. You can set it to whatever pin you want. Just, you know, wire things up like that.
#define POINTING_DEVICE_TASK_THROTTLE_MS 1
This sets the polling to 1ms, or a 1000 times per second, which the sensor can certainly handle. The default is 10, or every 10ms or 100 times per second.
#define PMW33XX_LIFTOFF_DISTANCE 0x02
This defines the "lift off distance" sensitivity of the sensor, which defaults to 2mm. This setting can come in REALLY HANDY if your sensor doesn't seem to be tracking well or even not at all, which can easily happen with DIY builds (could be print tolerances, sensor a bit too far, using a non-standard trackball, etc.)
trackball.h and trackball.c (and even more config.h)
These files are more-or-less optional, but they'll help make the trackball easier to use and add a couple of extra modes -- Dragscroll and Sniping. This guide assumes their inclusion.
In my Vial-QMK fork, trackball.h is here:
And here's trackball.c:
These two files are BLATANTLY RIPPED OFF WITH MANY THANKS TO DRASHNA from his tractyl-manuform keyboard code, which is here:
https://github.com/bullwinkle3000/vial-qmk/tree/wylderbuilds/keyboards/handwired/tractyl_manuform
There may be one or two tweaks but they're mostly identical to the tractyl-manuform files.
Incorporating them into your setup should be pretty easy.
Drop those two files into the main directory of your keyboard setup. Then, in your layout definition file (eg. 5x6.h or dactyl.h or zergo.h or whatever), insert at the top of the file:
#include "trackball.h"
Which will make everything available down to your keymap.c file.
Then, back in config.h, add the following:
#define SPLIT_TRANSACTION_IDS_KB RPC_ID_KB_CONFIG_SYNC
This specifies the customized communication handler which handles the extra trackball modes.
#define CHARYBDIS_MINIMUM_DEFAULT_DPI 1200
#define CHARYBDIS_DEFAULT_DPI_CONFIG_STEP 400
#define CHARYBDIS_MINIMUM_SNIPING_DPI 400
#define CHARYBDIS_SNIPING_DPI_CONFIG_STEP 200
#define CHARYBDIS_DRAGSCROLL_DPI 200
The defaults for the sensor's sensitivity for the various modes.
#define CHARYBDIS_POINTER_ACCELERATION_ENABLE
This will apply some acceleration calculations to the trackball's motion in addition to whatever pointer acceleration you have set up in your OS. You can comment this out if you don't need it.
keymap.c
With the above trackball.h and trackball.c files included in your setup, there's nothing actually necessary in keymap.c, but you do now have the Dragscroll and Sniping modes available, so let's look at all the fun mouse/trackball things you can play with.
Here's the QMK page with all the standard mouse key definitions available.
https://github.com/qmk/qmk_firmware/blob/master/docs/feature_mouse_keys.md
I usually at least set up the buttons:
- KC_BTN1 -- Left mouse button.
- KC_BTN2 -- Right mouse button.
- KC_BTN3 -- Middle mouse button.
Dragscroll mode can be set via a toggle or only while a key is held. It turns the trackball into a full page scrolling device, PgUp, PgDn, and horizontal scrolling (or panning).
Its available keycodes are:
- DRGSCRL -- Dragscroll while key is held.
- DRG_TOG -- Toggle dragscroll on or off.
Sniping mode is also set by a toggle or while a key is held. It effectively lowers the DPI sensitivity of the sensor which allows for more precise control over a small area.
The keycodes for sniping are:
- SNIPING -- Enable snipin while held.
- SNP_TOG -- Toggle sniping on or off.
An Aside on Auto Mouse Layers
I haven't yet set up anything with auto-mouse layers in QMK since official support for it isn't yet pulled into Vial-QMK, but if you use standard QMK, you can find how to enable auto-mouse layers here:
Once it's supported in Vial-QMK, I'll flesh out this section with details.
Flash and Test the Firmware
If you don't have QMK set up, get that out of the way now.
https://wylderbuilds.com/building-qmk-firmware
If that all went well, you should have a working setup with my fork of Vial-QMK ready to go and you should have the basic knowledge to go ahead and build the specific firmware for your configuration and flash the firmware file to your controllers.
If your sensor isn't wired up yet to your controller, do that now according making sure you've got all the pinouts matched up as noted earlier. Then plug the pico into the your computer with the USB cable. If it's just one side of a split keyboard, it may take a few seconds to boot.
Test the sensor by placing your fingertip near the lens and moving it. The cursor onscreen should react and move around.
If so, fantastic, it works!
If not, then you'll need to double check the wiring and the config settings. Common issues are:
The sensor's VI pin not connected to the 3v3 pin on the pico.
The GD pin on the sensor isn't connected to GND on the pico.
The SS pin on the sensor isn't connected to the pico pin defined by PMW33XX_CS_PIN in config.h.
Your rules.mk is missing some or all of the following:
POINTING_DEVICE_ENABLE = yes
POINTING_DEVICE_DRIVER = pmw3360
MOUSE_SHARED_EP = yes
Your config.h is missing some or all of the following:
#define SPLIT_POINTING_ENABLE
#define POINTING_DEVICE_RIGHT (or POINTING_DEVICE_LEFT or POINTING_DEVICE_COMBINED)
If nothing you do seems to be solving the issue, reach out to us and we'll try to help.
But, if it is working, you're almost done!
Final Assembly: Bearings, Axles, and Trackball
Insert the steel axle rods into the bearings and fit them into the three slots around the sides of the trackball socket (tweezers or needle-nose pliers can save you some crazy, here.) The axles should go in horizontally and the bearings vertically. Push them as far as they'll go down into the slots.
On some case prints, it's possible there's a bit of cruft in one of the slots which is usually very easy to pull away with those handy needle-nose pliers. Usually, the fit is snug enough on the axles that they'll stay in place and the bearings should just peek out into the socket a little bit. If they seem like they could easily fall out, you can use a tiny drop of super glue at the tips of the axles, just don't get glue onto the bearing!.
Make sure the bearings spin freely and don't bind. A little dab of teflon or "dry" lubricant on the bearings can help them spin nicely and with less noticeable noise.
Drop in the 34mm trackball and make sure it can spin around any direction without sticking.
Now, let's do the final test. If it's not plugged in now, go ahead and plug in the Pico with the sensor. Give it a few seconds, then test the trackball. At this point, if it works well, that's great and you're all set.
But it's actually common that you'll see a bit of stutter, or very slight jiggling of the cursor, or even just nothing. In that case, the answer is almost always to adjust the PMW33XX_LIFTOFF_DISTANCE setting in your config.h and raise it up bit by bit.
The default is 0x02, so try 0x03, then 0x04, 0x05, 0x06, etc.
You'll have to rebuild and reflash the firmware to the pico with the sensor for each bump.
Usually, one of those will be the sweet spot and you'll be good.
If at 0x06 you still don't see any good tracking, then there might be an issue with how the sensor is mounted or the bearings may be sticking too far into the socket, lifting the trackball too far from the sensor.
That's All for Now!
We'll be updating this guide with more varied info. We also hope to add full details for trackball support for KMK and ZMK firmware. Stay tuned.