Each player has three card readers. They all hang off a single Arduino through an I²C multiplexer — one shared bus, and the Arduino switches between the three readers one at a time. A tag lands on a reader, the Arduino reads it, and it goes to the game over USB.
This worked great in demos and then betrayed us in playtests.
The bug
Every so often, the connection to one reader would lock up mid-read. That reader would just go quiet. No error, no crash — it simply stopped reporting cards. And the only fix we knew was to unplug the Arduino and plug it back in. Doing that in the middle of a match, in front of someone you've talked into testing your game, is exactly as bad as it sounds.
The attempt we deleted
Our first idea was architectural: pull all the reader handling out into a separate background program that talked to the game over a socket. We built a version of it. It added two new moving parts, a new way for things to fail (that program could crash on its own), and — most importantly — it didn't actually fix the lock-up. It just relocated it. We scrapped it and went back to the real problem.
What actually worked
The fix lived in the firmware on the Arduino, and it was less code than the thing we threw away:
- A timeout on the bus, so a locked-up read gives up after about 25 milliseconds instead of freezing the whole loop.
- A heartbeat. The Arduino now sends a small "still alive" message about once a second, whether or not a card was placed. That one signal lets the game tell the difference between "nobody's playing a card right now" and "this thing is wedged."
- Self-recovery. Every heartbeat, the firmware checks each reader, and if one looks dead it re-initializes it on its own.
- A manual override. There's an on-screen debug overlay (we toggle it with the backtick key) that shows each reader's status and gives us a button to force-reset one, for the rare case the automatic recovery doesn't catch it.
The result
A wedged reader now usually fixes itself within about a second, and the match keeps going. We almost never touch the USB cable anymore.
The lesson stuck with me: the honest little signal — a heartbeat — was worth more than the clever architecture. The next job is turning these prototype rigs into something that actually lives inside a table.