How I Built an NFC Movie Library for my Kids

When I was a kid, my sister and I had a tower of VHS tapes we watched endlessly. Fast-forward to today, and my children's movie collection is vastly different. It's completely digital and dispersed across services. I wanted to recreate the tangible magic of my childhood for them.

Here's our current media routine. We have two boys, and the eldest is permitted a 30-minute TV session in the morning and another in the evening. He usually knows what he wants to watch, but he depends on us to operate the remote and navigate to the correct apps.

This got me thinking: why not blend the old with the new? I wanted a way for my son to have a more active role in choosing what to watch, despite the digital format.

Years earlier, I came across someone who had prints of his favorite music albums with an NFC tag inside of them, and I made something similar for my kids.

The end result

Here's what I ended up with:

It's a collection of laminated movie covers with an NFC tag inside. All my son has to do is pick the movie he wants to watch and put it on the NFC reader. Home Assistant will do the rest.

Here's a video of the system working:

Proof-of-concept & materials

I started by building a simple prototype. One of my electronics kits contained an NFC Reader (RC522) and some NFC tags.

I connected the NFC reader to an ESP32 board and flashed ESPHome onto it. I then placed an NFC tag on top of the reader and saw them appearing in the ESPHome logs.

That's all the confirmation I needed. I went to AliExpress and bought the "production" hardware:

I already had plenty of ESP32 boards lying around (LOLIN32 lite), but when I saw this "Super Mini" version, I had to have it. Look at this cutie:

Flashing ESPHome

Once everything arrived, I connected the NFC reader to the ESP32 using the following pins:

SS (SDA) -> GPIO7
SCK (SCK) -> GPIO4
MOSI -> GPIO6
MISO -> GPIO5

I also added a buzzer so I could beep some sounds when a tag was successfully scanned.

Next, I created an ESPHome configuration file. I had to add a few special platformio_options to make it work on the ESP32 "Super Mini" variant, but otherwise this is a very simple configuration.

---
substitutions:
  devicename: "nfc-scanner"
  friendly_name: "NFC Scanner"

packages:
  esphome: !include common/esphome.yaml
  api: !include common/api.yaml
  logger: !include common/logger.yaml
  wifi: !include common/wifi.yaml

esphome:
  # Magic variables to get the ESP32C3 Super Mini to work
  platformio_options:
    board_build.f_flash: 40000000L
    board_build.flash_mode: dio
    board_build.flash_size: 4MB
  on_boot:
    priority: 600
    then:
      - rtttl.play: 'short:d=4,o=5,b=100:16e6,16e6'

esp32:
  variant: ESP32C3
  board: esp32-c3-devkitm-1
  framework:
    type: esp-idf

status_led:
  pin:
    number: GPIO8
    inverted: true

output:
  - platform: ledc
    pin: GPIO3
    id: buzzer

rtttl:
  output: buzzer

spi:
  clk_pin: GPIO4
  mosi_pin: GPIO6
  miso_pin: GPIO5

rc522_spi:
  cs_pin: GPIO7
  on_tag:
    then:
      - rtttl.stop:
      - homeassistant.tag_scanned: !lambda 'return x;'
      - rtttl.play: 'short:d=4,o=5,b=100:16e6,16e6'

If you're building this yourself, you don't have to adhere to these pin numbers.

See my original ESPHome configuration for the LOLIN32 Lite here.
---
substitutions:
  devicename: "nfc-scanner"
  friendly_name: "NFC Scanner"

packages:
  esphome: !include common/esphome.yaml
  api: !include common/api.yaml
  logger: !include common/logger.yaml
  wifi: !include common/wifi.yaml

esphome:
  platform: ESP32
  board: lolin32
  on_boot:
    priority: 600
    then:
      - rtttl.play: 'short:d=4,o=5,b=100:16e6,16e6'

output:
  - platform: ledc
    pin: GPIO32
    id: buzzer

rtttl:
  output: buzzer

spi:
  clk_pin: GPIO18
  mosi_pin: GPIO23
  miso_pin: GPIO19

rc522_spi:
  cs_pin: GPIO15
  on_tag:
    then:
      - rtttl.stop:
      - homeassistant.tag_scanned: !lambda 'return x;'
      - rtttl.play: 'short:d=4,o=5,b=100:16e6,16e6'

After flashing ESPHome, the device will show up in Home Assistant with no entities. No need to panic, that's normal. It will emit tag_scanned events. More on that later when we automate everything.

3D Printing an enclosure & collection box

To keep the electronics and wires safe from tiny hands, I designed and 3D printed a simple enclosure.

It's a box

It's a simple design. The only challenge I had was fixing the ESP32 in place. Unlike other dev boards, the ESP32C3 Super Mini has no screw holes, so I designed a simple clip mechanism.

Don't worry, the ESP32 is not floating in the air. It rests on the soldered GPIO pins on the bottom of the case.

I used Fusion360 to design and test fit all the parts (thanks to Hans Wurst for publishing a model of the Super Mini on GrabCAD).

By modeling every part, I can test if it all fits together well.

I also made a nice box to store the NFC cards:

Creating the "DVD" cards

With the reader finished, only the "DVD" cards were left. I printed small movie covers in booklet form, placed an NFC tag inside, and ran them through the laminator.

The booklet protects the NFC tag and allows me to recover it in the future.

Home Assistant automation

On to the last piece of the puzzle: creating an automation in Home Assistant that plays the correct movie when a tag is scanned.

We store movies on Plex and played via an Apple TV. There are two ways to play a movie on Plex:

The Plex integration is clunky and unreliable in my experience. It requires you to turn on the Apple TV, launch the Plex app, and wait for it to become available in Home Assistant before sending it a command to play a specific movie.

The Apple TV integration is near instant. All you have to do is send a "deep link", and that's it!

The deep link format is documented on the Plex forum, and is rather simple. Here's the deep link to play a given media item:

plex://play/?metadataKey=UNIQUE_MEDIA_ID&server=SERVER_ID

Your Plex server ID can be found by going to this page and looking for your server's clientIdentifier.

Next, I needed to know what kind of data ESPHome sends to Home Assistant when a tag is scanned. I opened the Developer Tools in Home Assistant and started listening for tag_scanned events. I put a tag on the reader and saw an event pop up:

event_type: tag_scanned
data:
  tag_id: 1D-20-E2-06-96-00-00
  device_id: a93f971bb9622b266286460c3f2ac640
origin: LOCAL
time_fired: "2023-11-13T14:57:06.765528+00:00"
context:
  id: 01HF4JZB6DHY26TWZ903Z01BJ9
  parent_id: null
  user_id: null

The interesting bit here is the tag_id. I used this ID to create a mapping between the tag's ID and the ID of a movie in Plex.

Here's the entire automation:

- alias: "NFC Reader - Plex"
  description: ""
  mode: single
  trigger:
  - platform: event
    event_type: tag_scanned

  # Only allow movies to be played in the morning and evening.
  # Broad time window to allow for some flexibility in schedule.
  condition:
  - condition: or
    conditions:
    - condition: time
      after: '05:00:00'
      before: '09:00:00'
    - condition: time
      after: '18:00:00'
      before: '19:50:00'  # Bed time!
  action:
  - variables:
	  # Map the ID of each tag to a Plex ID. The "name" attribute is
	  # not used but nice to have for debugging.
      NFC_MAPPING:
        53-77-08-69-71-00-01:
          name: Ratatouille
          plex_id: 37353
        53-72-08-69-71-00-01:
          name: Coco
          plex_id: 3135
		# ...
  - if:
    # Make sure the scanned tag is in the mapping
    - alias: "NFC tag is in the mapping"
      condition: template
      value_template: "{{ trigger.event.data.tag_id in NFC_MAPPING }}"
    then:
    # Turn on the Apple TV (and the TV itself) when it's in standby
    - if:
      - condition: state
        entity_id: media_player.appletv_living
        state: standby
      then:
      - service: media_player.turn_on
        data: {}
        target:
          entity_id: media_player.appletv_living
      - delay:
          seconds: 5

	# Take the Plex ID from the mapping and send a deep link to the 
	# Apple TV. This will automatically open the Plex app and start
	# or resume the given movie.
    - service: media_player.play_media
      target:
        entity_id: media_player.appletv_living
      data:
        media_content_type: url
        media_content_id: "plex://play/?metadataKey=%2Flibrary%2Fmetadata%2F{{ NFC_MAPPING[trigger.event.data.tag_id].plex_id }}&server=xxxxxxxxxxxxxx"

	# Set an appropriate (low) volume on our Sonos Beam
	# Volume controls on Apple TV only support up/down when using an eARC
	# speaker.
    - service: media_player.volume_set
	  data:
	    volume_level: 0.17
	  target:
	    entity_id: media_player.sonos_tv

