Some early diagrams of what the keypad layout could be.
I set up a test circuit with just the ItsyBitsy and a single button to make sure my wiring logic was correct.
I also wanted to switch to a different kind of button. My MVP used the small brown breadboard buttons that we had in the lab, but I wanted to use the larger black buttons because they looked more polished and had a nicer click action. However, trying to run my code with the black buttons was producing odd behavior, where my key presses would trigger infinitely half the time and not trigger at all the other half the time. Eventually, I figured out that the black buttons were toggle buttons, not push buttons, so instead of completing a circuit while they were held down, they connected and disconnected the circuit each time they were pressed.For testing the buttons, I wrote a version of my code with just a single macro button assigned to the 'Undo' command, then connected the Arduino pin to each button in turn to test the solder joints.
I noticed that on every other press the black buttons had a tendency to trigger twice; this behavior is visible in the gif above. PK helped me open up one of the buttons to take a look at the mechanism, where we found that the reason for this was because each press rotates a metal disc so that it does or doesn't connect the two terminals of the button; when the button is in its "on" state, with the circuit closed, a half-press will disconnect the circuit and then re-connect it, thus registering as two presses. I solved this in my code by adding a cooldown timer to each button so they could only be pressed once every 100 milliseconds.Looking at the button mechanism revealed that this was a hardware problem, not a software one.
Stacking the breadboards meant I could keep my form factor and still have room for all my components.
I noticed a warning on the documentation page for the Keyboard library that reminded users to be careful of giving the Arduino unlimited access to the keyboard, because if the Arduino was endlessly sending key presses it would make it very hard to regain control. Sure enough, at one point I was trying to test my code only to discover that the Arduino was constantly typing text onto my computer, thus making it nearly impossible for me to upload new code. I managed to fix this by holding down the Arduino's reset switch until the code was just starting to upload, so that the upload would interrupt the current process and break the Arduino's control. After that, though, I found advice online from people who'd run into this issue and added a 'safety' pin or button to the circuit that would disable the program if connected to ground. I decided to add this to my implementation, and also noted that I should add a separate reset switch wired to the Arduino's RST pin because the reset switch on the board would be out of reach in the final enclosure.I designed the enclosure as one assembly in Fusion 360 to make sure the pieces all fit together, then separated the components to fabricate them.
My first version of the enclosure had some slight spacing issues: the slots for the nuts were slightly too big, so the sides weren't held in place correctly; the divots in the base didn't quite match the location of the screw holes on the breadboards; and the openings on the sides for the Arduino's micro-USB port and the reset and safety switches weren't quite the right size. I went back to my model and adjusted the dimensions, then re-printed the base and re-cut the sides, and version two worked much better.I tested the first version of the enclosure before soldering the circuit and was happy about the aesthetics of the design.
Soldering my breadboard stack was a little tricky. First, I soldered the eight macro buttons and the profile slider switch to the top board, then I soldered the microcontroller and the external safety and reset buttons to the bottom board. From there, I wrote out a list of all the solder connections I'd need to make so I could reference it at the solder bench.Top: my list of connections to solder. The macro buttons are labelled as if in a grid with rows A and B and columns 1 through 4.
Bottom: my soldering setup, with the two boards held open but as close as possible for minimum excess wire.
I used standard-thickness wire to make any connections that were on just one board (e.g. connecting the safety and reset buttons), and then I used thinner, more flexible wire to connect the buttons on top to the Arduino on the bottom. I soldered the two halves together with the top side propped open, then once everything was connected I folded the top down and gently coiled the excess wire between the boards. As I connected more of the wires, my freedom of movement got more and more limited; some of the connections were very tight. I'm pretty proud that I was able to solder everything down as well as I did!It took me several hours to carefully solder all the wires into place. I was worried I would break them by bending them too much.
I used a soldering iron to embed the screw inserts into the printed base (by heating up the metal inserts to soften the plastic around them, then pushing them into place). The divots in the base had been designed pretty close to the dimensions of the inserts, but since 3D printed parts tend to expand a little outside of their bounds, there was enough extra material for the inserts to fit snugly. Last but not least, I screwed the breadboards into the base and assembled the housing, and the hardware for my project was complete!The finished keypad.
The code ended up being a little simpler than I had anticipated -- or maybe that was just because I'd figured out a lot of it early on. I used the class functionality from my MVP to store the eight buttons to reduce code duplication when checking whether a button had been pressed, and also so that each one could have its own read pin and cooldown timer variable. Because these are toggle buttons, each one also needed its own state variable to keep track of whether the circuit was open or closed, because you check whether a toggle button has been pressed by seeing whether its state is different from what it was just a moment ago. I wanted to save the macro sequences also within the class, as a kind of dynamically variable function, but that kind of functionality doesn't work well in C++. Instead, I wrote macro functions at the bottom of my code and had each button call the appropriate function when pressed. To implement the two layers of macros, I just added a check at the top of the loop code for whether the profile slider switch was on or off. Lastly, I included the code for my safety button right at the top of the loop code, so that if a bug ever caused me to lose control of my keyboard, I could hit the safety button to delay the code for 10 seconds (which should be long enough to upload new code from my computer).