TLDR
I built a wireless macropad from scratch, failed numerous times, and somehow ended up with a working prototype. This post includes ramblings about motivation, hardware, industrial design, mechanics, and software.
ACT I: Prologue (failure counter: 1)
This story begins with a failure so immense it crushed us before we even started.
I’d set out to organize a hobby project with a few friends. You know, just the usual: do something fun, creative, and meaningful. We spent weeks brainstorming, debating, and refining ideas until an inconvenient truth revealed itself: nobody was actually willing to invest a significant part of their free time in the chosen idea.
The project never even began, and I was devastated.
But in hindsight, the failure might have been a blessing. I…
TLDR
I built a wireless macropad from scratch, failed numerous times, and somehow ended up with a working prototype. This post includes ramblings about motivation, hardware, industrial design, mechanics, and software.
ACT I: Prologue (failure counter: 1)
This story begins with a failure so immense it crushed us before we even started.
I’d set out to organize a hobby project with a few friends. You know, just the usual: do something fun, creative, and meaningful. We spent weeks brainstorming, debating, and refining ideas until an inconvenient truth revealed itself: nobody was actually willing to invest a significant part of their free time in the chosen idea.
The project never even began, and I was devastated.
But in hindsight, the failure might have been a blessing. If we had started, it probably would’ve ended up as another half-finished project on the ever-growing pile of abandoned projects.
Still, I couldn’t let it go. Why did this happen? I thought about it for weeks and eventually arrived at one unsatisfying conclusion: lack of motivation. After that realization, I decided two things:
- I’d start a project on my own. The bleeding soul of an engineer heals fastest by creating something.
- I must make sure I stay motivated for a very long time, because doing things solo… takes a shit-ton of time.
But how can I find motivation and stay motivated? For a moment, I felt proud of myself for finding the “root cause” of the failure, until I realized I had just created a bigger question.
Good work, me.
Search for Motivation
Figuring this out was easy.
I just had to spend months reading books, observing the behavior of myself and others, and analyzing. Eventually, I reached a conclusion that felt both simple and profound:
I need to find activities I love so much that I enjoy them even when they suck.
For me, these are:
- Creating something from nothing, it’s like a superpower. You look here, there is nothing, now you look again: there is a gadget you can take into your hands. How awesome is that?
- Learning something new, it’s like a mental expedition. It’s uncomfortable, it makes you feel miserable, but at the end, there is always a treasure.
So what offers infinite opportunities for creation (something you can touch, smell, or even lick) while demanding learning across widely different domains?
You guessed right: consumer product development!
It’s fun; it carries absolutely no risk whatsoever! And I can guarantee that every single time you think “I’m smart”, it will definitely never backfire on you!
Which project should I not start?
So, what kind of product should I develop?
I had a leftover idea from the failed project that I liked but eventually dropped. Why abandon a “perfectly good” idea we’d already examined thoroughly? Because I didn’t want to start with emotional attachment. Falling in love with an idea makes you blind. Falling in love with solving a problem is far healthier.
So I went hunting for a new problem. That’s when I stumbled into the magical world of mechanical keyboards, and later, macropads.
A generic macropad with 11 keys and 1 knob.
Macropads are small, special keyboards whose buttons perform every kind of funky magic instead of the boring keyboard stuff. Like launching programs, controlling media or performing multi-key shortcuts. They’re compact, clever, and look simple. Perfect for a solo project, right?
How hard could it be?
Reality Check
I should’ve known I was in trouble the moment I listed the domains involved:
- mechanical engineering,
- hardware engineering,
- software engineering (embedded + desktop/web),
- industrial design,
Bonus domains:
- marketing,
- small-scale manufacturing.
The risk I took was calculated, but man, am I bad at math.
Still, it checked all my boxes. Create something from nothing? Check! Learn an absurd amount along the way? Check and double check! So, let’s go! Let’s build a macropad!
But what kind of macropad should it be? Like the other normal macropads, where you have around 10-20 buttons on a box, you hook it up to your PC, and you’re good to go? Absolutely not, where’s the mental expedition in that? Come on, I’m smart, let’s make something which is unique! So here are the initial requirements list from my notes:
- I don’t like cables, it should use BLE (but have USB as a fallback, which can be used for charging too),
- Infinite battery life (obviously),
- Mechanical switches for tactile feedback,
- Somehow make it clear what each button does (I’m bad at memorizing things),
- A clean, simple configuration tool,
- It should look simple and aesthetic (or at least don’t make my eyes bleed when I look at it),
- RGB lighting,
- Automatically change layout based on the active window.
Just a couple of buttons! Easy, right? Three or four months, tops. Oh boy, how wrong I was. Also, minor detail: I had never designed a PCB more complex than an LED matrix.
The Strategy
So here I was: underqualified, underprepared, and ridiculously optimistic. I needed a strategy that would help me navigate the vast unknown. My idea was simple:
Plan → Create → Fail → Learn → Repeat, as fast as possible.
When you explore an unfamiliar domain, failure is guaranteed; you just don’t know how or why yet. So it’s better to fail in small, controlled doses. Build the smallest possible thing that tests what you don’t know. Learn from it. Then move one inch further.
It’s slow and sometimes painful, but every time I tried to skip ahead or bundle multiple unknowns together, I’d trigger spectacular chain reactions of failure. Two small issues would team up and create one magnificent, complex, pain-in-the-ass failure.
I learned that progress often happens faster when you move in tiny, deliberate steps, as long as you take them quickly and consistently.
ACT II: Hardware (failure counter: 7)
The first problem I needed to solve: I can’t solder. Sure, I’d held a soldering iron before. I’d even managed to attach a few components without setting anything on fire. But fine-pitch ICs? 0.5 mm pins? Absolutely not.
Unfortunately, I had to solder — there was no way around it. I couldn’t afford to order PCBA prototypes from JLCPCB just to “see what happens,” and waiting weeks for each iteration would kill my momentum. Given how little I knew about hardware, I was expecting a lot of iterations. So I needed a way to solder reliably and often.
That’s how I discovered hot plate soldering. Basically it’s DIY reflow soldering using primitive tools. You just need a small hot plate, some solder paste, and a stencil (or a steady hand if you’re hardcore).
To test the method, I designed a tiny PCB with just one Cherry MX socket, a diode, and an RGB LED. It worked perfectly. Watching that board solder itself felt magical! Sure, it wasn’t even close to the normal thermal cycle of a real reflow oven, but hey, it’s not stupid if it works.
A small test PCB, a Cherry MX style switch and a socket.
After soldering. If you have good eyes, you can see I applied too much solder paste and it created small balls of danger on the solder mask layer.
Which Button Does What???
Before diving into actual hardware design, I wanted to solve the biggest industrial design challenge: How could the user always see which key does what? The macropad needed a real-time way to display key bindings.
I came up with three options:
- Display info on the buttons (or beneath transparent ones).
- Display info near the buttons.
- Display info somewhere else.
Option 1 was immediately out. Sure, the Stream Deck does it, but I don’t have access to advanced mass manufacturing technology. Embedding a screen inside a moving part sounds like a perfect recipe for cracked glass, torn flex cables, and electrical nightmares.
Option 3 felt boring, others already do that.
Option 2 seems doable: a display surrounded by buttons. Simple, and haven’t seen somewhere else, let’s go with that.
Here is my first, silly sketch of the macropad.
So You Want Battery Life?
Then came the real hardware, and a new arch-nemesis: power consumption.
Almost all of my “brilliant” requirements (BLE, RGB LEDs, real-time display updates) were directly opposed to my other requirement: infinite battery life. You can’t have rainbows and efficiency too, so something had to go.
Goodbye, RGB LEDs. Goodbye, shiny backlit display.
From this point on, everything revolved around low-power design. And by low, I mean microamps, not milliamps. The goal: a device that runs on thoughts and prayers, because moving electrons is a luxury.
I needed a low-power display, and before designing the whole hardware, I wanted to test the display technology. Without backlight, you need some tricks to make the display readable somehow. I found two technologies and both of them are unique:
- E-paper displays,
- Memory LCD displays.
The first uses charged pigments, the latter uses memory inside pixels to achieve minimal power consumption. I’ve ordered sample pieces for both.
E-paper have gorgeous static image… and a refresh rate so bad it made molasses look fast. I want a quick and snappy display, because I like quick and snappy displays.
Fortunately, memory LCDs are perfect for me. They are crisp, responsive, and delightfully retro like a Game Boy display.
So I wired it up on a breadboard and… nothing. Okay, I checked the datasheet again, still not working. Checked the protocol of the display, not working, checked again, still not working. Rewired it three times, still dead. At some point, I rewired it so many times that I physically broke the display cable.
A display with a torn cable.
Absolute disaster. Ordered another sample and waited a week.
Eventually, I found the culprit: my controller automatically disabled the SPI chip select after sending the command and before sending the data, which the display controller hated. The datasheet mentioned this but somehow my eyes refused to see it (like four times). How could I not see that?! How?!
The first functioning test of the display sample.
After recovering from the brief depression caused by the obsession to make this display work, I moved on to the processor.
My first thought was to use an Espressif processor. Their chips are great, except when you care about power consumption. ESP32 didn’t just sip current, it chugged it like an energy drink.
So I turned to my friendly fabless neighbors: Nordic Semiconductor. Their chips are basically low-power radio sorcery. BLE integrated, peripherals galore, and power draw so tiny it feels like cheating. Plus, their software support is actually pleasant. I was sold. To test it thoroughly, I ordered a Seeed Studio Xiao BLE module, which uses an nRF52840 chip.
A breadboard macropad with actual functionality!
The Boost Converter Saga
You’ve got a display, a processor, and some buttons. Done, right? Well, not so fast cowboy! You will have to do something with the battery, like you know… charging it. Also, the display needed a higher voltage than the battery could provide, so I had to add a boost converter.
At first, it looked simple: a few components, easy schematic, voltage in → magic out. I was so confident, I didn’t even bother following the datasheet’s recommended layout. Because I was smart.
Before I had a chance to test anything, my first PCB revision went straight into the trash. Why? Stupid imperial system, that’s why! The packaging of passive components can get mixed up between the imperial and metric systems. You think you’re using 0603 components? Surprise! You’ve actually designed for 0201s, microscopic parts you can’t solder by hand (and you don’t have at all). Ha-ha. Very funny.
The mk1 with miniature resistor pads.
After fixing the passive components, I finally started to test my power supply circuit. It worked… briefly. Then it self-destructed after 5-10 power cycles. No problem, I thought. Faulty solder joint. Replaced the IC, it worked again. Then boom, another one died. I dove deep into the topic of boost converters to understand how they actually work (a tiny bit too late, I know). That’s when I found my next nemesis: parasitic inductance. These tiny invisible gremlins live in your PCB layout and their favorite hobby is slapping your poor MOSFET into oblivion. Turns out my layout created such high voltage spikes during power up that they annihilated the MOSFET of my boost converter IC.
The power supply circuit, just moments before blowing up!
I destroyed five boost converter ICs when I finally arrived to this hypothesis, so how to prove it’s really the problem? It is literally printed on the circuit board, I’d need a new layout just to test this! I don’t have that kind of time, so I improvised: grabbed a thick cable, globbed on an unreasonable amount of solder, and shorted the critical paths.
And somehow… it worked. Yes, it looked like an abomination. Yes, it would make any decent hardware engineer physically wince. But luckily I’m not one.
“Fixed” version of the PCB. No blowing up this time!
ACT III: Mechanics (failure counter: 15)
3D printing the housing was the obvious choice. I already had an Ender 3 V2 and a modest track record of melting filament into vaguely rectangular shapes. My biggest worry was the durability of the fastenings. Screws and plastic don’t usually play nice, especially when you have to disassemble and reassemble the housing a hundred times because you fu.. “just want to tweak one little thing.”
Then I remembered an old laptop of mine that was literally falling apart. One of its screws kept pulling out of the chassis, until I noticed the metal inserts inside the plastic shell. The plastic around the insert has aged and simply crumbled apart (talk about planned obsolescence). Heat-set inserts are brilliant: tiny threaded brass anchors melted into plastic. You heat them up, press them into printed holes, and suddenly plastic and metal are in love.
I printed a few test blocks, dialed in the perfect hole size (which, shockingly, matched the datasheet), and moved on feeling like an absolute genius.
Now for the main event: the housing design. My goal was simple: it should look clean, friendly, and make me smile when I look at it. It’s going to live on my desk, after all. But of course, there was a bucket of secondary goals too:
- Easy to repair and assemble. I hate planned obsolescence and intentionally hard to repair products.
- Slim, and portable.
- Printable with basic filament and printers.
- Customizable: swap parts, mix colors, have fun.
But before getting too artsy, I had to face a big mechanical question: where the hell does the display go?
It is a thin, fragile component, so it should be fixed to something rigid. My first idea was to glue it to the PCB, but it would be too deep between the switches and barely visible. To solve this problem I designed a separate 3D-printed display holder, complete with (of course!) heat-set inserts. Then, staring at my board, I noticed a large empty space right under the display. Wasted area! The perfect home for a slim Li-Po battery. If I cut a window into the PCB beneath it, the whole thing could stay razor-thin. Turning constraints into features are always feels awesome.
The first functioning display on our early prototype macropad. You can notice the glue and the tapes around the display holder, adding emphasis on the early.
Next, I had to mount the PCB itself. I considered snap joints, positioning done by the housing and screws. Snaps are elegant until they snap. Plastic holders require tight tolerances for the housing. So I went with the ancient and reliable solution: screws. They are simple, cheap, and have tight tolerances. Probably this is a classic: the housing fix and PCB positioning (at leas on the X/Y axis) are done by screws, going through on four holes at the four corners of the PCB.
For a while, I just taped the battery to the PCB. It worked, technically, but deep down it hurt my soul. I wanted the battery to be fixed to the PCB, because that way the device would have three big parts: backplate, frontplate, and electronics. Also I didn’t want to glue the battery, so I designed a separate battery holder part. I even reused the display holder screws: these two parts can be fixed with the same set of screws. What a fine idea… Except it didn’t fit. Why? Because I designed the housing first and left the battery for last. Genius move.
Battery taped to the PCB.
I almost gave up and glued it down (no, never glue batteries). Then, I realized the battery holder and backplate were doing the same job: holding things. Why not use only one of them? The middle of the backplate doesn’t add very much to the rigidity of the housing, so cut it out and let the holder be the structure. Also the large empty surface of the backplate will be nicely divided! Suddenly, what was a major screw-up turned into an aesthetic feature. Victory smells like freshly printed PLA.
Battery holder part in action. The cat also helps a lot.
After a few iterations, the housing started to look like a real product, but still a bit thick. I reviewed the PCB and realized most components were on the bottom, wasting space. If I moved everything to the top, the housing could be thinner. Of course, that meant reposition nearly all parts and redrawing nearly every trace by hand. Three nights later I finished the PCB, also redesigned the housing just to realize now I don’t have enough space even for the battery cables. Finally a few slots here and there did the trick.
Was it worth it? No.
Did I sleep better? Absolutely.
Some smaller details followed. The battery switch needed a tiny coupler between the user’s finger and the fragile SMD switch. And some protection around it so it wouldn’t break again (RIP test switch no. 1). We also need a hole on the housing, where the coupler can be reached. Let’s put a hole in the frontplate, but wait, what happens during a disassembly? The frontplate should be coming off easily and the coupler should stay in one place. This means we need to limit it’s movement vertically, so a little bridge over the coupler was added to the backplate.
At first, I didn’t realize how much a battery switch could improve your quality of life.
Then came the knobs: one for the encoder, one for the four-way navigation button. The details and tolerances were so fine they pushed my poor Ender 3 V2 past its limits. Fortunately, I later got access to a Bambulab P1S, and suddenly everything looked sharp, consistent, and professional. The parts are still close to the technology limits, but I managed to get a pretty decent production yield.
ACT IV: I need help (failure counter: 15, success counter: 1)
By spring 2024, another kind of project was in the making: my wife and I were expecting our first child. That wonderful news also meant my late-night development marathons were coming to an end. Around autumn, I realized that if I wanted this project to survive, I couldn’t do it alone anymore.
So I looked for a partner.
Luckily, I didn’t have to look far. A colleague of mine, Kristóf, said yes almost immediately. From October onward, the “I” in this project became “we”. Having someone to share ideas, talk through challenges, and celebrate the little victories with was a massive weight off my mind.
Kristóf didn’t waste a second. He dove straight into firmware development and took on a huge challenge. I warned him it would be tough. Neither of us had any idea just how tough. Because now, at last, we reached the final boss: software.
ACT V: Software (failure counter: 40, success counter: 2)
The elephant in the room had been waiting patiently: software. Firmware, bootloader, OTA update, web app, desktop app: the whole package. And since we were just two developers with limited free time and a ticking baby timer, we couldn’t afford to reinvent anything. We needed to build on something which already exists and have a suitable license.
Early in the project, I’d already explored Zephyr RTOS. I liked its devicetree configuration system, its mountain of built-in services, and especially its solid BLE stack. Plus, it played beautifully with the Nordic chip I’d chosen. Zephyr felt like a complete engineering toolbox (file systems, internal messaging, drivers, debug shell, etc.), not just a scheduler with locking mechanisms.
During research, I also looked at open-source keyboard projects: QMK, ZMK, KMK, Bluemicro. ZMK immediately stood out: Zephyr-based, MIT-licensed, and already supporting the nRF52840. Sure, it was macro-heavy and hard to read, but it enforced discipline. So we built on ZMK.
And wow, what a good choice. Around the same time, the ZMK community started developing ZMK Studio, a configuration tool that aligned perfectly with our goals. Still, we needed features that no regular keyboard firmware had: display handling, dynamic layouts, runtime macros, encoder mapping. These features were missing from ZMK too. Kristóf took on the hardest one: runtime macro editing. In ZMK, macros are defined in devicetree nodes, static by design. So making them dynamic meant breaking (and then carefully reassembling) fundamental parts of the firmware. It took months, but he made it work. And it worked beautifully.
Display of the macropad, showing shortcuts for Vscode. This early hardware version did not have knob.
Just for the record, we implemented a couple smaller changes too:
- a confirmation based pairing mechanism to achieve high security level connection using the display,
- a real-time updated display showing labels of the buttons/state of the device (it was like 5 iterations to make it snappy like hell),
- handling firmware version, parameter version, and other metadata.
- last but not least we integrated the firmware update transport layer, which needed Zephyr level patching.
From my work experience, I know every embedded product has bugs. To get rid of them you need a safe update process: rollback handling, power-failure safety, signed images. I started to examine the solutions for a Zephyr based app and found my dream bootloader: MCUboot. It supports A/B partitions, signature checks, downgrade prevention and it can even handle OTA updates over BLE. There was only one tiny issue: ZMK wasn’t ready for it. At all. Dozens of failed boots later, it finally worked! And immediately raised a new problem: we needed an updater tool.
That’s when I discovered something magical: modern browsers can talk to BLE devices. That meant no installer, no drivers, no “please run this .exe as admin.”, no “boot in DFU”, just a website. So I built a web-based firmware updater in React. Three clicks: connect device, pick firmware, press update. It felt like the future, the kind with flying cars and no USB cables.
Of course, the real world had to ruin it: only Chromium based browsers support Web Bluetooth, and Windows BLE stack there is, scientifically speaking, a dumpster fire. So we needed a desktop version too. That’s where Tauri came in (idea comes from ZMK Studio): a lightweight cross-platform framework for web apps using the system’s built-in web engine. No bundling an entire browser like Electron, so our app ended up being just 6 MB, which felt like a minor miracle. In exchange you need to take care of the differences of the web engines and it uses Rust for backend. Fortunately I didn’t mind this because I already wanted to check out rust.
The firmware updater GUI. Displaying the active and the storage partition info, the available FW list on the server, and the big green button of decision.
On top of Tauri, we built our own configuration app (inspired by ZMK Studio but built from ground up to our macropad). One codebase for both web and desktop, completely tailored to our macropad, firmware updates built right in, and complete offline functionality. Because one thing I refuse to do is ship a product that dies the moment a cloud server shuts down. I’ve seen too many “smart” devices bricked by their own creators. That’s the opposite of smart: wasteful and cruel.
Early version of the parametering web page. Final version of the parametering web page.
ACT VI: Epilogue
It’s funny. I started this whole thing because a group project failed before it even began. Then I made the same thing solo, only slower, harder, and with a detailed record of every single mistake. But this time it lives. It blinks, connects, updates over BLE, and looks way cooler than it has any right to.
Somewhere along the way, a hobby project quietly turned into a real team building a real product. The kind you can use, recharge, and show off without prefacing it with “don’t mind the duct tape.”
A pile of macropads we did for friends and colleagues.
You’d think that would be enough. That we’d take a break, enjoy what we did, maybe even sleep.
Hell no. Now comes the fun part!
Small-scale manufacturing, marketing, logistics. Basically, a whole new pile of domains to increase our failure counter.
Wan’t to laugh more on our failures?