I wanted to make an automated bell-player of some sort ever since I was in grad school. At the time, it was difficult to find reasonably-priced microcontrollers (and most of them required expensive toolchains to program!), reasonably-priced parts (solenoids were insanely priced!), and a reasonable amount of time to work on it.

I decided this fall to finally build it. I found a source for $3 solenoids that would be perfect to strike the bells on $30 glockenspiel I found. I used an ATX computer power supply for a $30 source of high-quality power. I found a neat little board to nicely break out the power from the ATX cable into screw-down terminals where I could easily run them to power buses. Even better, these power sources are fused! I used a Beagle Bone Black, as it has lots of GPIO that I could use to control 27 different solenoids without worrying about integrating another board for GPIO expansion.

Implementation Decisions

I have quite a bit of dated experience in cross-building code in C or C++ and running it on microcontrollers, especially microcontrollers running Linux. My day to day is also in modern C++, and so due to this an my past experience, I decided that I'd use C++ if there was a toolchain already available and I didn't need to build and deploy one to the BeagleBone (and building on the target is out of the question; it takes too long, especially if you need to build external libraries). What I found was a mixed bag. There was a toolchain available, but the host's libc version was picked up, so upon deploying the built code to the target, it wouldn't run. I asked a buddy who works with BeagleBone daily what he does, and he deploys a VM with the same version of debian as what's running on the BeagleBone. I tried it, and it worked, but it seemed clunky.

I decided that I didn't want to take the time to learn the ins and outs of midi format, and to use a library to do that work for me. As a small side project, I'd be busy enough writing my own synth anyway. This was the nail in the coffin for C++. I just didn't want to fight with include paths, link paths, and toolchain paths on a project where I'm not being paid to do it. I did some quick tests after I found that the Java Runtime has a lot of midi support built in, batteries included, and the JRE runs quite well on the BeagleBone! I was wondering if the GPIO control through sysfs would be fast enough, and the answer is a definite "yes," so long as you open the sysfs files at the start of the program and keep them open. The AutoGlocken can play "Flight of the Bumble Bee" at a pretty intense pace, so it worked quite well!

I didn't use the full Midi capabilities Java provides. This was mostly because I was learning as I go, and I just didn't want to rewrite working code on this personal project to match some ideal that nobody is ever going to read. The code is pretty hacky, but it works quite well! In the end, I just use Java to parse the midi file and tell me about all of the midi events. This saves me from needing to handle every little edge case in midi files. If there's something my code isn't prepared for, I just catch the exception, log it to console, and move on. The song keeps playing.

Interface Decisions

Originally, I was happy to just ssh into the board and play a song through the command line. That got old pretty quickly. I hacked together (it's nasty) a simple HTTP application using GETs and POSTs to list its library and play songs. Now anyone can browse the library and choose a song, from their own device!

There was still an annoyance about what knowing what IP to connect to. The BeagleBone would get assigned a new IP every so often, and it was annoying to need to log into my router to determine what it was. I also didn't want to add a permanent entry in the router, because I actually do take this contraption to different places to show it off. So, I wrote a systemd service that "dings" out the last octet of the BeagleBone's IP address...

Read more »