Building screenless digital jukebox for my daughter

2025-09-1016:5220rdeaton.space

Using NFC tags, an ESP32, and Home Assistant to let a toddler control their own music player

data-check-icon= >

NFC tag scanner after some decoration First set of tags to come off the printer

Like many parents in 2025, I'm extremely concerned about the impact of modern technology on the developing mind . The internet is not the same place as it was in the 90s and 2000s. As a species, humanity has invested untold billions of dollars engineering the greatest and most profitable attention sinkhole the world has ever seen, consequenes be damned as long as it puts eyeballs in front of ads and billions in profits in pockets. Stories of radicalization, echo chambers, epistemic bubbles, pervasive fraud, spam, and AI slop have been written about every single majorm. Even music streaming services, including the ones that we've used in my house like Spotify and Apple Music, both of which I've seen first hand.

But my daughter loves music, and even before she could speak, she would sign to ask for particular songs to be played or to change the music. We're a mostly no screen household, we don't watch TV or videos on our phones or computers (with the occasional home video excepted), but we do use them to control music and other tasks around the house, and we'll sometimes let her try out devices if she's being interactive and discovering. Annoyingly, even letting her learn to press play, pause, next, etc. in an app like Spotify has the risk of quickly turning into watching music videos. Apps like Spotify have started auto-playing videos when you visit an artist's page and other random interaction points unless you dig into the settings and configure a low bandwidth mode so it won't try to load those videos, and these are always but a swipe or smash at the screen away, and kids learn quickly.

So, I wanted to give her a way to play and control her own music, but not from a screen. It also can't involve relying on her parents, given one of her favorite phrases lately is "all by myself.". At almost 2-years old, the vinyl collection is out of the question for a good while. We could try getting a CD player, but it's hard to even find some content you want on CD these day. There are devices like the Toniebox and Yoto Player that come recommended highly from various other parents in the world. The Tonie stuff to me, unfortunately, seems a bit expensive for a lot of figures, and rather limited in the content available (there's no rick roll Tonie figurine, I checked. Get on that, Rick). The Yoto is at least a bit more reasonable on the custom content pricing where you can upload your own mp3s, but still on the expensive side, and both platforms suffer from a problem that I hate: they stop working whenever the company takes down their servers. Some efforts to reverse engineer the Toniebox (1, 2) and Yoto, but not far enough along to not need to reverse engineer and DIY it a bit... and if I'm going to DIY it anyways, why not build my own music player?

Let's skip more philosophy on how I think this all should work, and let's look at what came out of that idea.

Selecting hardware and software

My vision for a quick and dirty first version of this started from a couple simple goals: Use stuff I had lying around, and use NFC cards/tags that could be decorated to link to songs.

I had some ESP32-based D1 Mini clones lying around in a drawer, and remembered reading somewhere that ESPHome had come a long way since I bought these for another project a couple years ago . I already had a copy of Home Assistant running in my homelab to monitor my home solar system, and seemed capable for at least a first pass.

The one component I knew I'd be missing at first was an NFC reader. A quick glance through the supported peripherals in ESPHome led me to the rc522, so I quickly ordered a few back before the end of the de minimis exemption, along with some 50 packs of NFC 215 stickers.

For audio output, we've got some HomePod minis on the network already, and with a quick check that they should work with HomeAssistant, I figured we could just start there for audio. I did do some quick investigation on open hardware / open source networked speakers, but I wasn't impressed with the options. Sonos somewhat famously bungled an app update so bad that it took down a CEO and may have irreparably damaged the brand's reputation. Hardware from Google is out of the question given their track record on long term support of... well anything really. raspiaudio has a set of hackable speakers, but reviews on the build quality and audio quality seemed fairly consisently disappointing. Maybe in a few more years...

First Prototype

A breadboard prototype of the D1 Mini clone hooked up to an RC522

After a short detour to read some datasheets, we're ready for a quick breadboard test to wire up the components. Very simple here, just pin to pin, no extra resistors or circuitry necessary. Now we need some firmware. Fire up ESPHome in a docker container on a random linux machine:

services:
 esphome:
 container_name: esphome
 image: ghcr.io/esphome/esphome
 volumes:
 - ./config:/config
 - /etc/localtime:/etc/localtime:ro
 devices:
 - /dev/ttyUSB0:/dev/ttyUSB0
 restart: always
 privileged: true
 network_mode: host
 environment:
 - USERNAME=admin
 - PASSWORD=butts

and write a quick config for the D1 mini

esphome:
 name: d1-mini-rfid
 friendly_name: d1-mini-rfid

esp8266:
 board: d1_mini

spi:
 clk_pin: D5
 miso_pin: D6
 mosi_pin: D7


rc522_spi:
 cs_pin: D8
 on_tag:
 then:
 - homeassistant.tag_scanned: 'return x;'

logger:

api:
 encryption:
 key: "<redacted>"

ota:
 - platform: esphome
 password: "<redacted>"

wifi:
 ssid: wifi_ssid
 password: wifi_password

and just like that, I've got tag scanned events showing up in my Home Assistant Logbook. With that nice little ota block, after the initial flash over USB, I can update firmware over the air. There was a little trial and error as I initially missed that the D4-pin is hooked up to a built-in LED, but otherwise it went very well. A 20 line yaml file is definitely the simplest firmware I've ever written.

Next, a breadboard is not quite the form factor for a two-year-old, so it's time to make some sort of case. I had the cardboard box from a Thunderbolt to NVMe adapter that happened to be taking space on my desk. I grabbed a second D1 mini and RC522 and soldered on some right angle headers this time, to suit the flat box. Out of pure laziness, I kept using some dupont pins, but we can pretend it was with foresight in case I decide to update the design later. To make sure that my future self has something to curse about, I decided to forget about color-coding the wires, and instead just used whatever color happened to be on top.

I used an xacto knife to cut a little slot just wide enough for a USB cable, but not the microusb plug itself, so that the user can't just pull the plug out. I used a bit of hot glue to keep the NFC reader in a predictable spot, some card stock, and a sharpie to mark a good spot to place the NFC tags. Luckily, I wasn't completely alone for this effort; I got a bit of help gluing the card stock down.

Getting a bit of help gluing card stock

Automation with Home Assistant

So, now we need some audio to send through. I initially thought I'd use something like Music Assistant, but the Apple Music integration involves grabbing your cookies from another auth'd session, and I said no thank you.

I next thought I could just use my Apple Music subscription to play songs via deep links, as I'd found some examples of that in my research, but this ended up being a bit of a dead-end: Apple TVs can follow those deep links and launch them, but not the HomePod Mini, at least through the HomeAssistant integration as written today.

Fine, fine, I just want to test things, so what can we do? The HomeAssistant media_player.play_media can take a URL to an mp3 to play, and I've already got a NAS that's serving up static files over HTTPS on my network. We just need an audio file to test with, and so we turn to the best thing that's happened to the Python packaging ecosystem in decades: uv. A quick


and we've got a file to test with. Time to write an automation for Home Assistant.

A couple rounds of trial and error and a lot of yelling about another tool making another crappy yaml based programming language, and we get a first test automation in place. I chose to just write the YAML myself so that it's easy to check into my git repository and programatically update in the future.

alias: NFC Jukebox
description: Play scanned NFC tag
triggers:
 - alias: rr
 id: rr
 tag_id: 04-DB-F7-0C-C3-2A-81
 trigger: tag
conditions: actions:
 - action: media_player.play_media
 target:
 entity_id: media_player.bedroom
 data:
 media_content_id: https://nas.lan.rdeaton.space/jukebox/rr.mp3
 media_content_type: music

Time for our first end to end test.

Hopefully, you saw where that was going from a mile away.

Adding MVP Features

Before handing this over to my daughter, just a few more things left to do. The automation in HomeAssistant does not finish until the song plays, so you have to wait until the very end. We can fix that with a quick mode: restart in the automation's yaml. Since the ESPHome event is on tag scanned, you can restart a song by moving the NFC tag away and bringing it back. That interaction seemed fine, and my daughter ended up understanding it immediately.

Now, we need a way to let her stop the music. I grabbed a second NFC tag and updated the automation a bit to support a stop tag. I also wanted to make sure I could easily add more tags, so I added a third tag with another song and used the templating feature of Home Assistant to keep things relatively tidy, since I control the URLs. Finally, since the HomePod is out of her physical reach, I tested a couple volumes and picked a sensible volume to force on each scan.

alias: NFC Jukebox
description: Play scanned NFC tag
triggers:
 - alias: rr
 id: rr
 tag_id: 04-DB-F7-0C-C3-2A-81
 trigger: tag
 - alias: stop
 id: stop
 tag_id: 04-89-76-A3-72-26-81
 trigger: tag
 - alias: dinosaurs
 id: dinosaurs
 tag_id: 04-3F-5B-5D-BF-2A-81
 trigger: tagu
conditions: actions:
 - if:
 - alias: stop
 condition: trigger
 id: stop
 then:
 - action: media_player.media_stop
 target:
 entity_id: media_player.bedroom
 data:  else:
 - action: media_player.volume_set
 target:
 entity_id: media_player.bedroom
 data:
 volume_level: 0.35
 - action: media_player.play_media
 target:
 entity_id: media_player.bedroom
 data:
 media_content_id: https://nas.lan.rdeaton.space/jukebox/{{ trigger.id }}.mp3
 media_content_type: music
mode: restart

Now, adding a new song is as easy as putting an mp3 on the NAS and adding a corresponding 4 lines to the triggers section of the YAML, quite acceptable for now.

Once everything else came together, things got really exciting, and I wanted to get some tags in her hands to start learning how to use it as quickly as possible. Lacking the drawing skills necessary to draw the first ten or so tags I wanted to give her, I decided to try to use some sticker paper and print little tags. Unfortuantely, I didn't have any sticker paper at home, so I ran out to a couple stores within walking distance, and the best I could find was some Avery Internet Shipping Labels, which are listed as supporting laser printers (the only printer we have at home).

To make sure my prints lined up, I figured I'd grab the template that goes corresponds to the labels. Unfortunately, Avery, as well as the bulk of the results on the first page of Google results for this template, want you to sign up to download a template. To that, I say, No, I want to be left alone. Luckily I found a free one a few more results down.

Now, for some artwork. I stumbled upon Flaticon, which has a bunch of free (with attribution) outline icons. These will do for the weekend project at least.

A screenshot of a row of stickers for the NFC tags ready to be cut out

After printing, cutting, and peeling, I had a couple tags ready to go.

User testing: success

A photo of the control box now decorated with some stickers

It took no time at all for my daughter to figure out how to use the tags to play music, and to use the stop tag to stop the music. She figured out how to restart by taking the tag off and putting it back on, how to switch songs, and she was immediately able to associate the pictures with songs she already knew after one play. She now has a little bucket of tags and can pick out some of her own music on demand.

Future improvements

  • More songs. The bottleneck on getting all of the songs my daughter already knows and likes is getting a batch ready to print, cutting, peeling, and doing the initial tag to get the ID of each of the tags.

  • Restricted hours. This is super easy to do in Home Assistant, and probably a good idea to do before the first time she wanders around at 3am and starts jamming to the wheels on the bus.

  • Latency improvements. It takes 3-5 seconds or so from scanning the tag until music is playing. There's a few ways I want to improve this:

    • An LED or a piezo-buzzer triggered by the ESP32 on scan would give the low latency feedback that your scan had gone through, rather than waiting to hear the music.
    • A small shell script to trim quiet sections from the front of the audio files would probably help
    • The set_audio command is probably over-aggressive. I could check the state that HomeAssistant has for the speaker and not pay cost of the API call to set the volume, but I hate programming in these crappy yaml DSLs, so it hasn't been done yet.
  • Multi-room support. There's a homepod in the living room that could also be controlled in the same way. I could build a second scanner, I have enough parts, or I could try to make the scanner aware of what room it was in. ESPHome has a built-in Wi-Fi signal strength sensor, and since the HomePods are each located near a Wi-Fi AP, you could probably differeniate rooms for using the scanner pretty easily.

  • Smaller, battery powered scanner. This first one is a bit larger than it needs to be, and still needs power over the Micro USB cable. I ran for a while with a USB Multimeter attached, and current draw averages out to just under 0.4 W. With even a modest sized portable USB battery pack, you could expect days of lifetime with no on/off switch. I'm also sure there's some extra lines that we could drop in the ESPHome YAML to have more deep-sleep and drop the power consumption even more.

  • Better labels. The laser printer on the internet shipping label paper turns out to not be particularly durable, within an hour or so some of the labels were already fading from being handled. Some better sticker paper and maybe a trip to the library to print icons in color would really make these appear much higher quality.

  • Better organization for tags. The coin-sized tags are functional, but a bit hard to store, organize, and glance through. Blank credit-card sized NFC tags and some binder inserts for organizing playing cards could make for a nice book that you could flip through and look for songs, and with the extra surface area, we could add more detail to the labels.

  • New use-cases around the house. Since the interaction went so smoothly, it's very appealing to see what other things we could make controlled by NFC tags in the house. In the process of building this, I came across a similar project that can be used to play a movie or TV show.


Read the original article

Comments

HackerNews