This post is part of the QMK gamepad series
Part 1 - The CasePart 2 - The WiringPart 3 - The FirmwarePart 4 - ReviewIntro
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.
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.
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.
This post is part of the QMK gamepad series
Part 1 - The CasePart 2 - The WiringPart 3 - The FirmwarePart 4 - Review