Overview
You can build a movie library player using a Raspberry Pi 5, Adafruit's NFC breakout, and NFC (near field or "smart") cards. This project runs Python code with Blinka, the CircuitPython compatibility library for single-board computers running Linux, to create a script that allows you to use NFC cards to launch movies in the VLC media player. The M.2 HAT+ lets you add an M.2 drive with plenty of space for all of your media files.
Inspired by Simply Explained's NFC Movie Library, the goal of this project is to make movies feel special by offering a more physical experience. Plus, no WiFi necessary!
A 3D printed enclosure houses the Raspberry Pi 5 and the Adafruit NFC Breakout board. A standard size NFC card fits inside a VHS-inspired snap fit case. A tripod screw adapter allows you to mount it however you like.
A Python script allows you to assign video files to any NFC card and saves it to a CSV file. This guide will walk you through setting up the Python virtual environment, M.2 HAT, VLC media player and how to run the script on boot.
Page last edited November 20, 2024
Text editor powered by tinymce.
Circuit Diagram
You can use a 2x5 header to attach the PN532 breakout to the Raspberry Pi.
- PN532 3.3V to Pi 3.3V (red wire)
- PN532 SCK to Pi SPI0 SCLK (yellow wire)
- PN532 MISO to Pi SPI0 MISO (blue wire)
- PN532 MOSI to Pi SPI0 MOSI (green wire)
- PN532 SSEL to Pi GPIO24 (white wire)
- PN532 GND to Pi GND (black wire)
Page last edited November 20, 2024
Text editor powered by tinymce.
Installing Blinka on Raspberry Pi
Prerequisite Pi Setup!
In this page we'll assume you've already gotten your Raspberry Pi up and running and can log into the command line
Here's the quick-start for people with some experience:
- Download the latest Raspberry Pi OS or Raspberry Pi OS Lite to your computer
- Burn the OS image to your MicroSD card using your computer
- Re-plug the SD card into your computer (don't use your Pi yet!) and set up your wifi connection by editing supplicant.conf
- Activate SSH support
- Plug the SD card into the Pi
- If you have an HDMI monitor we recommend connecting it so you can see that the Pi is booting OK
- Plug in power to the Pi - you will see the green LED flicker a little. The Pi will reboot while it sets up so wait a good 10 minutes
- If you are running Windows on your computer, install Bonjour support so you can use .local names, you'll need to reboot Windows after installation
- You can then ssh into raspberrypi.local
sudo apt-get update sudo apt-get -y upgrade sudo apt-get install -y python3-pip
and upgrade setuptools:
sudo apt install --upgrade python3-setuptools
Setup Virtual Environment
If you are installing on the Bookworm (released in 2023) or later version of Raspberry Pi OS, you will need to install your python modules in a virtual environment. You can find more information in the Python Virtual Environment Usage on Raspberry Pi guide. To Install and activate the virtual environment, use the following commands:
cd ~ sudo apt install python3-venv python3 -m venv env --system-site-packages
You will need to activate the virtual environment every time the Pi is rebooted. To activate it:
source env/bin/activate
To deactivate, you can use deactivate, but leave it active for now.
Automated Install
We put together a script to easily make sure your Pi is correctly configured and install Blinka. It requires just a few commands to run. Most of it is installing the dependencies.
cd ~ pip3 install --upgrade adafruit-python-shell wget https://raw.githubusercontent.com/adafruit/Raspberry-Pi-Installer-Scripts/master/raspi-blinka.py sudo -E env PATH=$PATH python3 raspi-blinka.py
If you are installing on an earlier version such as Bullseye of Raspberry Pi OS and not using a Virtual Environment, you can call the script like:
sudo python3 raspi-blinka.py
If you are installing an older version of Raspberry Pi OS, your system default Python is likely Python 2. If so, it will ask to confirm that you want to proceed. Choose yes.
It may take a few minutes to run. When it finishes, it will ask you if you would like to reboot. Choose yes.
Manual Install
If you are having trouble running the automated installation script, you can follow these steps to manually install Blinka.
sudo raspi-config nonint do_i2c 0 sudo raspi-config nonint do_spi 0 sudo raspi-config nonint do_serial_hw 0 sudo raspi-config nonint do_ssh 0 sudo raspi-config nonint do_camera 0 sudo raspi-config nonint disable_raspi_config_at_boot 0
sudo apt-get install -y i2c-tools libgpiod-dev python3-libgpiod pip3 install --upgrade adafruit-blinka adafruit-platformdetect
Raspberry Pi 5 Adjustments
At the moment, RPi.GPIO is installed, which causes issues. Just remove it with the following command:
pip3 uninstall -y RPi.GPIO
Check I2C and SPI
The script will automatically enable I2C and SPI. You can run the following command to verify:
ls /dev/i2c* /dev/spi*
You should see the response
/dev/i2c-1 /dev/spidev0.0 /dev/spidev0.1
Fixing CE0 and CE1 Device or Resource Busy Issue
In order to use the CE0 and CE1 pins in Python, you will need to disable them from OS usage. To do so, check out the Reassigning or Disabling the SPI Chip Enable Lines section of this guide.
Enabling Second SPI
If you are using the main SPI port for a display or something and need another hardware SPI port, you can enable it by adding the line
dtoverlay=spi1-3cs
to the bottom of /boot/config.txt and rebooting. You'll then see the addition of some /dev/spidev1.x devices:
comment out this line :
#dtparam=spi=on
Create a new file called blinkatest.py with nano or your favorite text editor and put the following in:
import board
import digitalio
import busio
print("Hello, blinka!")
# Try to create a Digital input
pin = digitalio.DigitalInOut(board.D4)
print("Digital IO ok!")
# Try to create an I2C device
i2c = busio.I2C(board.SCL, board.SDA)
print("I2C ok!")
# Try to create an SPI device
spi = busio.SPI(board.SCLK, board.MOSI, board.MISO)
print("SPI ok!")
print("done!")
Save it, make sure your virtual environment is activated, and run at the command line with:
python3 blinkatest.py
You should see the following, indicating digital i/o, I2C and SPI all worked.
Page last edited November 20, 2024
Text editor powered by tinymce.
Python Virtual Environment Prep
As Carter writes in his Python Virtual Environment Usage on Raspberry Pi guide:
Starting with the October 10, 2023 Bookworm release of the Raspberry Pi OS, the use of Python Virtual Environments (venv) when pip installing packages is required. No more sudo pip. This will break things and require learning new things. Yeah.
You will need to setup a Python virtual environment (venv) on your Raspberry Pi 5 for this project. Don't worry though! If you follow along with the guide step by step, you'll be just fine.
Always venv
On the Other Ideas page in the venv guide, there is a tip for having the virtual environment enabled automatically at boot by editing the .bashrc file (sudo nano .bashrc) and adding this line to the bottom:
source home/user/venv/bin/activate
Where venv is the name of your virtual environment. You can take this a step further by adding the alias for running Python scripts as sudo to your .bashrc file as well:
alias gogo='sudo -E env PATH=$PATH python'
You can change gogo to any command you want. This way, every time you boot up your Pi, you'll have your Python venv enabled and you'll be able to use your alias for running Python scripts.
Page last edited November 20, 2024
Text editor powered by tinymce.
M.2 HAT+ Prep
Before using the M.2 HAT+ with your Raspberry Pi 5, you'll need to follow some steps to setup the hat. Raspberry Pi has steps laid out on their site on how to install the hat on Raspberry Pi OS. These steps are more than likely subject to change depending on updates with Raspberry Pi OS.
M.2 Drive
Before using an M.2 Drive with the hat, you'll need to format and partition it for the Raspberry Pi to recognize it. You can use a USB to M.2 drive adapter to connect the drive to another computer. Use your operating system's drive format utility (Disk Utility on macOS, Disk Management on Windows, etc) to format the drive. Multiple formats are acceptable, exFAT and NTFS were tested.
After formatting the drive, insert the drive into the hat and attach the hat to the Raspberry Pi as described in the Raspberry Pi documentation.
Automatic Mounting
To make sure that your M.2 drive mounts properly to your Raspberry Pi every time on boot, you can configure it to have a mount point in your OS. First, boot into Raspberry Pi OS and open your File Manager. You should see your M.2 drive listed. Click on the drive. You'll be asked to enter your password to allow access to the drive.
In a terminal, enter the command:
sudo blkid
This lists the connected drives on your Raspberry Pi. You should see a drive listed called /dev/nvme0n1p2. This is your M.2 drive. Make note of your drive's UUID number. You will need this later when you edit the fstab file.
Next, you'll create a mounting point for the drive. In the terminal, enter:
sudo mkdir /mnt/movie_disk
This creates a folder target called /movie_disk. Then, you'll mount the M.2 drive to this mount point:
sudo mount /dev/nvme0n1p2 /mnt/movie_disk
Edit fstab
Next, you'll edit the fstab file to add the M.2 drive to the list of disks that mount on boot. In the terminal, enter:
sudo nano /etc/fstab
Then add this line to the fstab file, changing the UUID to your drive's UUID that was listed after running sudo blkid. Replace fstype with the file system type (NTFS, exFAT, etc).
UUID=YOUR-UUID-HERE /mnt/movie_disk fstype defaults,auto,users,rw,nofail,umask=000 0 0
Edit config.txt
In the next step, you'll edit the config.txt file to add a device tree overlay for the M.2 drive. In the terminal, enter:
sudo nano /boot/firmware/config.txt
At the bottom of the file, add the lines:
dtparam=nvme
dtparam=pciex1_gen=3
Depending on your drive, you may need to adjust the pciex1_gen value. 3 is the highest recognized by the Raspberry Pi and most drives use this speed, but refer to your drive's documentation to be sure.
ls /mnt/movie_disk
This lists the files in the movie_disk mount point aka on your M.2 drive. You should see all of the files on your M.2 drive listed.
Page last edited November 20, 2024
Text editor powered by tinymce.
NFC Media Player Software
Install the Required Libraries
You will need to install the PN532 library onto your Raspberry Pi. In the terminal, enter:
pip install adafruit-circuitpython-pn532
Download the Project Bundle
Once you've finished setting up your Raspberry Pi with Blinka and the library dependencies, you can access the Python code files, Blinka graphic, and CSV file by downloading the Project Bundle.
To do this, click on the Download Project Bundle button in the window below. It will download as a zipped folder.
# SPDX-FileCopyrightText: Copyright (c) 2024 Liz Clark for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import subprocess
import csv
from datetime import datetime
import os
import tkinter as tk
from PIL import Image, ImageTk
import board
import busio
from digitalio import DigitalInOut
from adafruit_pn532.spi import PN532_SPI
# ---- Update these file paths for your raspberry pi! ----
username = "YOUR-USERNAME"
image_path = f"/home/{username}/Pictures/blinka.png"
movie_path = "/mnt/movie_disk"
csv_file = "movies.csv"
# ----
if not os.path.isfile(csv_file):
with open(csv_file, mode='w', newline='') as f:
writer = csv.writer(f)
writer.writerow(['uid', 'movie'])
root = tk.Tk()
root.attributes("-fullscreen", True)
root.configure(background="black")
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
root.geometry(f"{screen_width}x{screen_height}+0+0")
def toggle_fullscreen():
root.attributes("-fullscreen", not root.attributes("-fullscreen"))
def exit_fullscreen():
root.destroy()
root.bind("<F11>", toggle_fullscreen)
root.bind("<Escape>", exit_fullscreen)
image = Image.open(image_path)
photo = ImageTk.PhotoImage(image)
image_label = tk.Label(root, image=photo, bg="black")
image_label.place(x=0, y=0)
info_label = tk.Label(root, text = "00:00: AM", font=("Helvetica", 68), fg="white", bg="black")
info_label.place(x=1870, y = 1030, anchor="se")
x_pos, y_pos = 100, 100
x_speed, y_speed = 1, 1
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
cs_pin = DigitalInOut(board.D24)
pn532 = PN532_SPI(spi, cs_pin, debug=False)
ic, ver, rev, support = pn532.firmware_version
print("Found PN532 with firmware version: {0}.{1}".format(ver, rev))
# Configure PN532 to communicate with MiFare cards
pn532.SAM_configuration()
print("Waiting for RFID/NFC card...")
now_playing = False
# pylint: disable=global-statement
def move():
global x_pos, y_pos, x_speed, y_speed
x_pos += x_speed
y_pos += y_speed
if x_pos + photo.width() >= root.winfo_screenwidth() or x_pos <= 0:
x_speed = -x_speed
if y_pos + photo.height() >= root.winfo_screenheight() or y_pos <= 0:
y_speed = -y_speed
image_label.place(x=x_pos, y=y_pos)
root.after(30, move)
def get_movie(uid):
with open(f"/home/{username}/{csv_file}", "r") as file:
reader = csv.DictReader(file)
for row in reader:
if row["uid"] == uid:
return row["movie"]
return None
def read_card():
global now_playing
uid = pn532.read_passive_target(timeout=0.05)
# Try again if no card is available.
if uid is not None:
print("Found card with UID:", [hex(i) for i in uid])
title = str([hex(i) for i in uid])
movie_name = get_movie(title)
if movie_path:
video = f"{movie_path}/{movie_name}.mp4"
subprocess.run(["sudo", "-u", username, "vlc", "--fullscreen", video], check=False)
now_playing = True
else:
current_time = datetime.now().strftime("%I:%M %p")
info_label.config(text=f"{current_time}")
root.after(2000, read_card)
def vlc_check():
global now_playing
try:
result = subprocess.check_output(["pgrep", "-x", "vlc"])
if result:
return True
else:
return False
except subprocess.CalledProcessError:
now_playing = False
return False
root.after(5000, vlc_check)
if not now_playing:
move()
read_card()
if now_playing:
vlc_check()
root.mainloop()
After downloading the Project Bundle, move the folder to your /home/user directory. Then, unzip the folder by right-clicking on the folder in the File Manager and selecting Extract or with your preferred command line tool. Keep the following files in the /home/user directory:
- add_movie.py
- nfc_movie_player.py
- movies.csv
Move the blinka.png graphic to the /home/user/Pictures folder.
File Explanation
The main script is the nfc_movie_player.py file. This Python script launches a graphical background and bounces the Blinka image around the screen. When you scan an NFC card, the script reads the card and compares it to the entries in the movies.csv file. If the card exists in the CSV file, then the associated movie starts to play via VLC.
The CSV file can be edited manually or by using the add_movie.py Python script. More information on this script is available on the Add New Movies Python Code page in the guide.
Customize the Script
At the top of the nfc_movie_player.py file are some user configuration options. You'll need to edit these options to match the file paths and user name on your Raspberry Pi.
# ---- Update these file paths for your raspberry pi! ----
username = "YOUR-USERNAME"
image_path = f"/home/{username}/Pictures/blinka.png"
movie_path = "/mnt/movie_disk"
csv_file = "movies.csv"
# ----
Page last edited November 20, 2024
Text editor powered by tinymce.
Add New Movies Python Code
This project works by associating a movie file with an NFC card. You could manually type out the UID of each of your cards and movie titles, but a Python script helps to ease this process.
# SPDX-FileCopyrightText: Copyright (c) 2024 Liz Clark for Adafruit Industries
#
# SPDX-License-Identifier: MIT
import csv
import os
import sys
import board
import busio
from digitalio import DigitalInOut
from adafruit_pn532.spi import PN532_SPI
spi = busio.SPI(board.SCK, board.MOSI, board.MISO)
cs_pin = DigitalInOut(board.D24)
pn532 = PN532_SPI(spi, cs_pin, debug=False)
csv_file = "movies.csv"
if not os.path.isfile(csv_file):
with open(csv_file, mode='w', newline='') as file:
writer = csv.writer(file)
writer.writerow(['uid', 'movie'])
def is_duplicate(uid, mov):
with open(csv_file, mode='r') as f:
reader = csv.DictReader(f)
for row in reader:
if row['uid'] == uid or row['movie'] == mov:
return True
return False
ic, ver, rev, support = pn532.firmware_version
print("Found PN532 with firmware version: {0}.{1}".format(ver, rev))
# Configure PN532 to communicate with MiFare cards
pn532.SAM_configuration()
print("Waiting for new RFID/NFC card...")
while True:
the_uid = pn532.read_passive_target(timeout=0.05)
if the_uid is not None:
uid_str = f"{[hex(i) for i in the_uid]}"
movie = input("Enter the name of the new movie: ")
movie = f"{movie}"
if is_duplicate(uid_str, movie):
print("UID {uid_str} or movie {movie} already exists, skipping.")
else:
with open(csv_file, mode='a', newline='') as file:
writer = csv.writer(file)
writer.writerow([uid_str, movie])
print(f"Added UID {uid_str} with movie title {movie} to movies.csv")
sys.exit()
How the Script Works
You'll run the script in a terminal, separate from the main NFC player script. When the script starts, it will wait for an NFC card to be read. When a card is read, it will prompt you to enter a movie title. After you enter the title, the UID from the card and the title are added to the movies.csv file.
Page last edited November 20, 2024
Text editor powered by tinymce.
Run on Boot
You probably don't want to be futzing with a keyboard and terminal to launch this project every time. Luckily you can configure it to run on boot by creating a systemd unit file that runs the Python script within the Python venv after the desktop environment loads.
sudo nano /lib/systemd/system/nfcmovie.service
Copy and paste the contents of the text file below into the systemd file:
[Unit] Description=NFC Media Player After=x-session-manager.service [Service] User=USERNAME Environment=DISPLAY=:0 Environment=XAUTHORITY=/home/USERNAME/.Xauthority ExecStartPre=/bin/sleep 10 ExecStart=sudo /home/USERNAME/blinka/bin/python /home/USERNAME/nfc_movie_player.py [Install] WantedBy=default.target
The file waits for the desktop environment to load, waits another 10 seconds and then launches the Python script within the virtual environment.
Save the file. Then, in a terminal run these commands:
sudo systemctl enable nfcmovie sudo systemctl start nfcmovie
This enables the service and starts it. To test, reboot your Pi. After the desktop environment loads, you should see the script startup automatically.
Page last edited November 20, 2024
Text editor powered by tinymce.
CAD Files
3D Printed Parts
STL files for 3D printing are oriented to print "as-is" on FDM style machines. Parts are designed to 3D print without any support material using PLA filament. Original design source may be downloaded using the links below.
Build Volume
The parts require a 3D printer with a minimum build volume of:
- 136mm (X) x 44mm (Y) x 62mm (Z)
Design Source Files
The project assembly was designed in Fusion 360. This can be downloaded in different formats like STEP, STL and more.
Electronic components like Adafruit's boards, displays, connectors and more can be downloaded from the Adafruit CAD parts GitHub Repo.
Page last edited November 20, 2024
Text editor powered by tinymce.
Wiring
2x20 Socket Header
Use a 2x20 socket header to easily connect the NFC breakout to the GPIO headers on the Pi.
Get the socket header ready to solder wires.
Solder Wires to Socket Headers
Create 6 wires using the different colored spools of wire, each about 10cm (4 inches) in length.
Solder the colored wires to their corresponding pins on the socket header:
- Left side, top to bottom: red, green, blue, yellow, black
- Right side, top pin: white
Solder Wires to NFC Breakout
Solder the colored wires to their corresponding pins on the NFC breakout board.
- Red wire to 3.3V
- Green wire to MOSI
- Blue wire to MISO
- Yellow wire to SCK
- Black wire to GND
- White wire to SSEL
Enable SPI on NFC Breakout
Use right-angled header pins to create a slim jumper.
Break off two pieces of 3-pin right-angled headers. Insert them onto the back of the NFC breakout PCB and solder them in place.
Insert the included jumper to enable SPI.
Connection Test
Plug the socket header into the SPI0 GPIO header pins on the Pi. Reference the Circuit Diagram page for the correct placement.
You'll disconnect for placement into the enclosure on the next page.
Page last edited November 20, 2024
Text editor powered by tinymce.
Assembly
Installing NFC Breakout
Orient the NFC breakout with the NFC cover.
Use 4 M3x4mm long screws to secure the NFC breakout PCB to the cover.
Place the NFC breakout over the cover so the mounting holes are lined up.
Connect Active Cooling Fan
Get the active cooling fan ready to install onto the Raspberry Pi.
Plug in the cale from the active cooling fan to the fan port on the Raspberry Pi.
Install Active Cooling Fan
Orient the active cooling fan with the Raspberry Pi so the plastic screws are lined up with their corresponding mounting holes.
Place the active cooling fan over the Raspberry Pi. Press the two plastic screws so they fit through the mounting holes.
Installing Pi
Insert the Pi into the 3D printed frame so the ports fit through their corresponding cutouts. Line up the mounting holes.
Secure Pi to Frame
Use four M2.5 x 10mm long screws and hex nuts to secure the Raspberry Pi to the 3D printed frame.
Connect M.2 Hat
Loosen the latch from the PCIe port and insert the flex cable from the M.2 Hat+ breakout.
Fully seat the flex cable and press the latch to secure it shut.
Install M.2 Hat
Line up the pins from the Pi with the pins on the M.2 Hat. Press down on the M.25 Hat to fully seat the GPIO pins.
Connect NFC Breakout
Plug the 2x20 socket header onto the Pi's GPIO header so the corresponding pins are inserted correctly.
Install NFC Cover to Frame
Line up the edge of the 3D printed NFC cover with the railing on the frame. Slide the cover so it lined up with the frame.
Install Top Cover
Orient the top cover with the 3D printed frame. Press fit the top cover to close it shut.
Install Bottom Cover
Orient the bottom cover with the frame so the ports on the Pi's are lined up with the cutouts.
Press fit the bottom cover onto the frame.
Power Raspberry Pi
Plug in the 27W PD Power Supply 5.1V 5A into the USB C port on the Raspberry Pi.
Connect a HDMI display to the micro HDMI0 port on the Raspberry Pi.
The python script should automatically start after the booting sequence.
Page last edited November 20, 2024
Text editor powered by tinymce.
Use
Boot up your Raspberry Pi. The NFC movie script should start up with Blinka bouncing around the screen.
Tap your NFC card on the card reader. Your movie will start to play.
When the movie is done, quit VLC with Ctrl + Q. The Python script will resume and you'll be able to tap another card.
Page last edited November 20, 2024
Text editor powered by tinymce.