Arduino Button Mapper
A full-stack web app that turns an Arduino Leonardo into a custom USB HID controller — map physical buttons, joysticks, IR sensors, and sip/puff switches to keyboard keys or gamepad inputs, then compile and flash the firmware directly from your browser. No Arduino IDE required.
Why I built it
Accessibility hardware is expensive and inflexible. I wanted anyone — a student, an occupational therapist, a gamer with limited mobility — to be able to plug in a few buttons and have them work as keyboard shortcuts within five minutes, without touching a line of code. The existing tools either required deep Arduino knowledge or cost hundreds of dollars for commercial alternatives.
How it works
The app is split into two layers. The frontend is a Next.js 14 app hosted on Vercel — it handles the visual configuration interface, input mapping UI, key capture, and the full testing environment. The backend is a Node.js / Express server deployed on Railway via Docker. When you hit Upload, the frontend sends your configuration as JSON to the backend, which calls arduino-cli to compile a custom .ino sketch and then streams every line of compiler output back to the browser in real time over Server-Sent Events. The board shows up as a native USB keyboard — no drivers, no pairing, just plug in and type.
Overview
The core design decision was treating this like a consumer product rather than a developer tool. The configuration tab lets you add input components (buttons, joysticks, IR sensors, sip & puff switches) from a template library. Each component gets a collapsible card where you assign an Arduino pin, capture a key shortcut by pressing it on your keyboard, and choose hold vs. tap mode. Duplicate pin detection, LED assignment with conflict checking, and an integrated wiring diagram update live as you configure. The IDE tab shows the generated Arduino sketch and lets you edit it directly, with an AI assistant (Cloudflare Workers) for guided modifications. Once you are happy, Upload compiles the sketch and streams the progress to a terminal-style console. A built-in testing surface — including mini-games like Dino, Snake, and Pong — lets you verify every input actually works before you close the tab.
Process
Split the browser from machine access
Browsers can not run arduino-cli or talk to serial ports directly. I split the system early: the frontend handles all UI and configuration state, while a local or hosted Express backend owns the machine-level work. That boundary kept the interface fast and portable while the backend stayed focused on compilation and flashing.
Generate the sketch dynamically from JSON
Instead of shipping a static firmware file, the backend receives the user's configuration as structured JSON and writes a complete .ino sketch at runtime — with the correct pin assignments, HID key codes, debounce logic, hold/tap detection, and LED behavior already embedded. That means any combination of inputs produces valid, board-specific firmware without the user writing a line of C++.
Stream compile output with Server-Sent Events
arduino-cli compile and upload can take 20–30 seconds and produce dozens of log lines. Running them silently and returning success/fail felt untrustworthy, so I piped stdout and stderr through an SSE endpoint that pushes each line to the browser as it appears. The terminal pane in the UI scrolls in real time — it makes the process feel visible and debuggable rather than like a black box.
Handle port detection across every OS
USB port names differ on every platform: /dev/cu.usbmodem* on macOS, /dev/ttyACM* on Linux, COM ports from the Windows registry. The backend checks all three in a fallback chain, and if arduino-cli is installed it uses 'board list' first for the most accurate result. This made the tool work out of the box without any manual port configuration.
Build a testing loop into the interface
Configuring inputs is only half the job — you also need to verify they work. I added a controller mockup that reflects live HID state, a serial output viewer, and playable mini-games (Dino, Snake, Pong, Geometry Dash). Testing a sip/puff switch by playing Dino is a much better confidence check than staring at a port monitor.
Outcome
The result is a complete hardware configuration platform that removes the coding barrier entirely. What previously required writing C++ HID firmware from scratch can now be done in a browser tab in under five minutes. The hosted version supports over-the-wire compilation through Railway, while a local helper app enables direct serial flashing for users who want it. The project also directly informed an Instructables writeup on building a 3D-printed accessibility switch using the same stack.