In this tutorial I'll be walking you through the steps of making your own smart lock with an RFID reader!
You can use this lock for many things, like:
- a safe
- securing your garden house
- a locker
- a front door ( do this on your own risk though :) )
When we where asked to think about a project we would like to make for school, I was originally thinking about make a lock that uses facial recognition. Sadly enough I wasn't allowed to make a lock with facial recognition,
so I made one with an RFID reader instead. No worries though, it's just as much fun!
Before we get into the technologies I used to create this lock, let's look at all the components and materials you need.
Step 1: The Unlock Flow
Before we start writing code and connecting the components. We should think about the flow our user has to go through to unlock the lock. Something I should've done more thorough.
Obviously we want the user to be able to unlock the lock with an RFID tag. So how are we going to link an RFID tag to a certain user, you think? I've opted for a registration flow, where only the admin can add new users to the lock. That way we have full control over who can access the lock. I'll come back to this when we'll take a look at the database scheme.
A user is able to unlock the lock with two options:
- Scan your RFID tag and hope you're authorized
- Log-in to the web-app and unlock from within the web-app
Step 2: Electric Scheme
This is the electrical scheme I used. You can follow it or create something of your own.
DISCLAIMER: I used a library to read and write with the RFID reader, these pins cannot be changed so you should use the same ones as I do. This is the library for anyone who's interested. I have modified this to fulfill my needs and you can find these modifications in the GitHub repo.
Step 3: Database Scheme
The database is rather simple. We have 2 tables, a User table and a History table.
The User table contains information about the user, it stores the password as a 72 chars long hash and generates a UUID for every user.
The history table contains the date and time of when the lock was unlocked and has a relation with the User table on the user_id.
Step 4: Setup Your PI
For the RFID reader to work you need to make some changes to the Raspberry Pi config.
- Firstly you have to enable the SPI interface of your Pi
sudo raspi-config # Use arrow keys to goto: 5 - Interfacing options
# Press enter
# Use arrow keys and choose P4 SPI
# Press enter
# Confirm with "Yes"
sudo reboot now
- Run the following command to install the required packages
sudo apt-get install python3-dev python3-pip
- Lastly run, this wil install spidev and the library I used to read and write with the RFID reader
sudo pip3 install spidev mfrc522
Step 5: Writing the Code
Now comes the easy part. After wiring everything up we start with writing the code. For this projects I also used Flask to write the back-end. This caused a lot of problems, but once you understand what was happening these problems are actually quiet easy to fix.
Let's take a look at the core of the program, reading with the RFID reader. The library I used makes this really simple ( maybe that's why it's called SimpleMFRC522 ;) )
Just import it and read, like this:
from mfrc522 import SimpleMFRC522 reader = SimpleMFRC522() id, test = reader.read()
This makes for a problem though, because "reader.read()" is just a while loop that checks if an RFID tag is close to the reader, this caused for blocking code. Nothing else can process while this loop is running. So how to we fix this?
I created a class that imports the "SimpleMFRC522" and makes a thread out of it.
from mfrc522 import SimpleMFRC522
# init stuff
# This is whats get called when you do RFID().start()
reader = SimpleMFRC522()
id, text = reader.read()
Now this runs in his own thread without blocking the rest of the code. If you wan't to read more about how threading works, I highly recommend reading this Wikipedia article, it helped me to understand the concept better.
Unlocking the lock
Now that we can read the tags, we need to validate the owner of the tag, so we don't just let everyone open our lock. This was rather simple, just extract the UserId that's on the tag and check it in our database.
user = db.get_data('SELECT authorized, first_name from willy.User where user_id = %s', remote_id, True)
if not user: raise IndexError
return user['authorized'], user['first_name']
print('No user found')
Unlocking with the web-app was a bit more difficult, as this needs an authentication system to log a user in.
And once they are logged they have to send a JWT (JSON Web Token) with every request they send, to make sure they don't change stuff that only the admin should be able to.
token = request.headers.get('Authorization') if not token: return reply(exception=ApiExceptions.forbidden, socket=True).send() token = token.split(' ') payload = AuthUser.validate_jwt(token) if payload: print(payload['id']) print(session) try: # if session[payload['id']]: # current_user = AuthUser(**session[payload['id']]) # return f(*args, **kwargs) auth_user = AuthUser.find_user_by_id(payload['id']) if auth_user: current_user = auth_user return f(*args, **kwargs) else: return reply(exception=ApiExceptions.forbidden, socket=True).send() except IndexError: return reply(exception=ApiExceptions.email_not_found).send() except Exception as e: print('error', e) return reply(exception=ApiExceptions.default).send() else: return reply(exception=ApiExceptions.forbidden, socket=True).send()
Step 6: The Web-app
To write the web-app I used React. The reason for this is because of the login system. WIth React I can save state and easily reuse components and context to access the state. I wrote custom routing for the front-end and and authentication context that checks if you should see certain admin stuff or not. The routing makes sure that you can only view the pages when you're actually logged in.
Step 7: Wrapping Up
I hope that you enjoyed reading this and that I made you curious to make your own smart lock.
The complete codebase can be found in my Github repo.
This is an entry in the