Intro

Just creating a new firmware in QMK is actually not that difficult nowadays.

The QMK docs on new keyboards are pretty short, but that's because there isn't that much to it.

In the past, it was necessary to create a layout-function that manages the matrix.
Nowadays, all the basic hardware configuration is done in the info.json file, and quite a bit of it is already done by the new-keyboard helper.

Complete keyboard config
Complete keyboard config

Matrix

All of the core configuration is inside of the info.json file. The processor and bootloader should already be set when using qmk new-keyboard to initialize a new board.
All that's left is telling QMK how our keyboard is wired, part 2 of the series has the wiring diagram. That's what we need to convey to QMK.

info.json
{
  "diode_direction": "COL2ROW",
  "matrix_pins": {
    "cols": ["E6", "B3", "B7", "B2", "B1", "B0", "F7"],
    "rows": ["D5", "D2", "D3", "D4", "D7" ]
  },
  "layouts": {
    "LAYOUT_gaming_7x5": {
      "layout": [
        { "matrix": [0, 0], "x": 0, "y": 0 },
        { "matrix": [0, 1], "x": 1, "y": 0 },
        { "matrix": [0, 2], "x": 2, "y": 0 },
        { "matrix": [0, 3], "x": 3, "y": 0 },
        { "matrix": [0, 4], "x": 4, "y": 0 },
        { "matrix": [0, 5], "x": 5, "y": 0 },
        { "matrix": [0, 6], "x": 6, "y": 0 },

        { "matrix": [1, 0], "x": 0, "y": 1 },
        { "matrix": [1, 1], "x": 1, "y": 1 },
        { "matrix": [1, 2], "x": 2, "y": 1 },
        { "matrix": [1, 3], "x": 3, "y": 1 },
        { "matrix": [1, 4], "x": 4, "y": 1 },
        { "matrix": [1, 5], "x": 5, "y": 1 },
        { "matrix": [1, 6], "x": 6, "y": 1 },

        { "matrix": [2, 1], "x": 1, "y": 2 },
        { "matrix": [2, 2], "x": 2, "y": 2 },
        { "matrix": [2, 3], "x": 3, "y": 2 },
        { "matrix": [2, 4], "x": 4, "y": 2 },
        { "matrix": [2, 5], "x": 5, "y": 2 },
        { "matrix": [2, 6], "x": 6, "y": 2 },

        { "matrix": [3, 1], "x": 1, "y": 3 },
        { "matrix": [3, 2], "x": 2, "y": 3 },
        { "matrix": [3, 3], "x": 3, "y": 3 },
        { "matrix": [3, 4], "x": 4, "y": 3 },
        { "matrix": [3, 5], "x": 5, "y": 3 },
        { "matrix": [3, 6], "x": 6, "y": 3 },

        { "matrix": [4, 2], "x": 2, "y": 4 },
        { "matrix": [4, 3], "x": 3, "y": 4 },
        { "matrix": [4, 4], "x": 4, "y": 4 },
        { "matrix": [4, 5], "x": 5, "y": 4 },
        { "matrix": [4, 6], "x": 6, "y": 4 }
      ]
    }
  }
}

diode_direction tells QMK how to scan the matrix. I wired my keyboard col to row, so I'll need to use COL2ROW.

matrix_pins just tells QMK which pins to scan, and we'll use those rows and cols in the layouts section.

LAYOUT_gaming_7x5 is just the name of the layout helper-function we later use in our keymap.c file. The x and y coordinates weren't really necessary this time, but if I decide to add VIA(L) support, I already got a rough layout defined.

Keymap

Not much to say here as it does not differ from any other QMK keymap definition.

RGB

The RGB strip is also pretty straight forward, we add RGBLIGHT_ENABLE = yes to our rules.mk to enable the feature.

Then we update our config to tell QMK how many LEDs we have and where our data pin is.

config.h
#ifdef RGBLIGHT_ENABLE
#  define RGB_DI_PIN D6
#  define RGBLED_NUM 21
#  define RGBLIGHT_ANIMATIONS
#endif

Then we just add the necessary keycodes to our keymap and RGB is ready to go.

OLED

OLEDs follow the same logic as RGB strips, first we enable the feature and load the analog.c file

rules.mk
+ OLED_ENABLE = yes
+ OLED_DRIVER = SSD1306
+ SRC += analog.c

Adding the analog.c file to our build allows us to use the OLED functions to write stuff onto the OLED using the oled_task_user function in our keymap.c.

The OLED docs have more in depth examples on how to interact with the OLED. I mostly just copy&pasted the example and adjusted it to show me the different modes.

Joystick

Here comes the interesting part since I never toyed with joysticks in QMK.
Unsurprisingly, this followed the same enable, configure, define pins & done.

The joystick docs are just as helpful here.

rules.mk
+ JOYSTICK_ENABLE = yes
+ JOYSTICK_DRIVER = analog
config.h
#define JOYSTICK_BUTTON_COUNT 0
#define JOYSTICK_AXES_COUNT 2

The keyboard just has the X and Y axis, all switches report as normal keyboard keys, so we only need to define those two axises and 0 joystick buttons.

keymap.c
//joystick config
joystick_config_t joystick_axes[JOYSTICK_AXES_COUNT] = {
  JOYSTICK_AXIS_IN(F6, 0, 512, 1023),
  JOYSTICK_AXIS_IN(F5, 0, 512, 1023)
};

And then we tell the keymap where our analog axis are connected, and what range the inputs report.

Emulating WASD-movement

After all this I already had the basics, the keyboard would send normal keycodes and additionally report two joystick axises to the OS.

Games that support analog inputs natively worked like a charm, but a lot of games don't, and I wanted a WASD mode without running additional software on my PC. So I build that in QMK.

full WASD code
full WASD code

All the code is pretty straight forward

  • analogReadPin() to get the current joystick position
  • if the deadzone is exceeded, press the right key
  • if the joystick is within the deadzone, unregister the key

The 4 booleans to remember which key is held was necessary because calling register_code repeatedly would first unregister_code.
Every time the matrix got scanned, the key would first be released and then pressed again until the next scan.

Additionally, I build a WASD + Shift mode. In this mode, moving the joystick the last few mm in the W direction would also hold Shift to sprint.

Final result

Here is the final build put together with a working firmware.

There is going to be a Part 4 in this series in which I'll cover the issues with such a gamepad.
That's also where I'll publish the modified STL, just so you get the whole picture before building such a gamepad.

Finished gamepad with wrist rest
Finished gamepad with wrist rest
RGB Underglow
RGB Underglow
Joystick
Joystick
OLED
OLED