20 keys ought to be enough for anybody.

— probably not Bill Gates

For a long time, I’ve wanted to design my own printed circuit board and have it built. In 2025, I had the opportunity to do it, thanks to my fellow Googler Matthew Lai running an informal course on PCB design. So, in this post I’ll describe the process of designing and manufacturing TwentyPad, a twenty-key macro pad based on the ATmega32 microcontroller, using KiCAD.

I’ll assume a basic knowledge of electronics here, specifically that you know the basic components and can read a circuit diagram. I won’t go into click-by-click detail of how to do things in KiCAD, but instead I’ll aim to summarize the steps and give you a high-level overview. Hopefully this will give you an idea of what’s involved in a project like this, before you dive in to more precise sources.

Why a macro pad?

For this project, my objective was to learn about PCB design, rather than to make a specific device. So why a macro pad?

  • My mechanical keyboards are both tenkeyless, and I occasionally miss having a number pad. I figured I could make a 5 x 4 grid of switches that could double as a number and macro pad.
  • The circuitry required is simple, and didn’t require me to dig out my notes from Electronics A-level.
  • Back at university, I’d once taken a break from a somewhat stressful hardware group project by desoldering all the switches from a Cherry keyboard I’d found in the e-waste. Of course, they’d then sat in a bag in my cupboard for 10 years. Now I could finally use them!

Since I also had a Teensy 2.0 with header pins hanging around, I initially planned to mount the switches on a board with connections to two female headers to slot the Teensy into. QMK Easy Maker can even produce firmware for it in your Web browser, no terminal required, so long as you only put one switch on each pin. This design was very simple, but Matthew encouraged me to aim higher and include the microcontroller itself on my board. After all, the Teensy 2.0 schematic is freely available, so I could base my design on that.

Process overview

The PCB design and ordering process can be broken down into six parts:

  • Design a schematic, a circuit diagram showing what components are present and how they’re connected without specifying where they’ll be in physical space
  • Choose (or possibly make) footprints for each of those components. A footprint describes the physical layout of the pads and holes needed to mount the component on a PCB. This step generally involves matching your components to parts available from your PCB assembler, too
  • Lay out the components in physical space and add tracks to connect them together
  • Create a set of files to describe the board layout, list the parts, and specify where they should be placed
  • Submit these files in an order to the manufacturer, making various decisions about the manufacturing process

I’ll go through these, and also a few things that happened afterwards:

  • Debugging the thing when it didn’t work first time
  • Making a QMK firmware binary and flashing it to the board
  • Exporting a 3D model of the board and its components, to help with designing a 3D-printed case

The schematic

For our purposes, a schematic is a circuit diagram showing a set of electronic components and how they’re connected, without specifying how they’re laid out on the real board. Designing a complex circuit from scratch is beyond the scope both of this article and my current expertise — Electronics A-level was a long time ago. Instead, I based my design on the schematic of the Teensy 2.0, available on the PJRC website, and added key switches to the GPIO pins. This still left a few things to do, however.

Firstly, I needed to find a symbol to represent the Cherry MX switches I was going to solder on to the board. A symbol is a circuit element that can be placed on a schematic, like a capacitor or microchip, or something more abstract like a power rail. KiCAD has a built-in symbol library, but it doesn’t include keyboard switches. Instead, I downloaded Sebastian Szczepański’s Cherry MX symbols and footprints from GitHub into my project directory, and added the symbol library to the project.

Secondly, the Teensy 2.0 schematic didn’t specify what crystal it used to give a clock signal to the microcontroller, just that it oscillates at 16MHz. From inspecting my own, I could tell that it had two ground pins, pointing me to the “Crystal_GND24” symbol from the Device section of KiCAD’s library.

