Subscribe on YouTube!

How to Build an NFC-Powered Door Lock with a Raspberry Pi

Forget keys and codes; secure your private spaces with fob access!

(This tutorial was originally published in Issue 4 of HackSpace Magazine from the Raspberry Pi Foundation.)

You will need…

  • Raspberry Pi
  • USB NFC/RFID Reader + some coded fobs
  • Electronic door striker plate
  • 12V 1.5A power supply
  • Female DC Power Jack
  • 5V Relay Switch (single-channel)
  • 3x Dupont jumper cables (female/female)
  • 1x extra cable (male/male)

Even if it’s completely unnecessary, there’s always something quite exciting about adding extra layers of security to your workspace entrance, harking back to childhood days of asking “what’s the secret password?” before letting your friends into your den. Plus if you’re anything like me, your space will have some pricey toys and expensive equipment in it, so it can certainly give you some extra piece of mind to have it protected by an additional barrier from any would-be intruders!

This is a great first project if you’re just getting started in the world of hacking and Raspberry Pis, as we’ll learn not only about accepting input to the system from something other than a standard keyboard & mouse but also about operating a relay switch to control an entirely separate (and higher voltage) electrical circuit.

Before we get started I’m going to assume that you already have your Raspberry Pi all set up with the latest version of Raspbian installed and up-to-date. With that all ready, start by connecting up all the hardware components.

First, plug in the RFID reader to any of the USB ports. This type of reader is especially easy to work with as it acts just like a keyboard, in that the operating system sees the data contained in any fob presented to it getting typed in, followed by a new line. To see this for yourself, open up a Text Editor and then hold each fob to the reader in turn; you’ll see a string of numbers getting quickly typed in followed by a newline character each time.

The RFID Reader inputs data it reads from the NFC tags (keyfobs) into the operating system like a keyboard inputting text.

Raspberry Pi GPIO Pins

Next, connect your relay to the GPIO pins (Figure 2). It’s a 5V relay, so the positive pin (which will be labelled either “+” or “5V”) needs connecting with a dupont cable to a 5V pin on your Pi, and the negative (labelled either “GND” or “-”) can go to any ground pin. The signal pin (labeled “S” or “in”) needs to go to any numbered GPIO pin. You can use any you like, but the code examples later use Pin 13 so you might as well do the same.

The electronic door strike works with a little electromagnet inside which pulls a small lever out of the way when activated by a 12V supply. As that is more than the Raspberry Pi can supply, you need a separate 12V power supply for this. A little DC power jack allows you to easily connect to the positive and negative from this supply’s cable (Figure 3). If you want, you can wire the striker plate directly into the 12V supply and test it out by flicking the power switch and seeing how it allows you to open it only when the 12V is being supplied.

You could also use a maglock as an alternative to a striker plate. I chose the latter though because it remains secure even if power is lost! Plus, depending on the door’s lock, you can still use a key as an override.

The Relay Switch Locks & Unlocks Your Door

To incorporate this into your circuit, wire the positive supply from the 12V socket into the middle terminal (COMMON) of your relay switch (below). Then, connect the final loose wire from the door strike into the Normally Open terminal (usually labelled NO). Your striker plate is now switched on/off by the relay switch connected to your Raspberry Pi.

A typical Raspberry Pi setup with relay switch powered by Pins 2 (5v Power) and 6 (Ground) and switched by Numbered Pin 13.

Now it’s time to get serious and write some simple Python code to act as the brains between all of the hardware components, switching the relay only when a valid keyfob is presented to the RFID reader.

The first thing to tackle is reading the data contained in a keyfob. Since we know that the newline character is “typed” when the fob’s code has finished being entered, you can use that newline character’s appearance as an event which will trigger some decision-making in your code along the lines of: “is this fob allowed to unlock the door?”

In order to get to that stage, you first need to write some code to listen to the Input Device and capture what’s being entered. This will involve using the evdev module, so before you do anything else, type the following into a Terminal window to ensure it’s installed and available to use on your Raspberry Pi:

sudo pip install evdev

With that installed, create a new Python script like this:

nano lock.py

This opens up the file called “lock.py” in the Nano text editor – and since it doesn’t yet exist, it also creates it for you. The first thing to add to your script is the following lines:

#!/usr/bin/env python3
from evdev import InputDevice
from select import select

The first line specifies the environment in which you’ll be working and that you want to use Python 3. Then, we need to import InputDevice and select, both of which will allow us to read the data from the RFID fobs.

Next, add the following three lines of code:

rfid_presented = ""
keys = "X^1234567890XXXXqwertzuiopXXXXasdfghjklXXXXXyxcvbnmXXXXXXXXXXXXXXXXXXXXXXX"
dev = InputDevice('/dev/input/event0')

The first line creates and initializes an empty variable in which we’ll store the characters which make up the data string from the keyfob, one by one. The second line defines a string of characters which later code will refer to in order to establish which character has been typed. The third line defines your input device so the code knows which to listen to. You’ll need to check what ID your input device has been given by the Raspbian operating system and modify your code accordingly. To do this, type the following into a Terminal window:

ls -lah /dev/input/by-id
Each USB input device (eg. mouse, keyboard) is assigned its own ID by the Raspbian Operating System. You’ll need to know the ID your RFID reader has been assigned so that you can listen to its inputs.

You should see a list of all your Raspberry Pi’s input devices, with a symlink for each one giving it a friendlier name. Look for the long name which is clearly your RFID reader – in this case it’s “event0” (because I’m shelled into the Pi via SSH and there are no other USB devices attached, such as a keyboard and mouse).

The system needs to continuously listen for input from the reader, so we achieve this by defining a infinite loop like this:

while True:

Everything else in the programme takes place within that loop, so it’s now constantly listening for an event. To handle the event, add these lines to the while loop:

while True:
        r,w,x = select([dev], [], [])
        for event in dev.read():
                if event.type==1 and event.value==1:
                        if event.code==28:
                                if rfid_presented=="0001155844":
                                        # Unlock Door
                                        print("Unlocking Door.")
                                        GPIO.output(13,GPIO.HIGH)
                                        time.sleep(5)
                                        # Lock Door again
                                        print("Locking Door Again.")
                                        GPIO.output(13,GPIO.LOW)
                                else:
                                        print("Access Denied.")
                                rfid_presented = ""
                        else:
                                rfid_presented += keys[ event.code ]

Inside this loop, we’re listening for events of type 1 which means a (virtual) key has been pressed and a character entered. Each time that happens, we look at the event code to see if it’s 28, which corresponds to the newline character which we know from before means the end of the data being read from the fob. If the data which has been entered isn’t a newline, we take that data and add it to the “rfid_presented” variable using the “+=” syntax, gradually building it up with a character at a time to create the string of text. Once we get a character which is 28, ie. the newline, we stop building the text string up and see if it’s the data that we’re after.

There are simpler ways to listen for user input using Python, but this method allows for greater flexibility and threading options if you later expand your project to give it a GUI using something like Tkinter. You’ll thank me later!

For the sake of clarity in this example, I’ve hardcoded the data we’re looking for into the if/else statements (see the boxout for a better real-world alternative) – you’ll obviously need to substitute that with the data from your own keyfob. If the string read from the keyfob is the one we’re looking for, then we jump into action and do five things:

  1. Print a line of text to the terminal window saying what we’re doing (useful for debugging)
  2. Unlock the door by switching the GPIO pin to HIGH, which triggers the relay
  3. Wait for 5 seconds to allow time for the door to be opened
  4. Print another line of text saying the door is now being locked again
  5. Lock the door again by switching the GPIO pin back to LOW
The Python script prints text to the Terminal, showing when the door is unlocked and locked again, as well as when access has been denied

If the data read from the fob isn’t the data we’re looking for, then we do nothing apart from print a line of text saying “Access Denied” to the terminal (Figure 5). After either outcome, it’s extremely important to remember to clear out the “rfid_presented” variable so that we’re starting afresh next time ready for the next unlock attempt.

Before those lines of code which control the GPIO pin will work, you first need to import the RPi.GPIO module for use in your script, as well as the time module which will enable the sleep function to work. To do this, go back to the top of your script and underneath the two imports you already have, add these lines:

import RPi.GPIO as GPIO
import time

GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
GPIO.setup(13,GPIO.OUT)

As well as importing the two extra Python modules you need, this will also configure and initialise the GPIO pins ready for use.

With your Python script now complete, save and exit by pressing CTRL+X to exit and then pressing Y to save changes. Then to load your program, type:

python lock.py
Once you’re done, you can install the electronic striker plate into the doorway that you want to secure with NFC fob access!

The Moment of Truth… Does the Raspberry Pi RFID Door Lock Work…?

If you hold the authorised keyfob to your reader you should immediately hear the relay “click” and if you try the striker plate, it will allow itself to be opened. After 5 seconds, you’ll hear a “click” again and the striker plate will no longer open. Then try another fob and you should be met with a blank wall of silence, and an Access Denied message printed to the Terminal.

Once you’ve launched the Python script in your terminal, click outside of the Terminal to defocus the window – this will avoid the terminal getting bombarded with the codes from your keyfobs each time you present them to the reader.

That’s all there is to it! You now have a sophisticated security system for whichever room (or cupboard!) that you’re securing. What’s more, since it uses a Raspberry Pi which can be connected to the Internet, it’s rife with possibilities for expansion. You could write an app to unlock the door remotely by simply toggling the GPIO pin between high and low, or you could add a keypad to require a PIN to accompany the NFC tag presented. If you build one yourself, be sure to share it with us so we can see what you’ve come up with!

To make the Python script load automatically every time your Raspberry Pi boots up (eg. after a powercut), type “sudo nano ~/.config/lxsession/LXDE-pi/autostart” and add this line to the end of the config file: python lock.py

Extra Tips…

Power Source: To avoid having to run two separate power supplies, consider using a dual-voltage power supply. If you have an old computer PSU lying around, you can easily make one yourself: simply use your multimeter to find the 12V and 5V supply cables normally delivered to a SATA hard drive. To make the PSU power up without needing an “on” switch on the front of a computer case, short out the green (signal) cable to an unused black ground cable.

Building in Flexibility: Instead of hard-coding the data from the RFID fob into your code, consider storing a list of authorised codes in a MySQL database instead, and modify the Python code with an SQL query to look up the presented code in the database to check validity. This opens up a world of possibilities; you could create a web interface to manage access remotely and assign owners’ names to fobs, or you could add extra columns to your database table to restrict access to certain days or times… ideal for cleaners or dog walkers, etc.

Paul Freeman-Powell

Paul (@paulfp) is the main presenter of the award-winning Switched On Network YouTube Channel, which covers a variety of interesting topics usually relating to his love of technology and all things geeky. He also founded and runs Innobella Media, where he leads in all aspects of video production, video editing, sound & lighting. A father of 3 children including twins, his hobbies used to include photography, playing the drums and cycling. With a degree in Modern European Languages, Paul speaks French, Spanish and a little bit of Italian, and holds dual British & Irish citizenship.

View all posts

Add comment

Comment with your Thoughts!

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Subscribe to our YouTube Channel!

Follow Us for Updates!

%d bloggers like this: