STM32 Gaming Keypad

Written 8-28-19

Since I recently found out about the STM32 Blue Pill board, I saw it as the perfect opportunity to dive right into learning ARM. The STM32 Blue Pill board costs only about $2 on eBay and boasts a number of advantages over standard Arduino boards. The one thing I was always interested in was building a gaming keypad. Of course, I could’ve just used an Arduino Leonardo or a Teensy, but then I wouldn’t really be learning anything and it’d cost more.

After doing some research, I came across this website which was invaluable in getting started with using the STM32 as a HID device. The gist is you send reports that contain your input data in the format specified by your device descriptor. With the basics down, I got to designing the keypad.

Hardware

During the school year, I found out that Arrow was selling Cherry MX switches for $0.22 each. I immediately snatched up 25 Cherry MX Greens for myself. Unfortunately, they’ve since gone way up in price, at least for small quantities.

First, I used EasyEDA to design a board that could hold all the switches. Since I just needed a plate to add rigidity and hold switches in place, I didn’t add any traces.

Then I launched Fusion 360 and modeled a case for the board.

Next I looked into making keycaps. Due to the tight spacing I used to fit 24 switches onto the limited size of my FR4, it wasn’t compatible with standard keycaps. Trying some caps on Thingiverse, I found one that had a good fit and was relatively easy to bring into Fusion 360 and modify.

After making the PCB using my hybrid mill etch method and printing out the parts, I got to assembly. It was a bit hard cramming all the wires in, but I eventually did it and hot glued everything down.

Software

After finishing the hardware, I got onto writing the code. I started by making a device descriptor that could send two different reports, one for mouse commands and one for keyboard commands. The computer distinguishes these two reports using a report id. I used this tool to help out.

0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x06, // Usage (Keyboard)
0xA1, 0x01, // Collection (Application)
0x85, 0x02, //   Report ID (2)
0x05, 0x07, //   Usage Page (Kbrd/Keypad)
0x75, 0x01, //   Report Size (1)
0x95, 0x08, //   Report Count (8)
0x19, 0xE0, //   Usage Minimum (0xE0)
0x29, 0xE7, //   Usage Maximum (0xE7)
0x15, 0x00, //   Logical Minimum (0)
0x25, 0x01, //   Logical Maximum (1)
0x81, 0x02, //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x06, //   Report Count (6)
0x75, 0x08, //   Report Size (8)
0x15, 0x00, //   Logical Minimum (0)
0x25, 0x65, //   Logical Maximum (101)
0x05, 0x07, //   Usage Page (Kbrd/Keypad)
0x19, 0x00, //   Usage Minimum (0x00)
0x29, 0x65, //   Usage Maximum (0x65)
0x81, 0x00, //   Input (Data,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0xC0,		// End Collection

0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
0x09, 0x02, // Usage (Mouse)
0xA1, 0x01, // Collection (Application)
0x85, 0x01, // Report ID(1)
0x09, 0x01, //   Usage (Pointer)
0xA1, 0x00, //   Collection (Physical)
0x05, 0x09, //     Usage Page (Button)
0x19, 0x01, //     Usage Minimum (0x01)
0x29, 0x03, //     Usage Maximum (0x03)
0x15, 0x00, //     Logical Minimum (0)
0x25, 0x01, //     Logical Maximum (1)
0x95, 0x03, //     Report Count (3)
0x75, 0x01, //     Report Size (1)
0x81, 0x02, //     Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x95, 0x01, //     Report Count (1)
0x75, 0x05, //     Report Size (5)
0x81, 0x01, //     Input (Const,Array,Abs,No Wrap,Linear,Preferred State,No Null Position)
0x05, 0x01, //     Usage Page (Generic Desktop Ctrls)
0x09, 0x30, //     Usage (X)
0x09, 0x31, //     Usage (Y)
0x09, 0x38, //     Usage (Wheel)
0x15, 0x81, //     Logical Minimum (-127)
0x25, 0x7F, //     Logical Maximum (127)
0x75, 0x08, //     Report Size (8)
0x95, 0x03, //     Report Count (3)
0x81, 0x06, //     Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
0xC0,       //   End Collection
0xC0,       // End Collection */
// 95 bytes

After that I wrote the structs for the reports and the code to handle each switch’s keypress. Here’s my code. I don’t like the large, repetitive switch case statement, but it’s what I came up with at the time and is pretty user friendly. It’s currently setup to play Destiny 2.

Sources: (Check these out!)

Categories:

Updated:

Comments