With those things done, I had a schematic extremely similar to the Teensy 2.0’s, but I made a few other changes in addition to adding switches to the GPIO pins.

  • I replaced the MicroUSB connector with USB-C. This is a simple conversion: on KiCAD’s “USB_C_Receptacle_USB2.0_14P” symbol from the Connector library, just bridge the pairs of D- and D+ pins, and add 5.1KΩ resistors between its CC1 and CC2 pins and ground.
  • Feeling like I should do something with the unused GPIOs, I added a couple of LEDs and a pin header for a hypothetical future expansion board.
  • In case the chip I bought didn’t come with a bootloader, I added an AVR ISP connector to allow it to be flashed without USB. In the end, this was unnecessary.
  • I added some test points to various lines, including some that I shouldn’t have done, but also one that turned out to be vital to salvaging the boards after I missed a connection in the schematic (see Debugging).
The finished TwentyPad schematic
The finished schematic, corrected to include the missing VCC to GND connection

Choosing footprints and parts

For most symbols on a schematic, there are a bunch of different physical forms it could take, and which one we choose will determine what copper pads and holes we need to make on the PCB to accommodate it. For example, my schematic calls for 22Ω resistors on the USB data lines. It doesn’t specify the physical size of those resistors, or how they’re mounted to the PCB. That’s decided by what footprint I choose to match with that symbol on the schematic. Similarly, I might want the USB-C port to stick up vertically out of the board, or horizontally, and that choice will affect what footprint I choose.

In practice, at this point you’ll be choosing specific parts for all but the basic components, then picking the footprint that they need. For example, Matthew had recommended a specific USB-C port, so I chose the “USB_C_Receptacle_HRO_TYPE-C-31-M-12” footprint it needed.

Similarly, the ATmega32U4 microcontroller comes in a variety of package types with different footprints, but from the datasheet’s Ordering Information section (33.2, page 422) I learned that only the -AU or RC-AU chips come with a bootloader pre-loaded, which would save me from having to use a specialist flashing tool before I could program the chip. The RC-AU chips come with a built-in oscillator, but it only runs at 8MHz, so I avoided them, and settled on the -AUR package type, since it was a little cheaper at the time. It corresponds to the “Package_QFP:TQFP-44_10x10mm_P0.8mm” footprint in KiCAD’s library.

For the resistors and capacitors, I didn’t choose specific parts at this point, but I knew that I’d want surface mount components because they’re more compact. Two-pin surface-mount components come in a bunch of sizes described in the form XXYY, meaning 0.XX” by 0.YY”. For example, an 0805 footprint is for a 0.08” by 0.05” component. That’s pretty much the smallest you can solder by hand, so I defaulted to that in case I needed to make changes to the PCBs after they were manufactured. I did change this later, however, for two 10pF capacitors, which I could only get in 0605 and 0403 sizes.

Another consideration here is loading fees. JLCPCB, the manufacturer I was using, has a list of parts that they consider “Basic”, for which you just pay per component. For any others, they also change a per-order loading fee of a few dollars per part type to cover the costs of loading them into the placement machine. For example, the USB-C port I used cost $0.17 each, but had a $3 loading fee. Since I was only ordering 5 copies of the board, the total cost of the USB-C ports was 5 * $0.17 + $3 = $3.85, so the loading fee was significant, and something to be avoided if possible.

Choosing crystal capacitors

Choosing a crystal is a little more complicated than some of the other parts, in that different crystals have different load capacitance (CL) values, and the values of the two capacitors connecting the crystal to ground have to be calculated based on the load capacitance and the board’s “stray” capacitance (often approximated to 5pF). For this, I found a crystal that was included in JLCPCB’s basic parts list, and calculated that it would need 8pF capacitors, which weren’t in the basic list. However, approximating the stray capacitance to 4pF resulted in 10pF grounding capacitors instead, which are in the basic list, so I went with that.

Modifying MX switch footprints

Sebastian Szczepański’s KiCAD library included footprints for the Cherry MX switches alongside the symbols, and these were quite sufficient for doing the initial board layout, but the bag of old switches I had lying around had two additional pins for a bridge wire1. Using KiCAD’s footprint editor, I made a copy of the 2-pin Cherry MX switch footprint and added additional holes for them, using the positions and dimensions from the MX switch series datasheet. (It seems the pin locations match those used for switches with diodes.)

Laying out the PCB

Next comes the fun part (in my opinion): turning our abstract schematic into a physical layout of components on a circuit board.

To start with, we’ll have a blank layout. Using the “Update PCB from schematic” function will add all the footprints to the PCB, placing them in a clump without any tracks connecting them and probably not in a particularly helpful arrangement:

Screenshot of all the components in KiCAD's PCB editor, just after adding them from the schematic.
All the components we'll need, just not in any sensible arrangement

We can then drag the components around to where we want them. Some need to be in a specific spot for the design to work — for example, the USB-C connector needs to be on one edge of the board, and I want the keys to be in a grid. For some others, we want to put them close together to minimize the length of the tracks between them. That’s the case with the oscillator, which we want as close as possible to the ATmega32’s XTAL1 and XTAL2 pins. Similarly for the VCC–GND capacitors; though the schematic only shows the ATmega32 as having two VCC pins for simplicity, it actually has four, and each should have a capacitor between it and ground as close to the chip as possible. For the rest, we just want to put them in places that’ll make the board look neat and tidy.

Screenshot of a few components in KiCAD's PCB editor. Thin, straight blue lines link the pins that need to be connected to each other.
A subset of the components arranged to show the "rats' nest", the blue lines linking their pins

We haven’t added any tracks yet, but you might notice that KiCAD has drawn some thin blue lines between the pins of the components. This is colloquially referred to as the “rats’ nest”. These indicate the connections we made on the schematic, which are currently missing from the PCB. We use the “Route Single Track” tool to replace these lines with real tracks. At this point, we won’t add tracks for connections to GND, however — we’ll deal with those all in one go a bit later.

This also involves choosing the widths of the tracks. Electrically, at the low currents and frequencies involved in this design, this doesn’t matter much. I mostly used 0.3mm tracks, expanding to 1mm for the power connections (GND and VCC) where there was room.

Some tracks, like the USB data lines or the signal lines from the oscillator, belong in pairs, and we want them to be the same length. To route these, we use the “Route Differential Pair” tool to draw them, then the “Tune length of a single track” tool to make the shortest one a bit longer.

We’ll also use vias to add connections between the two sides of the PCB. First, we’ll use these where a track needs to go underneath another track or the pads for a component. Then, we’ll use them for connections to the ground plane.

Remember that I said I was leaving the GND connections until later? That’s because on a typical PCB those are routed a little differently. Instead of having individual tracks to get GND to all the places it needs to be, one layer is used almost entirely as a ground plane. This is a continuous sheet of copper all connected to GND, which we can punch through to using a via wherever we need it. As well as being convenient, this also reduces electrical noise.

To create a ground plane, we first need to draw the board outline, which the factory will use to cut the board out. This is as simple as using the drawing tools on the Edge.Cuts layer. Then, we select the back copper layer (B.cu) and use the “Draw filled zones” tool. Afterwards, we’ll have to re-fill the zones (Edit > Fill All Zones) whenever the back of the board changes, for example when we change tracks there or move a through-hole component.

Validation

Next, we should run the design through KiCAD’s validation tool. This will check for things like connections on the schematics that aren’t on the layout, and tracks or pads that are too close together to be reliably produced by the factory. The “Design Rules” section at File > Board Setup… has a bunch of parameters, which the factory will tell us the values for, for example on this JLCPCB capabilities page. Once those are set, running the Design Rules Checker will highlight any potential problems.

Finishing touches

Now the design’s pretty much finished, we can take care of a couple of extra details.

My plan was to 3D print a case for the macro pad, and fix it on with screws, so I’d need some mounting holes. I could have added these by drawing circles on the Edge.Cuts layer, but I opted to use KiCAD’s built-in footprints for mounting holes instead, adding them straight to the layout using the “Place Footprints” tool.

Then I tidied up the silkscreens, which are layers of labels added to both sides of the PCB. KiCAD automatically adds part labels to the silkscreen, but doesn’t necessarily make them neat when parts are close to each other. As well as dragging them around and rotating them to not overlap, I added my own labels for debugging pins, and a GitHub link on the back.

