The missing bits for how to make a QMK macro pad from scratch: Writing a firmware for a custom board

At some point, I was linked to Ruiqi Mao’s guide on how to make a keyboard PCB in KiCad and kinda filed in the back of my head the idea of doing that. Because, honestly, PCB design is kinda fun.

Eventually, I decided there were enough interesting reasons all coupled to making a ATMega32u4 board that I might as well sit down and make a development board for that chip that was also able to be a QMK keyboard that I should devote some design time to the problem.

Picture of my little USB macropad

But first, a warning:

I’m writing this slipshod because I could not find anybody who wrote any version of it at all. This ought to be an actual proper reasonable step-by-step guide.

If you are making a keyboard, why make a PCB using KiCAD?

  • PCB fabrication is cheap these days. If you like the process of hand-wiring a keyboard, maybe your mental equation would be entirely different, but I very much prefer the process of working with fabbed PCBs to any version of point-to-point wiring
  • KiCAD is open source and actually quite friendly these days. Apparently folks had some complaints in the past about KiCAD 4.0, but 5.0 has been a fairly trusted friend in electronics.

Designing the board

There’s a few pointers and warnings here.

First, Ruiqi Mao’s guide is old and has issues and has been unmaintained, although none of the issues actually caused me trouble in my design because I was ignoring those bits anyway.

If you want that guide, but more up-to-date, the ai03 guide is actually fairly current.

Second, DigiKey’s KiCad footprint library is dangerously out of date and caused me to have to make a second rev because they knowingly left broken footprints in their repo.

I have a few differences of opinion with the build process from the more modern ai03 guide:

  • The guide is using a SMT crystal. I tend to work off of the philosophy that I’ll use a HC-49/U sized crystal unless I have to do something else. So I grabbed a known working clock circuit from a past design instead of using the one provided. These are much easier to solder up with a hot iron.
  • I added a PRTR5V0U2X ESD diode array. This is a cute little chip that’s specifically designed to block surges on the lines of a USB 2.0 connection.
  • I decoupled the shield from the ground with a 4700 pF capacitor and a 1 M resistor, as per an Atmel application note.
  • Instead of the Molex 0548190589 Mini-USB connector, I decided to use an Amphenol 10103594-0001LF Micro-USB connector.
  • Because it’s a multi-purpose protoboard, I used the standard 6mm x 6mm tactile buttons instead of actual keyswitches.
  • Because I’m trying to fit as much as I can inside of the 5cm x 5cm square from DirtyPCB’s, I ended up making a 2x3 matrix instead of a 2x2 matrix.
  • Also, because it’s a multi-purpose protoboard, I broke out all of the pins to headers so I can use it for other projects.
  • Finally, I added a NeoPixel LED.

Unlike many microcontrollers, the ATMega32u4 comes with a bootloader

If you like to live dangerously, you can design a keyboard PCB in KiCAD or another tool, purchase a ATMega32u4, solder it down, and program it with QMK without owning any sort of developer cable or device programmer.

For many microcontroller designs, you need to have a developer kit of some sort with an overpriced cable to put some firmware in place. You can still program an ATMega32U4 via the ISP port just like any other AVR microcontroller if you want, but they left a completely reasonable DFU bootloader there.

I don’t understand why this isn’t the default for all microcontrollers. This is something only the hardware-oriented hardware companies seem to think is a good idea.

I haven’t yet had the time to try this trick with the larger Arduino environment, so I’m not sure if this applies there as well.

A vague outline of how to create a new QMK keyboard.

  1. You can probably find something close. In my case, it was the 6ball.

  2. I created a folder in qmk_firmware/keyboards/handwired/ called “wirehead” and renamed the 6ball.h and 6ball.cpp files to wirehead.h and wirehead.cpp

  3. For config.h:

    • You probably want to change the USB Device ID section to match your device. QMK devices tend to use 0xFEED as the vendor ID and a unique product ID,

      ```C++
      /* USB Device descriptor parameter */
      #define VENDOR_ID       0xFEED
      #define PRODUCT_ID      0x3993
      #define DEVICE_VER      0x0000
      #define MANUFACTURER    Wirehead
      #define PRODUCT         Keyboard-Bit
      #define DESCRIPTION     Keyboard-Bit Macropuck
      ```
    • Here’s where you set the dimensions for the keyboard and the pinout

      /* key matrix size */
      #define MATRIX_ROWS 2
      #define MATRIX_COLS 3
      
      /* pin-out */
      #define MATRIX_ROW_PINS { F0, F1 }
      #define MATRIX_COL_PINS { F4, F5, F6 }
      #define UNUSED_PINS
    • Also, here’s how to turn on the NeoPixels:

      /* ws2812 RGB LED */
      #define RGB_DI_PIN F7
      
      #define RGBLIGHT_ANIMATIONS
      #define RGBLED_NUM 1    // Number of LEDs
    • Finally, here’s the diode direction that the tutorials use:

      /* COL2ROW or ROW2COL */
      #define DIODE_DIRECTION COL2ROW
  4. For rules.mk:

    • Here’s how you specify that you are using a ATMega32u4 out of the bag with the default bootloader:

      # MCU name
      MCU = atmega32u4
      
      # Bootloader selection
      #   Teensy       halfkay
      #   Pro Micro    caterina
      #   Atmel DFU    atmel-dfu
      #   LUFA DFU     lufa-dfu
      #   QMK DFU      qmk-dfu
      #   ATmega32A    bootloadHID
      #   ATmega328P   USBasp
      BOOTLOADER = atmel-dfu
  5. For wirehead.h:

    • Here’s the entire file, demonstrating what the layout is

      #ifndef HANDWIRED_WIREHEAD_H
      #define HANDWIRED_WIREHEAD_H
      
      #include "quantum.h"
      
      #define LAYOUT( \
            k01, k02, k03, \
            k04, k05, k06 \
      ) \
      { \
          { k01, k02, k03 }, \
          { k04, k05, k06 } \
      } 
      
      #endif
  6. Define the keymap

    qmk_firmware/keyboards/handwired/wirehead/keymaps/default/keymap.c looked like this:

    #include QMK_KEYBOARD_H
    
    #define _MAIN 0
    
    const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
      [_MAIN] = LAYOUT(
        KC_Q, KC_W, KC_E,
        KC_A, KC_S,  KC_D
      )
    };
    

    Note: This is not quite right. There’s the keymap.json file that’s a bit of a standard in a bunch of places and I didn’t make one.

  7. Burn the bootloader

    I sidestepped some issues I don’t really want to go into by setting up a fresh Ubuntu 18.04 VM so I could run QMK and then I burned it from Windows, literally by copying the .hex file in my qmk_firmware directory and then pointing the handy QMK Toolbox at the generated .hex file… and it worked.

Conclusion

It’s universally a jerk move to say something is “easy” or “simple,” even though a lot of software engineers have been doing it. I’m not going to do that. Nothing is easy or simple.

Even though there’s not really a good step-by-step guide to how to set up a completely new keyboard in QMK, QMK was the least of all annoyances in my quest to go from a decent hobbyist understanding of understanding of PCB design in KiCAD to a functioning UCB keyboard.

Furthermore, with the help of the two guides, plus some earlier time reading the Arduino From Scratch series and the DIY Arduino Checklist, I would have even gotten it right on the first try had the DigiKey KiCAD library not steered me wrong.


Posted:

Updated: