Macro Keyboard

Do you know Elgato Stream Deck? It is a fantastic macro keyboard consisting of programmable keys displaying custom images. It allows you to automate numerous things including entering key sequences, running programs or websites, controlling specific software and more. Moreover you can extend its capabilities by writing your own plugins. And don’t get fooled by the “stream” in its name. Even though it was initially created for content creators, it serves fantastically as a productivity tool.

There’s only one downside of this fantastic keyboard: the price. The most reasonable, 15-key version costs around 150€, which sometimes may be a deal breaker. So I decided to create such macro keyboard on my own.

Designing buttons, which display an image underneath is not simple (Stream Deck actually does it with only one screen in quite an ingenious way). I saw one DIY approach named FreeDeck, but ultimately I decided that this is not the way I would like to go. Firstly, it has only 6 keys (because of screen board sizes), secondly it treats goldpins as springs – every screen press causes stress on the pins, which may eventually start cracking. Also, LED/OLED screens tend to wear off in time – especially if they display the same image all the time. Stream Deck counters that by allowing you to set a delay, after which the screen is turned off. You can turn it back on by pressing any of the keys.

Finally, I decided to take two separate approaches.

One is using an e-paper screen, which displays labels for keys. However, they are not displayed underneath the keys themselves, but instead I show them above them, in the same layout as the keys. So effectively, top-left icon describes top-left key and so on. Surprisingly, this works quite well, you quickly learn, which button represents which feature.

Macro Keyboard with e-paper

Another approach is a screen-less keyboard. Apart from an obvious lack of the e-paper screen, the design is exactly the same.

Macro keyboard without screen

I figured, that the device without some form of displaying labels for buttons represents a fraction of its capabilities. The sole purpose of using such keyboard is so that you don’t have to remember things (eg. shortcuts), so now having to remember which key does what hinders its usability. Moreover it is hard to use more than one layout, because you have no way of knowing, which layout is currently active. So to counter this problem I modified the software for the keyboard and also wrote a desktop application, which acts as a satellite. It connects with the keyboard with a COM port and displays on screen contents of what otherwise would be shown on the e-paper display. Moreover, if you hold any button for more than half a second, screen is temporarily displayed for you, so that you can quickly check, which button you need.

Macro Keyboard satellite application

The Stream Deck requires a satellite application to be running at all times. Moreover, it stores layouts of buttons on the computer, so if you connect the device to another PC, you will have a completely different set of keys there. Obviously, you can synchronize layouts between devices, but this requires some more effort.

On the other hand, in my case, the layout is stored on the SD card, which you put in the keyboard’s reader. Thanks to that, you can use the same layout regardless of which machine you connect the keyboard to.

I made my keyboard opensource (both software and hardware). You can build your own unit by following the tutorial.

Bill of material

Components required to assemble the keyboard are the following.

  • 1 x Macro Keyboard 1.1 PCB – approximately 8 USD + shipment (I ordered mine from JLCPCB)
  • 1 x Teensy 3.5, 3.6 or 4.1 – approximately 25 USD
  • 12 x Cherry MX switches – 1 USD per switch
  • 12 x Keycaps for keys – can be 3D-printed or purchased separately
  • 2 x 24-pin female goldpin strips – < 1 USD
  • 1 x encoder with button (I used 30 steps per rotation, 6mm diameter, 15mm height) – < 1 USD
  • 1 x encoder cap (up to 30mm of diameter) – < 1 USD
  • 5 x 18mm (incl. head) M3 screw + nut for the case
  • 1 x MicroSD card of as small size as you can buy (128 Mb will do just fine)

If you want to assemble version with e-Paper screen, you need as well:

  • 1 x angled 8-pin male goldpin strip
  • 1 x Waveshare 2.9″ 296×128 black & white e-Paper display (code: 12956). DO NOT buy 3-colored versions! – 20 USD
  • 3 x 15mm (incl. head) M3 screw + nut for the screen

Note that if you use my designs, you may add the screen at any point in the future. Just buy missing parts, solder pins for the screen, connect it, assemble everything again and burn different version of the firmware.

If you want to use my enclosure, you also have to either 3D print it or order a print.

Pre-requisites

Clone my repository on Gitlab.

Board

In the cloned repository, in Board folder you can find a couple of files describing the PCB. You can either open them using Autodesk Eagle or simply use provided Gerber files to order a board.

Macro Keyboard PCB

Assembly

You have to solder the following components:

  • Female goldpin strips for Teensy
  • Cherry MX keys
  • Angled male goldpin strip for e-paper screen
  • The encoder

Fully soldered board should look like the following:

Soldered PCB

If you are assembling version with screen, screw the screen into its case with screws, which are provided with the screen itself (M2s). Remember to connect the screen’s plug before screwing it. Afterwards, screw screen’s case.

Then connect the screen to the board. Use the descriptions on the board to connect proper pins (colors of wires may differ from the version I used).

Screen assembled

Screen case provides enough space to push the wires inside, so they don’t clutter the main board.

Afterwards, place the main board on the bottom part of the casing and afterwards place the top part of the casing on the board. Case is designed in such way, that screws go through the top part, then through dedicated hole in the board and then through the bottom part. Remember to insert nuts into their slots before screwing the casing.

Assembling the case

When everything is screwed on, put Teensy into its socket, put on the keycaps and encoder cap. You’re done with the assembly!

Programming

MacroKeyboard folder contains application for the keyboard itself. Use Arduino Studio (along with Teensyduino loader) to burn the software into the board.

Software is designed for both screen and screen-less version. However depending on which version you want to use, you have to set one thing in the code.

Version with e-paper screen

Beginning of MacroKeyboard.ino file should look like following:


// *** Configuration start ***

// Define to send screens via serial port
// Undefine to send to e-paper display
#undef DISPLAY_SCREEN_SERIAL

// *** Configuration end ***

Version without screen

Beginning of MacroKeyboard.ino file should look like following:


// *** Configuration start ***

// Define to send screens via serial port
// Undefine to send to e-paper display
#define DISPLAY_SCREEN_SERIAL

// *** Configuration end ***

Keyboard definition

Macro keyboard loads screen layouts from a binary Keyboard.def file. To simplify creation of this file, you may define layouts in XML format and then use the MacroKeyboardGen console tool to convert XML to binary.

The sample XML layout definition may look like following:


<?xml version="1.0" encoding="utf-8"?>
<Definition xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Screens>
    <Screen Id="0">
      <Items>
        <Item Header="Copy" Icon="Copy.png">
          <Actions>
            <ModifierDown Modifier="Ctrl" />
            <KeyPress Key="C" />
            <ModifierUp Modifier="Ctrl" />
          </Actions>
        </Item>
      </Items>
    </Screen>
  </Screens>
</Definition>

The definition consists of the following tags:

Definition
Description Root XML tag containing the whole definition
Children Screens
Screens
Description Contains a list of Screen entries describing screens, which may be displayed either on the e-paper display or on screen of the PC (by the satellite application)
Children Screen (multiple)
Screen
Description Describes a single screen
Attributes Id (required) should be a unique identifier for a screen. You must supply screen with Id 0, it will be treated as a main one, displayed after booting up the device. You can assign identifiers to other screens as you wish, as long as their Ids are unique.
Children Items
Items
Description

Contains a list of items, from which every one relates to a button on the keyboard. Keys are described row-first, in the following order:

1 2 3 4
5 6 7 8
9 10 11 12

Each item contains information about its look and actions, which will be executed when button is pressed.

Children Item and EmptyItem, in total up to 12
Item
Description Describes a single item (its appearance and behavior)
Attributes

Header (required) is the text displayed underneath icon on the screen. There is no limit for length, but too long labels will overlap.

Icon (required) is the path to icon displayed for this item. Icon may be anything bitmap-ish (.png, .bmp, .jpg) and will be automatically converted into monochrome (black & white) if it is not already in such format. Path is relative to where input file is placed.

Children Actions
EmptyItem
Description An empty placeholder. Use if you want to omit some item definitions to eg. position an item in specific place. Pressing button, which is represented by EmptyItem has no effect.
Actions
Description Contains a list of actions, which will be executed, when a key is pressed. Actions are always executed in order in which they are defined.
Children ModifierDown, ModifierUp, KeyPress, SwitchToScreen, ToggleEncoderMode, Sleep, SwitchToEncoderMode, Execute (multiple)
ModifierDown, ModifierUp
Description Causes the modifier key to be pressed or released. Use to define keyboard shortcuts, like Ctrl+C.
Attributes Modifier (required) Modifier key to be pressed. See list of valid modifiers below.
KeyPress
Description Causes the key to be pressed and released.
Attributes Key (required) Key to be pressed. See list of valid keys below.
SwitchToScreen
Description Switches the current screen to one with matching Id. This allows you to create complex hierarchies of screens with pages and folders. There is no built-in way of returning to previous screen, so always remember to provide some kind of "back" or other navigation key, or you will be stuck on a screen.
Attributes Id (required) Id of the target screen. Screen with such Id must exist.
ToggleEncoderMode
Description Cycles encoder behavior between arrow keys, mouse movement and mouse scroll. Press the encoder to switch the submode (eg. vertical/horizontal keys, mouse movement and scroll)
SwitchEncoderMode
Description Switches to a specific encoder mode.
Attributes Mode (required) Target mode. 0 – Arrow keys, 1 – Mouse, 2 – Mouse scroll.
Sleep
Description Clears the screen. Useful if you want to turn the device off, to reduce e-paper scren wear. You can exit sleep mode by pressing any key.
Execute
Description Executes given command (eg. starts a program or opens a webpage)
Requirements This action requires the satellite application to be running on the PC.
Attributes Command (required) Command to be executed.

Valid modifiers

ctrl shift alt gui lctrl
lshift lalt lgui rctrl rshift
ralt rgui

Valid keys

system_power_down system_sleep system_wake_up media_play
media_pause media_record media_fast_forward media_rewind
media_next_track media_prev_track media_stop media_eject
media_random_play media_play_pause media_play_skip media_mute
media_volume_inc media_volume_dec a b
c d e f
g h i j
k l m n
o p q r
s t u v
w x y z
1 2 3 4
5 6 7 8
9 0 enter esc
backspace tab space minus
equal left_brace right_brace backslash
non_us_num semicolon quote tilde
comma period slash caps_lock
f1 f2 f3 f4
f5 f6 f7 f8
f9 f10 f11 f12
printscreen scroll_lock pause insert
home page_up delete end
page_down right left down
up num_lock keypad_slash keypad_asterix
keypad_minus keypad_plus keypad_enter keypad_1
keypad_2 keypad_3 keypad_4 keypad_5
keypad_6 keypad_7 keypad_8 keypad_9
keypad_0 keypad_period non_us_bs menu
f13 f14 f15 f16
f17 f18 f19 f20
f21 f22 f23 f24

Version without screen

If you want to use version without screen, compile and run application in the folder MacroKeyboard.Satellite. Note: it requires .NET 6 to run (you can download and install it freely though). After starting, right-click its icon in the System Tray and choose proper COM port, under which your Teensy is available. If you are not sure, which one is it, simply try all one by one until you find the proper one.

Macro Keyboard - pick port

To check if port is valid, press and hold any key on the macro keyboard. If you chose the proper port, keyboard’s screen should be displayed just above the System Tray.

Macro Keyboard satellite application

Done!

If you reached this step, congratulations! You have just assembled your own macro keyboard. If you did, make sure to send me a message with photo, I will gladly show a gallery of successful builds. Also, if you have trouble building your keyboard, leave note in the comments, I will try to improve the description or answer any questions you may have.