The finished TwentyPad PCB layout
The finished PCB layout, corrected to include the missing VCC to GND connection

Creating the order files

With the design done, we need to turn it into a form we can send to the manufacturer. For this, I used Benny Megidish’s Fabrication Toolkit KiCAD plugin. The main task here is making a final decision on which parts to use (see Choosing footprints and parts), then copying their JLCPCB part numbers into a new field on the footprints or symbols in KiCAD. At this point I realized that parts for a number of the optional things I’d added to the board — large LEDs, pin headers, and the AVR ISP connector I’d added — weren’t available through JLCPCB at a reasonable price, so I didn’t specify part numbers for those and marked them as “do not populate” when I submitted the order. Similarly, since already had my Cherry MX switches I marked those as do not populate.

With the part numbers added, Fabrication Toolkit can then be used to generate the files to upload to JLCPCB.

Ordering

We’re ready to order the boards. This involves uploading the files we just created to JLCPCB, which will present many choices for how we want the board to be made. Most of these are pretty self-explanatory, or have sensible defaults. The main points are that the surface finish should match the type of solder you’ll use (i.e. leaded vs. lead-free), and if you’ve put vias directly under copper pads then you’ll need to choose filled and capped vias.

When it comes to the PCB assembly stage, you might find that JLCPCB’s component placement viewer shows your components offset from their footprints. This seems to be a commonly-encountered bug. Some say that JLCPCB’s engineers will correct this for you before proceeding with the order, but in my case all the components were offset by the same amount so I was easily able to correct it myself in JLCPCB’s viewer.

And now we wait. JLCPCB tried to default to their fast 2-day shipping from China every time I changed an option on the order screen, but I wasn’t going to pay that much, so it took about a week for the boards to be shipped to the UK.

Debugging

The boards arrived, I plugged one in to my Linux laptop, and… nothing happened. I hadn’t loaded any firmware onto the chip yet, but pressing the reset button should have made the ATMEL bootloader appear when I ran lsusb on the laptop, and yet, nothing.

Using a multimeter to check the voltages on various pins around the ATmega32 soon revealed the problem: the voltage on the VCC pin of the chip was 0, so it wasn’t getting any power. Double-checking the Teensy 2.0 schematic that I’d based my design on, I noticed that it shows three solder pads that allow VCC to be connected to either 5V (VBUS) or 3.3V. Crucially, the schematic itself doesn’t show any connection between the solder pads, but a note below says that when it’s shipped two pads are bridged to connect VCC to 5V. I’d missed this connection before.

The top half of the Teensy 2.0 schematic, with a red circle around the three solder pads that allow VCC to be connected to 5V or 3V
The part of the Teensy 2.0 schematic I'd missed (original full schematic)

Thankfully, when designing the schematic I’d added a test point for VBUS (5V), thinking it might come in useful later. The extra header I’d added for the unused GPIOs also included a VCC connection, so fixing my oversight didn’t require any fiddly soldering, just the addition of a single wire between two quite large pads.

Photo of the top part of the TwentyPad PCB, with a wire added to link the TP_VBUS test point with the VCC pin of the (unpopulated) expansion header.
The wire adding the missing connection

After this, connecting the board to my laptop did result in the bootloader appearing in lsusb:

Photo of a TwentyPad PCB dangling by its USB connection in front of the screen of a vintage ThinkPad, which is showing the output of lsusb in a terminal. Among the entries is one for "Atmel Corp. atmega32u4 DFU bootloader".
It's alive! (See the "Atmel Corp. atmega32u4 DFU bootloader" entry in the terminal)

At this point, I dug out those old Cherry MX switches and soldered them on:

Photo of a TwentyPad PCB with black Cherry keyboard switches soldered on
The board with its switches soldered on

Building and flashing QMK firmware

Of course, the chip needs some firmware on it to make it into a usable keyboard. I mentioned QMK Easy Maker above, which can make firmware for a Teensy 2.0 in your Web browser, but because the Teensy has a different bootloader to the one shipped with the ATmega32, I couldn’t use it directly. Instead I followed the QMK Tutorial and then the guide to porting a keyboard. I based most things on the Easy Maker Teensy 2.0 configuration files, changing the bootloader to atmel-dfu, and soon had a binary ready to flash.

However, flashing failed on the first attempt:

$ qmk flash -kb handwired/twentypad -km default
...
Flashing for bootloader: atmel-dfu
Bootloader Version: 0x00 (0)
Checking memory from 0x0 to 0x6FFF...  Empty.
Chip already blank, to force erase use --force.
Checking memory from 0x0 to 0x39FF...  Empty.
0%                            100%  Programming 0x3A00 bytes...
[ X  ERROR
Memory write error, use debug for more info.

One Reddit post said this meant I needed to reflash the bootloader with an ISP (exactly what I’d tried to avoid by choosing the -AUR version of the chip), but a blog post and a comment on a QMK GitHub issue said that simply force-erasing the flash first would fix it. To do that, I installed dfu-programmer from source2 and ran (referencing QMK flashing docs):

$ dfu-programmer atmega32u4 erase --force
Erasing flash...  Success
Checking memory from 0x0 to 0x6FFF...  Empty.

Thankfully, that made the qmk flash command succeed, so that I could type with the number pad!

QMK has a handwired directory so that firmware configurations for niche keyboards like this can be contributed to the project. Once my pull request had been merged, TwentyPad appeared in the QMK Configurator, providing a nice tool for designing key layouts and producing firmware binaries for them.

Making a case

The CAD and 3D printing skills for designing a case are beyond the scope of this article, except for this trick: from KiCAD’s PCB editor, you can export a 3D model of the board as a STEP file, ready to load into your favourite CAD software. Not all components have 3D models associated with them by default, but if you find one yourself — such as Costantino Schillebeeckx’s STEP file for Cherry MX switches — you can associate it with a footprint in the footprint properties. I loaded my model into Onshape, and used it as a reference to design a case around.

Rendering of a 3D model of TwentyPad, including 3D objects for the PCB itself and most of the components, including the Cherry switches, LEDs, pin headers, and some surface-mount components
The 3D model created by KiCAD

Lessons for next time

There are a few tweaks I’d make if I was going to produce more TwentyPads (other than adding the power connection that I missed).

I added test points in a couple of places that I apparently shouldn’t have done. The ones on the USB data lines could cause trouble at higher data rates, though probably not the ones that the ATmega32 is capable of.

The ones on the oscillator outputs (the XTAL1 and 2 pins of the chip) are more likely to cause problems, and aren’t likely to be useful for debugging anyway, since attaching any sort of test device would change the characteristics of the circuit. Rerouting a few connections to switches would allow those lines to be shortened and avoid vias, which would be good given the high frequencies on them.

In future designs I probably wouldn’t use the ATmega32. It was good for a starter project, given my familiarity with it from the Teensy 2.0, but it’s a pretty old and expensive chip. You can get much more capable chips for less money now, such as the RP2040.

Conclusion

Top-down view of a TwentyPad in a 3D printed case of translucent green filament View of the back side of a TwentyPad, showing the "TwentyPad" label next to the USB-C port
Two of the finished TwentyPads in their cases

All in all, I’m really pleased with how this project turned out. The many steps and little details of the process made it seem pretty intimidating, but having someone to guide me through it made all the difference. Thanks again to Matthew Lai for running the course! I hope that this blog post can be helpful for those who don’t have such a knowledgable coworker around to help.

If you want to replicate the project yourself, you can find the PCB design on GitHub (with the missing VCC to VBUS connection added), case design in Onshape, and firmware in the QMK repository and QMK Configurator.

  1. These are just two extra pins sticking out of the bottom of the switch linked by a wire, which are used on some keyboard designs to avoid needing an extra layer of copper on the PCB. 

  2. There’s a Debian package with that name, but it has a different command syntax; it’s either a very old version or a different tool.