Debugging The Instant Macropad

Last time, I showed you how to throw together a few modules and make a working macropad that could act like a keyboard or a mouse. My prototype was very simple, so there wasn’t much to debug. But what happens if you want to do something more complex? In this installment, I’ll show you how to add the obligatory blinking LED and, just to make it interesting, a custom macro key.

There is a way to print data from the keyboard, through the USB port, and into a program that knows how to listen for it. There are a few choices, but the qmk software can do it if you run it with the console argument.

The Plan

In theory, it is fairly easy to just add the console feature to the keyboard.json file:

{
...
    "features": {
        "mousekey": true,
        "extrakey": true,
        "nkro": false,
        "bootmagic": false,
        "console": true
    },
...

That allows the console to attach, but now you have to print.

Output

The code in a keyboard might be tight, depending on the processor and what else it is doing. So a full-blown printf is a bit prohibitive. However, the system provides you with four output calls: uprint,uprintf, dprint, and dprintf.

The “u” calls will always output something. The difference is that the normal print version takes a fixed string while the printf version allows some printf-style formatting. The “d” calls are the same, but they only work if you have debugging turned on. You can turn on debugging at compile time, or you can trigger it with, for example, a special key press.

To view the print output, just run:

qmk console

Note that printing during initialization may not always be visible. You can store things in static variables and print them later, if that helps.

Macros

You can define your own keycodes in keymap.c. You simply have to start them at SAFE_RANGE:

enum custom_keycodes {
    SS_STRING = SAFE_RANGE
};

You can then “catch” those keys in a process_record_user function, as you’ll see shortly. What you do is up to you. For example, you could play a sound, turn on some I/O, or anything else you want. You do need to make a return value to tell qmk you handled the key.

An Example

In the same Git repo, I created a branch rp2040_led. My goal was to simply flash the onboard LED annoyingly. However, I also wanted to print some things over the console.

Turning on the console is simple enough. I also added a #define for USER_LED at the end of config.h (GP25 is the onboard LED).

A quick read of the documentation will tell you the calls you can use to manipulate GPIO. In this case, we only needed gpio_set_pin_output and the gpio_write_pin* functions.

I also sprinkled a few print functions in. In general, you provide override functions in your code for things you want to do. In this case, I set up the LED in keyboard_post_init_user. Then, at first, I use a timer and the user part of the matrix scan to periodically execute.

Notice that even though the keyboard doesn’t use scanning, the firmware still “scans” it, and so your hook gets a call periodically. Since I’m not really using scanning, this works, but if you were trying to do this with a real matrix keyboard, it would be smarter to use housekeeping_task_user(void) which avoids interfering with the scan timing, so I changed to that.

Here’s most of the code in keymap.c:

#include QMK_KEYBOARD_H
    enum custom_keycodes {
        SS_STRING = SAFE_RANGE
     };
    const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
        [0] = LAYOUT(
            // 4 buttons
            KC_KB_VOLUME_UP, KC_KB_MUTE, KC_KB_VOLUME_DOWN, SS_STRING,
            // Mouse
            QK_MOUSE_CURSOR_UP, QK_MOUSE_CURSOR_DOWN, QK_MOUSE_CURSOR_LEFT, QK_MOUSE_CURSOR_RIGHT, QK_MOUSE_BUTTON_1),
};

void keyboard_pre_init_user(void) {
// code that runs very early in the keyboard initialization
}

void keyboard_post_init_user(void) {
// code that runs after the keyboard has been initialized
gpio_set_pin_output(USER_LED);
gpio_write_pin_high(USER_LED);
uprint("init\n");
}

#if 1  // in case you want to turn off that $<em>$</em># blinking
void housekeeping_task_user(void) {
    static uint32_t last;
    static bool     on;
    uint32_t        now = timer_read32();
    uprintf(&quot;scan tick %lu\n&quot;,now);
    if (TIMER_DIFF_32(now, last) &amp;amp;gt; 500) { // toggle every 500 ms
        last = now;
        on   = !on;
        if (on)
            gpio_write_pin_high(USER_LED);
        else
            gpio_write_pin_low(USER_LED);
    }
}
#endif

bool process_record_user(uint16_t keycode, keyrecord_t *record) {
    switch (keycode) {
        case SS_STRING:
            if (record-&amp;amp;gt;event.pressed) {
                SEND_STRING(&quot;http://www.hackaday.com\n&quot;);
            }
            return false;
    }
    return true;
}

You’ll notice the process_record_user function is now in there. It sees every keycode an when it finds the custom keycode, it sends out your favorite website’s URL.

More Tips

I mentioned last time that you have to let the CPU finish loading even after the flash utility says you are done. There are some other tips that can help you track down problems. For one thing, the compile script is pretty lax about your json. So you may have an error in your json file that is stopping things from working, but it won’t warn you. You can use jq to validate your json:

jq . keyboard.json

Another thing to do is use the “lint” feature of qmx. Just replace the compile or flash command with lint, and it will do some basic checks to see if there are any errors. It does require a few arbitrary things like a license header in some files, but for the most part, it catches real errors.

Get Started!

What are you waiting for? Now you can build that monster keyboard you’ve dreamed up. Or the tiny one. Whatever. You might want to read more about the RP2040 support, unless you are going to use a different CPU. Don’t forget the entire directory is full of example keyboards you can — ahem — borrow from.

You might think there’s not much you can do with a keyboard, but there are many strange and wonderful features in the firmware. You can let your keyboard autocorrect your common misspellings, for example. Or interpret keys differently when you hold them versus tapping them. Want a key that inserts the current time and date? Code it. If you want an example of getting the LCD to work, check out the rp2040-disp branch.

One thing interesting about qmk, too, is that many commercial keyboards use it or, at least, claim to use it. After all, it is tempting to have the firmware ready to go. However, sometimes you get a new keyboard and the vendor hasn’t released the source code yet, so if that’s your plan, you should find the source code before you plunk down your money!

You’ll find plenty of support for lighting, of course. But there are also strange key combinations, layers, and even methods for doing stenography. There’s only one problem. Once you start using qmk there is a real chance you may start tearing up your existing keyboards. You have been warned.

Leave a Reply

Please be kind and respectful to help make the comments section excellent. (Comment Policy)

This site uses Akismet to reduce spam. Learn how your comment data is processed.