James Stanley

I made a macro keypad with 3d-printed switches

Mon 6 July 2020
Tagged: keyboard, electronics, 3dprinting

Latest on the 3d-printed keyboard switch project: I've reached a switch design that I think is probably reliable enough, and I've put 3 of them together to form a macro keypad just to see how it all goes together before I commit to a full keyboard. I don't have a number for how many presses the switch lasts, other than to say that the motor on the testing machine stopped working before the switch did (after about 250,000 presses).

You can watch my short demo video if you want.

"80%" and "60%" keyboard layouts are quite popular, which are correspondingly reduced versions of a full keyboard layout. I like to think of this keypad as a "3%" keyboard, it's fine as long as you only want to type "jes".

It's controlled by an Arduino Pro Micro. It's hard to tell from the pic because of the hot glue, but the switches are wired up to pins 2, 3, and 4, from the other side of the board.

For the software side of it, I used the Arduino Keyboard library, using this quick and dirty Arduino code. Approximately all of the code is just related to the debounce logic.

The switches are printed in some generic no-name clear PETG. The case and keycaps are printed in Prusament Galaxy Black PLA. I'm a big fan of this filament. The glittery effect means the layer lines almost completely disappear. It's basically the same effect you get when printing with carbon fibre-filled nylon, but with none of the difficulties of printing carbon fibre-filled nylon.

All of these parts, including the switches, were printed on my new Prusa Mini printer, which arrived last week and I'm very pleased with so far.


The problem with the switch design from last time was that the copper tape was splitting apart, which prevents it from conducting along its full length. I initially thought I had solved this by applying a 2nd layer of copper tape, but that turned out not to be a workable solution. I did a bunch of tests of various combinations of 50% and 75% activation position, and 1 or 2 layers of copper tape, and found no significant correlation between either of those 2 variables and how long the switch lasts. The switches lasted between 225 presses and 330,000 presses before failure. 7 failed by the copper tape splitting, 2 failed by the PETG spiral spring breaking (both after more than 200,000 presses), and 1 didn't seem to have any broken parts, it just wasn't reliably working any more.

This is the CAD model of the latest design:

The "leaf spring" has that funny shape in its bend so that it can tilt easily, which ensures that it makes reliable contact with both contact wires, instead of just the first one it happens to touch.

And here's a slow-motion video showing how it works (a slightly earlier iteration, but the same concept):

Also, just for fun, I did some FEA on the spiral spring design, and it behaved just like the real version:

The red areas show the areas of highest stress, and indeed the springs tend to break around the middle of the spiral.

Print your own

I consider this a "release candidate" rather than a finished design, because there is still some work to do on it (in particular, I want to make it a bit less tall). But you can have it if you want it.

You will need:

Download the STL files and arrange them on the build plate something like this (you'll probably have to rotate the spiral spring):

I suggest printing more than 1 of the "leafspring" part as it is a small part quite likely to have defects in the print. Make sure you're printing at a fine enough quality level to include all the features. In particular, make sure (both) the spirals on the spring are connected all the way around, and that the leaf spring is connected all the way along. I use the "0.15mm QUALITY" setting in PrusaSlicer. You shouldn't need any support material. You might need to enable "detect thin walls".

You might need to add up to -0.3mm of X/Y size compensation. I have found that when printing this on the Prusa Mini, optimal clearance is achieved when the 3.0 mm plunger rides inside a 3.0 mm hole. On previous printers I've owned, this wouldn't even fit together. I'm not sure what's going on, but obviously I modelled it to print on my machine, so you might need to apply X/Y compensation on yours.

Step 1: Print the parts. It takes about 40 minutes for me.

Step 2: Clean up any stringing inside the case from the PETG. I use a lighter to burn off the worst of it, and then a Stanley knife to trim off what remains.

Step 3: Examine the spiral spring and ensure that it moves freely. You might need to cut any stringing between the spirals with a knife, or re-print with different X/Y compensation, depending on whether it is a few small tight spots or bad clearance all the way along.

Step 4: Slide the spiral spring into the shelf at the top of the case. The orientation is not critical, I tend to slide it in with the smoothest sides against the case just to make life easy for myself.

Step 5: Push the plunger through the top hole in the case, through the hole in the spiral spring, and through the middle hole in the case. Verify that it moves up and down freely and that the spring reliably returns it to the top. If not, take it back out and trim the tight spots with a knife.

Step 6: Cut off about a 13mm length of the copper wire. Slide it into the hole in the leaf spring. If you can't get it through, you might need to widen the hole with a pin or sharp tool of some kind. Use whatever you have.

Step 7: Press the plunger down slightly to expose the attachment end inside the lower chamber of the switch. Using a small pair of pliers or tweezers, push the attachment part of the leaf spring onto the end of the plunger. Make sure it is pushed home. Looking in the big hole at the bottom of the case, you should see the leaf spring roughly centred. When you let go of the plunger, the spiral spring should still be stretched out slightly. This is desired, without this preload the switch would start moving at 0 force.

Step 8: Cut off 2x roughly 35mm lengths of copper wire. Using a pair of pliers, put a bend about 5mm from one end. Insert the non-bent end through 1 of the wire holes and out the other side. Bend the wire down the other side, and then in whatever direction you want your contacts to point. Repeat for the second wire.

You should now have something that looks a bit like this:

And that should be it! Manually press the switch a few times and visually verify that the middle copper wire appears to be making good contact with the other 2 wires, and then you're ready to assemble it into a circuit! If you want it mechanically attached to a PCB, you could leave a longer length of wire coming out of the first side, and you'd then have 4 soldering points. Otherwise, mount it inside a 14mm square hole. You'll probably need cut-outs in the corners to accommodate the wires sticking out, like this:

If you make one (or more!) I'd be really interested to hear your opinion on what could be designed better, and to see pictures of your results. Email me: james@incoherency.co.uk.

Next steps

We're getting pretty close to being able to put together a finished keyboard. I still have the following things to do:


It would be good to get an accurate value for the number of presses that the switches can take. I think currently a lot of the variability comes from the height at which the switch is positioned relative to the tester. If it's doing 0.1mm short of a full stroke, the switch is likely to last significantly longer than if it is being hammered 0.1mm longer than a full stroke. This is hard to configure as I have only very coarse control of the height.

I watched the Linus Tech Tips tours of the Cherry MX factory and the Omron factory. They're both interesting and worth watching. I took a screenshot of this testing machine in the Omron video:

So there we have a single motor raising a platform up and down which is pressing 10 switches at once. The platform glides on a pair of linear rails, and the exact vertical position of the press is finely adjusted with a separate screw for each switch. The moving platform also has a spring at each end, I am not completely sure why, I think just to take up some of its weight, to reduce the load on the motor.

But this is a much better way to gang-test switches than what I have at the moment, which involves wiring up a separate motor for each switch and gets messy quickly:

I'm past the point where more testing is critical (famous last words, I know), but it would be good to do anyway, so I'd like to make a small-scale copy of the Omron testing machine.


The switch fits in a 14mm square hole (apart from the protruding wires), which is the same size as a Cherry MX switch, but the total height from the bottom of the switch to the top of the keycap is almost twice as large, about 45 mm vs 25 mm:

This directly translates to increased keyboard height, so it would be good to cut this down as much as possible. I should be able to lose 2 mm from the wire support at the bottom of the switch, and another 1 or 2 mm in the height of the leaf spring attachment, but there's no way it's going to be as thin as the Cherry MX.

I'd like to find a way to add a tactile bump (ideally with a clicky noise) to the motion of the plunger. I did try simply adding a bump to the side of the casing that would catch on the leaf spring, but it didn't work very reliably.


Currently my keycaps are based on the one I designed in my CAD Dojo project. This is suboptimal for several reasons:

  1. adding text in FreeCAD is extremely time-consuming, and it can't be edited without deleting it and re-creating it (i.e. there would be a substantial O(n) effort to make keycaps for an entire keyboard)
  2. the keycaps should have a slightly different profile on each row
  3. there should be some keycaps that are different shapes and sizes (tab, caps lock, shift, enter, space, backspace, etc.)

This picture of my Filco Majestouch 2 Tenkeyless shows the different keycap profiles on each row:

I probably want to write some OpenSCAD code to generate the keycaps instead of using FreeCAD.

I found a project KeyV2 that is a parametric keycap library, although I found it quite hard to work out how to add support for custom switches. I haven't yet worked out whether it would be easier to make KeyV2 do what I want or whether I should start from scratch.

I also probably need to add some sort of stabiliser, at least to the space bar and probably to the enter key, backspace key, and right shift key as well. A stabiliser is a mechanism underneath the keycap that keeps the keycap moving up and down vertically, instead of rocking over the switch, in the event that it is pressed on one side instead of in the middle. It typically works exactly the same way as an anti-roll bar on car suspension: when one side moves down, the bar rotates from that end. The rotation is transmitted to the other end of the bar, which also rotates, and pulls the other side down.

(Keyboard image from WASD Keyboard support forum; Car suspension image from Wikipedia)

Finally, I might consider reducing the layer height at the top surface of the keycaps, to reduce the "contour lines" effect:


Even without reducing the height of the switches, I could reduce the height of the keyboard by sticking the Arduino vertically, behind the switches, instead of horizontally, underneath them. That would allow the switch bodies to sit much closer to the bottom. It's probably worth doing.

I probably want to either remove or paint over the LEDs on the Arduino, because currently I get a funny red and green glow coming from the bottom of the keypad, which I don't want to happen on the real keyboard.

And I could consider running QMK, instead of writing my own control software. This would get me lots of fancy features for free, so as long as it is easy enough to get it to work with my switches, it's probably the way to go.

That's all for now. If you're following along with the keyboard switch project, please get in touch. I'd love to hear feedback on anything you have thoughts on.

If you like my blog, please consider subscribing to the RSS feed or the mailing list: