I have played World of Warcraft since I was roughly 15 years old. Imagine a teenage me, playing my then-high-end gaming PC in my parents’ house, going on adventures across Azeroth, diving deep into its dungeons to slay legendary dragons for loot. For that, I used to slam my keyboard with infinite combinations just to get that exact strike at the right time to beat the monster. But every time I finished a battle exhausted, HP bar almost depleted, I wished I had some programmable extra buttons that would one-click all of my combos instead of breaking my fingers.
Fast forward 17 years, I still play WoW. Albeit less frequently, trying my luck with adventures from time to time. But even today, after so many years and a LOT of playtime, I still wish for something extra to help my combos. The difference is that today, I have much greater tech skills. Sure, I can always buy a premade keypad off Amazon, but where’s the fun in that?
I wanted a new project that tackled everything I have taught myself in the past two years: Embedded Software, CAD, Electronics, PCB design, and on top of that I wanted it to be open-source. Bonus points if it actually teaches me something new. Therefore, I have set the goal: I’m going to create the most bad-ass, super-configurable, multi-function macro-pad! And it must be solder-free. And both physically and digitally fully customizable (OK enough requirements).
Mechanically, the point of this project was to create a macro-pad that can be built in any form or size, so users can build their device with any number of keys they wish, in any shape they want. Additionally, I wanted the ability to use any kind of mechanical key-switches and for it to be solder-free. Therefore, all components must be snap-fitted or connected through some sort of clipping mechanism.
As for the software, the macro-pad should be easily configurable through a basic UI that will detect the size and form of the macro-pad automatically and allow the user to choose the colors and functionality of each key individually.
Nothing Ever Goes According to PlanI have decided to tackle the software part first, as this is my strong suit. I wanted the macro-pad to connect to the PC via USB as an additional keyboard. For this, I have purchased a few Arduino ProMicro16. The ProMicro uses an ATmega32U4 which has USB built-in to the chip and connected to the micro USB of the board. Useful.
To understand the next decision, one must understand how keyboards work: Keyboards are usually constructed as a matrix of keys. To identify which key was pressed, each intersection of a row and a column yields a key index. The keys are soldered on this matrix and by pressing down a key, a circuit is closed between the key’s row and a column and so the specific key is identified. Unfortunately in my case, I cannot know how much keys the macro-pad consists of or how they are connected, therefore it is impossible to use this design.
To tackle this, I attached a microcontroller to each key. I used an ATTiny85 to control each key switch individually to uniquely identify each one. This creates a “network” of microcontrollers which are connected to each other via I2C lanes. To uniquely identify each key switch, each microcontroller receives a unique ID at boot time. At startup, the Arduino initiates an ID allocation routine and allocates a unique ID to each microcontroller within the “network”.
To do this, each microcontroller allocates two additional pins on top of the I2C bus: TOKEN_IN pin and TOKEN_OUT pin. This creates a “token-ring” between the Arduino and all the microcontrollers, synchronizing the communication. To start off, the Arduino raises the TOKEN_OUT pin to HIGH, which is in turn connected to the first microcontroller’s TOKEN_IN pin. Whenever the TOKEN_IN line is HIGH, a microcontroller “owns” the I2C bus and it is able to use the currently transmitted ID. Then, it echoes back the ID over the I2C bus and raises its TOKEN_OUT pin to HIGH so the next controller in the chain can receive the next ID.
In practice, there are a lot of hiccups when working with embedded devices. Just to name a few: setting up the build environment for the ATTiny85 resulted in a broken toolchain. The internal clock was configured wrong, so a delay(1000) sleeps for 2 or more seconds.
To top this, the I2C bus is quite slow, along with this tiny microcontroller which runs at 16MHz at best, each ID needs to be retransmitted a few times until the ATTiny85 receives the signal and ACKs it.
More-so, as I2C is a master-initiated protocol. Therefore, for the ID allocation routine, the Arduino is the master. On the other hand, during normal operation, key switches are pressed asynchronously, which would require them all to act as I2C masters. This raises questions such as: how to deal with bus contention, who drives the clock etc.
To solve this, after setup, the Arduino becomes the I2C slave, while on each key press, the relevant microcontroller becomes an I2C master for a brief time, transmitting its ID back to the Arduino, then goes back to acting as slave. This allows a (mostly) clean data transmission along the I2C network and so only a single entity on the bus acts as I2C master at all times.
Doing things that have a predictable course is boring. This is why every project must contain something I don’t know how to do. And this project excelled in that department. A lot of questions needed answering right from the get-go: how does a key switch connect to a board solder-free? How do two keys connect to each other? How small can the board be and still accommodate all the components? How do backlit key switches work? Does it require a special key switch? How to fit an LED behind a key switch?
For the board-to-board connectors, I have settled on connecting all the modules to one another through ribbon cables, which are small, flexible and carry as much lanes as I’d practically want.
The key switches were a whole other story: apparently there are unique components for that (the L shaped black component in the picture below) that allow solder-free, hot-swappable key caps. As for the LEDs, I used an SK6812 which is reverse-soldered. This allowed me to solder it from the back of the PCB and create a small window for it to shine under the key cap. This trick made it flush with the top part of the PCB, directly shining through the key switch.
As for the PCB, it turns out it is just like those kiddie riddles where you have to connect the dots of two groups without the lines intersecting with each other. When it comes to PCB design, traces carrying different signals should never intersect. As this project has many signals (I2C, LEDs, Token lines and more), I had to draw them again and again to minimize vias to prevent intersections. I think my next software project should be something that does that for me. Any takers?
Once the hardware is finalized, ordered and shipped, it is time to create the case that will hold all the modules together. After all, right now they are more fitting as Christmas lights than as a macro-pad.
For this, I have tried a number of designs. The first ones proved to be too chunky. The keys were positioned so high that it was uncomfortable pressing them. Also, it appears that keyboards are slightly angled by 5–15 degrees. So the completely flat approach was scrapped. On top of that, the ribbon cables mangled one another when two modules were adjacent to each other, sometimes shorting themselves.
Creating an angled design and lowering the module profile fixed a lot of the issues I stumbled upon previously. I have angled the whole design by 5 degrees, remade the locking mechanism that connects the modules to one another and shrunk down the ribbon cable holes for a slimmer design.
As this is certainly not my strong suit, for the desktop app I have turned to my faithful friend Python. The application communicates with the Arduino over a serial connection using a configuration protocol I have designed.
As for the app itself, I wrote it as a webview application. I hope all the web developers reading this will excuse me for writing this entire app using plain old JavaScript with a bit of jQuery for the dynamic elements. This is certainly not the best looking app, but it definitely works.
After months of back-and-forth designing, coding, testing, redesigning, printing, redesigning, PCB fabricating, redesigning and some more redesigning, I have finally settled down on version 0.2 which I think looks amazing!
It's been a wild journey. but I have learned so much from it. It had everything: Electronics, PCB design, Networking, CAD, Embedded development and Application development. And on top of it all, my World of Warcraft Blood-Elf Monk can now slay dragons using my own bad-ass, zero-solder, super-configurable, multi-function macro-pad!
LinksPaws Module — https://github.com/gili-yankovitch/paws-module
Paws Arduino and App — https://github.com/gili-yankovitch/paws-master
Twitter — @GiliYankovitch
LinkedIn — @YankovitchGil
Comments