Update, May 2020: Here’s the video of a talk I gave at !!con about this build!
You know how everybody hates on recipe blogs that start posts with stories even though a) it’s good for copyright and b) stories can tell you a lot of things about humans and the why and how of their food, well if that’s you then you’re going to hate this tech recipe! Or you’ve already scrolled past this.
So. Last year, I saw this tweet:
I have never done any hardware tinkering, but I immediately wanted to make one of these for my kids. The oldest is pretty good with making requests from voice assistants, but the little one isn’t there yet and we have some other problems anyway: preferring edited or specific alternate versions, difficulty requesting things that aren’t in English, and often a lot of people in the house making a lot of noise, which leads to yelling and garbled results. (See what I did there, I defined the problems I was trying to solve for, it’s like I’m good at my job or something.)
Since I have a few Raspberry Pis I’ve acquired as conference speaker gifts, I wanted to try to use as many existing parts as possible. My first thought was that I would like to play audio through an unused Google Home Mini, but it doesn’t have aux-in and honestly I just could not decipher the documentation around Google Assistant+Actions and those type of generic names and entangled services make things highly unsearchable. I decided to go with regular audio output and figure I can keep iterating with software and/or hardware over time.
The original maker doesn’t seem to have ended up doing a write up of what he did, so I took a look at other similar things people have built. I found this blog post, which took me to their code on GitHub. Availability and pricing as well as a love for the more tactile experience told me I still wanted to use swipe cards rather than RFID, and I don’t really know Python and wanted to make something with as little configuration or dependency management as possible so I figured I was on my own code-wise. That said, that blog post gave me a general direction for approach and led me to Pi MusicBox, which also serves as the base for my player.
I finally ordered the materials off Amazon mid-December for my spouse to bring back from his trip to the US, and got to work because I only had a week to get it all done. Here’s my planner page I worked on to get a head start while I waited:
First up was testing the card reader and setting up the writer, which was not a smooth process. I had to find the writer software online, because who has a CD drive anymore? The driver took forever to finish installation for some reason, but once it got there it seemed to work. Except that the card reader wasn’t returning anything. I tried a regular old bank card (I used one for a defunct account just to be extra safe) and only got numbers, no name, even though having done my time in retail I knew my name should show up in the magstripe data. I tried writing data to a fresh card in all 3 tracks and would only get tracks 2 and 3 from the reader, or sometimes even just track 3. Seems like a malfunctioning card reader (I’ve also occasionally noticed ghost input now that the jukebox has been running for a few days). Update in 2020: I got a second card reader and it works correctly.
Because tracks 2 and 3 can only contain numbers, I decided that the best route to go would be to just encode numbers sequentially and grab that line from a list of Spotify URIs for playback. My first few tests got nowhere – successful write, but wouldn’t read. I tried finding a new card reader in person but to no avail. I tried test data again and… it worked? That’s when I realized that my test data had three numbers, so I tried putting
001 and voilà, that fixed it. After thinking through it a bit, I actually realized that this was the best route anyway – if a Spotify URI stops working, I can swap it out, and the usual ISO encoding for cards is all-caps anyway, and the URIs are case-sensitive.
I decided to put the number on all 3 tracks since the reader seemed to unpredictably read either one or both of tracks 2 and 3 and I might replace it later. This was pretty easy in the card writer software, which allows you to create cards from a file, which I generated using a quick and dirty Bash script. I also made a card with
999 as a special case for toggling playback – there is also a local web server you can access for various functionality including shutdown/reboot, but a basic toggle seemed useful for the kids.
The script itself is extremely small – it does a little Bash magic (parameter expansion!) to extract the first number it comes across, strips off any leading zeroes, and then gets the indicated line from a list of song URIs if it’s not a special case card and plays said song. I stressed about it for a long time but in the end, my first working run was less than 10 lines of code.
For labels, the initial inspiration indicated that they made a React app that pulled in the data to make printables, which is super cool! I, however, am a weirdo who despite being a programmer is still often faster knocking stuff out in Photoshop/Illustrator as opposed to writing a whole app. I also wanted to use artwork besides album covers for many of the cards so that my 2 year-old can differentiate between tracks from a Mother Goose Club or El Reino Infantil album and pick what she wants – this has already proven successful 🙂
I did not hand-make a fancy box, though I might end up 3D printing something eventually, but I did discover that some of the drawer organizers I use were a great size and are good enough for now. Here’s what I ended up with for Christmas morning:
Happy holidays, everybody 🙂 pic.twitter.com/dsB2i323SD— Helen 侯-Sandí (@helenhousandi) December 25, 2019
I enjoy posting about the process of building or learning things, and I’ve had a lot of people asking me about this, so I think it makes sense to blog about the process and the result. So now that you know why I decided to build a jukebox and how I arrived at the choices I made, let’s get into the step-by-step.
- Raspberry Pi (any model, 2+ recommended)
- MicroSD card (1GB+)
- WiFi adapter if needed for the Pi
- Magnetic stripe cards (mine)
- Magnetic stripe card writer (mine)
- Magnetic stripe card reader that emulates keyboard input (mine, the first one I had so you can avoid that one because of the issues)
- Spotify Premium account (protip if you have kids: use a family account and set up individual accounts for devices so you don’t run into playback limitations and you keep your recommendations somewhat more sane, I name them things like Alexa so if you ever see a reference to an Alexa Sandí associated with me please know that is not my actual child and somebody has sold or stolen my data)
- A computer that can flash SD cards, run the card writer driver and software (I’m on a 12″ MacBook), and if you want to use my templates, access Adobe Illustrator
- USB keyboard and HDMI monitor for initial setup (I used my Magic Keyboard with a cable and a TV)
- Whatever you need to print 2×3″ labels (I had them printed onto adhesive sheets at Office Depot and cut them to size myself)
- Set up Pi MusicBox. I found their documentation to be perfectly adequate, just note that when setting up Spotify you have to authorize and generate tokens that are copied into the config file.
- Boot up the Raspberry Pi with the card reader plugged in and log in. Test Spotify playback by running
mpc add spotify:track:7GhIk7Il098yCjg4BQjzvb && mpc play
- Optional but recommended: fork my GitHub repo so you have your own copy and can keep your song list edits there. After forking, be sure to edit
jukebox.shto point to the
songs.txtin your own repo.
curl https://raw.githubusercontent.com/helen/swipe-jukebox/master/jukebox.sh > jukebox.sh(substitute in your own username if you’ve forked the repo above).
rootto auto-login on boot and
/root/jukebox.shto run after login so the setup can live headlessly going forward. These instructions work well, noting that the user for MusicBox is
bash jukebox.sh; this will download the song list referenced in the script as
songs.txt. Enter a number like
1at the prompt that says
Swipe:to ensure the script is working as expected. If you want to quit the script, hit
songs.txtto your liking. I have found the easiest way to do this is to create a Spotify playlist and then select-all, right click, go to the Share menu item, select Copy Spotify URIs, and paste the result into the file. I also decided to paste the results into a Google Sheet along with the titles and artists extracted using a playlist converter tool so I have them conveniently numbered and saved separately.
- Encode the cards. I found the easiest way to do this was to write them from a file, which just loads up each record sequentially and writes it to the next card you swipe. There’s both a sample file that goes up to 250 in the repo, as well as the Bash script I used to generate that file in case you want to go higher or if you get interrupted and need to make a subset. I wrote the number for each card on the back and also tested every card in the reader after writing, which at this point should trigger the appropriate playback.
- Create the labels for the cards. I recommend you make them 2″ x 3″. There is an Illustrator template in the repo for those of you who are graphically-inclined. The intended artwork area is 1.5 inches square.
- Print the labels for the cards and stick them on – I tested each individual one again before putting the label on because I am extremely particular, but if you make a mistake it’s fine, you can change the order of the URIs in the
songs.txtfile (and your playlist, if using one).
- Shut down the Raspberry Pi – I find it easiest to do this by navigating to the web interface (typically at
- Box everything up and plug it in again to test the whole experience. When it’s headless, you’ll want to wait a minute or two for the green light to stop blinking so much for everything to be ready to go.
If you build one, please let me know! I’d also love to hear any tips or tricks or issues you come across if you try following along with this – I’m a software developer, as far as I’ve ever experienced there are always going to be bugs 🙂