The automation is simple and fast. It takes the Apple TV about 5 seconds to turn on the TV, launch the Plex app and have it play the correct movie.

I was thrilled! And so was my oldest son. He quickly realized he could use this to watch movies anytime he wanted. That wasn't the goal, so I added a condition to only allow the automation to run in the mornings and evenings.

The Apple TV also supports deep links for other services. Here are examples for the biggest streaming services:

The only problem with these services is that they will require you to select a profile before the movie/show will start playing. With Plex, you can enable "auto login", but I haven't tried it for the other services.

Future improvements

I'm thrilled with my first iteration, but I have some things I'd like to tweak in the future.

On the hardware side, I want to replace the NFC reader with the PN532. It's a lot smaller, and will allow me to make the enclosure smaller.

On the automation side, I want to make it possible for tags to play random episodes of TV shows. Right now, that's not possible with Plex deep links, so I'll use the Plex Integration as a workaround.

And finally, I found printable NFC cards on Amazon that look amazing. You can insert these in any inkjet printer and have really beautiful, and uniform cards.

Why physical media is great

So why did I go through all this trouble to give my kids something tangible? Well, I see many advantages in a system like this.

First: you have a limited choice of movies to watch. When I was a kid, we didn't have an infinite catalog of movies to watch. We had a handful of VHS tapes and DVDs. We watched the same movies over and over again, each time discovering new details we would have missed otherwise.

Second, it gives my kids autonomy. They can decide what they want to watch and are not dependent on us to operate the remote and navigate to the correct app. They simply go to the box of cards, flip through them, pick a movie, and play it. Just like we used to do with VHS and DVD.

Third, with autonomy comes responsibility. Between certain hours, we allow them to watch TV. They can watch any movie they like. They can even switch between movies. But the timeframe is fixed. They can watch a good chunk of one movie, or they can watch a tiny bit of 10 different movies. It's completely up to them.

And finally, we have only one TV and two half-hour slots to watch it. This means they will need to learn to collaborate and compromise. They must quickly agree on a movie or forfeit their viewing time. Or perhaps they use their creativity to come up with a system to decide who gets to choose the movie. You can pick in the morning, I'll pick in the evening.

I have no evidence or research to back any of these claims. But I genuinely believe this could teach them valuable skills. And I feel it's way better than giving my kids unlimited access to Netflix, Disney+, YouTube, or any other streaming service.

Do you hate streaming services?

It's not that I hate streaming services. I've had a Spotify subscription for years, and I occasionally subscribe to other streaming services. It's just that I don't watch that many movies or shows, so it's hard to justify the price.

Instead, I buy our favorite movies on Blu-ray and put them on our Plex servers. This has 3 major benefits.

First, my son watches a few movies over and over again (especially Pixar movies). A Disney+ subscription costs €10,99/month, but for that price I can buy 3-4 Blu-rays on second hand websites. They're very cheap and once you buy a disc, it's yours forever.

Secondly, Blu-ray discs come with a lot of extra content. I especially love the "behind the scenes" or "deleted scenes" that most discs come with. These aren't always available on streaming services.

And finally, if you're a non-English speaker, Blu-rays will include a dubbed version in your native language. This is a big plus for the kids, especially when they're as young as mine are now.

Conclusion & downloads

We're really happy with this system, and I'm looking forward to when my youngest will be able to use it as well.

Want to build your own? Here's my Fusion360 design file that you can use or improve upon.

Posted on

You May Also Enjoy

Subscribe to my newsletter

Monthly newsletter with cool stuff I found on the internet (related to science, technology, biology, and other nerdy things)! Check out past editions.