So I've been working with a friend on a pi-based access control system as a proof of concept. I took a lot of things I've learned so far, like building a relay board and other general low voltage shenanigans. We've been winging it but everything seems to be coming out quite nicely. Our prototype is essentially done but just needs to be tied into some type of authentication back end. Also it currently uses a barcode scanner (not pictured) but I would imagine you could integrate any identity collection device that uses serial.
So what does it do?
1. Outputs whether or not it's ready (via optional outputs for status lights)
2. When ready, listen to serial input for a barcode (or other credential)
3. Check credential against an access file, database, etc.
4. If approved, trigger a relay to grant physical access for a few seconds otherwise do nothing.
5. Plus possibly more features to be implemented as we refine things.
I feel like it's a bit easier to explain going in reverse from the input/output of the system down to the pi and code.
Power
Power comes in via a 12v barrel jack connector and onto a supply block. Power goes from there to the main access control interface which has 5 connectors:
+12v supply
-12v supply
Relay in
Relay out (Normally closed)
Relay out (Normally open)
The relay I'm talking about is that black box on the bottom left, it's a 12v 30A automotive relay. Right now power goes into it and the normally opened output goes to a normally locked magnetic strike plate. When access is granted that plate is unlocked. You could also use the normally closed connector to power a magnetic door lock that would release when access is granted.
12v/5v supply
12v comes from the supply block and goes to the blue screw terminals on this legit regulator but also continues off to power the 12v rail (right side of the breadboard). It outputs to a +5v and -5v solder points (next to the usb connector) where we added wires to power our 5v rail (left side of the breadboard). It also outputs USB that powers the pi. We might drop the USB cable and run jumper wires from the 5v rail on the breadboard to the +5 and ground pins on the pi to power it (which works but they are not soft-fused inputs like the micro usb port). This is a DROK regulator and it's awesome, it can accept from something like 4-40 volts and outputs 5. There is a trace that you have to cut on the PCB if you want the USB port powered when the regulator starts up, otherwise you have to push a little button on the regulator to enable power to the USB output...every time.
The first stage relays
To drive that big 12v automotive relay, we're using some small relays with a 5v coil. If you look closely there are two identically setup relay circuits on that breadboard. The one on the right is the system ready/busy relay. The one on the left is the locked/unlocked relay.
Each relay has 5v going into the coil, and the other end of the relay coil connects to the collector pin of a transistor. The emitter pin goes to ground. An LED also is powered by the 5v rail and grounds through the collector on the emitter. A wire from GPIO feeds through a 330 ohm resister (so you don't pull more than 16ma from the pi's GPIO) into the Base pin of the transistor. Each relay has a blue LED that comes on when powered. I did measure the draw in mA from the pi which is about 6-7 and well below the 16 or so that can damage it.
Although this pair of yellow relays are DPDT (dual pole dual terminal) we're only using one side of it. Notice the black wire going from the +12v rail on the bottom into the input of the relay, and both output poles go to separate block terminals (and there is one common ground). So then there is a red momentary button, followed by four LEDs, Red, Green, Red, Green.
The left red/green LEDs tell you if the system is granting access.
The right red/green LEDs tell you if the system is ready.
Notice that the 1st and 3rd terminal on that block have some extra crap going to them. First, there are a set of wires for GND and +12v that power the coil of the big relay. Secondly, a diode hooked up in reverse polarity to protect against transient voltage output from the big relay's coil when you stop powering it. Apparently when you stop powering a relay coil, it's magnetic field collapses and it can generate and output some power and it is helpful to have a diode or relay to suck some of that transient voltage up and protect your circuit. This is why I put a diode on all three relays and one on the strike plate (which shipped with it). Anyways Google/Wikipedia are here to help!
It is important that you use GPIO pins that are OFF, or "Low" by default (boot state) otherwise your system could say it's ready or grant access before it should. We used GPIO pins 17 and 27.
System is ready, but being manually triggered.
So pushing the red button grounds the access relay and bypasses the transistor hooked to GPIO. This will in turn trigger the bigger relay. This is different than a "push to exit" button which you'd probably have interrupt power to the lock with a normally closed momentary switch.
The strike plate
This strike plate unlocks when powered, which happens when the big relay is triggered, which is triggered when the smaller access relay is triggered, which is triggered via GPIO from the pi.
The serial interface
The barcode scanner that we were using (not pictured) is a USB handheld type with a pistol grip. The interface was USB to a modified RJ45 with an extra pin on either side but they are unused. We were able to figure out the pinout and hook an RJ45 jack (bottom left) that allows us to use a regular Ethernet cable straight to our scanner. We had to scan config barcodes that put the device into serial mode and disable the carriage return. Anyways pin 1 goes to the +5v rail, pin 2 to the -5v rail, pin 7 to ground on the pi, and pin 8 to the serial GPIO pin which is GPIO 15 (RXD UART). I believe this differed significantly from the regular serial to RJ45 pinout. I don't remember exactly where it was, but in the latest Raspian build the serial UART is disabled by default.
The code
We wrote everything in python, and aside from reading serial being a bit of a pain, we were able to put everything together with just a few simple functions. The nice thing is that by nature this app needs to be single threaded because it does everything in a sequence. The pi runs a loop that listens on the serial GPIO/UART/RXD pin for a credential to be scanned which is sent as a string, checks if it has access (a basic if statement right now), then calls a function that triggers the relay (if the person is authorized). There is a function to set the state, where you pass it "ready" or "busy" and it sets the "state" relay accordingly that lights up green when ready or red when busy. The beginning of the loop sets the state to ready, and as soon as it gets data from the scanner it sets state to busy until it's checked the person and granted access (if applicable).
The main loop. It runs indefinitely and should never stop, unless there is an exception, which if detected will clean up the GPIO outputs.
Setting up serial. Serial is kind of a pain in the ass to get working but extremely powerful. As I mentioned before there was a bug with raspeian where UART RXD Serial was disabled by default. I think until we found to enable it we weren't able to find /dev/ttyS0.
Setting up the GPIO outputs. 17 outputs the ready state, 27 triggers the access relay. Notice the "initial=0" arguement which is optional, it makes them not set high as soon as you set them up.
Function that gets passed "ready" or "busy" and sets the status relay accordingly There may or may not be status lights connected to this relay as it is for optional outputs.
Function that keeps reading from serial until it gets something other than an empty string, then returns it.
Function that when passed a credential, returns a 1 or 0 based on if it recommends access or not. In this example it is a dummy function that sleeps for a second to simulate a backend lookup. If you scan a can of vanilla coke zero it will grant you access, as the test value is the UPC code of said can.
Function that gets called when access is granted. It's passed a number of seconds as an arguement and will grant access (power GPIO27, which triggers the transistor, which triggers the small access relay which triggers the large access relay) for the passed number of seconds.
The main loop. It runs indefinitely and should never stop, unless there is an exception, which if detected will clean up the GPIO outputs.
Setting up serial. Serial is kind of a pain in the ass to get working but extremely powerful. As I mentioned before there was a bug with raspeian where UART RXD Serial was disabled by default. I think until we found to enable it we weren't able to find /dev/ttyS0.
Setting up the GPIO outputs. 17 outputs the ready state, 27 triggers the access relay. Notice the "initial=0" arguement which is optional, it makes them not set high as soon as you set them up.
Function that gets passed "ready" or "busy" and sets the status relay accordingly There may or may not be status lights connected to this relay as it is for optional outputs.
Function that keeps reading from serial until it gets something other than an empty string, then returns it.
Function that when passed a credential, returns a 1 or 0 based on if it recommends access or not. In this example it is a dummy function that sleeps for a second to simulate a backend lookup. If you scan a can of vanilla coke zero it will grant you access, as the test value is the UPC code of said can.
Function that gets called when access is granted. It's passed a number of seconds as an arguement and will grant access (power GPIO27, which triggers the transistor, which triggers the small access relay which triggers the large access relay) for the passed number of seconds.
Operation
So the script is a service and runs as the pi starts. So basically you power the whole board and in about 9 seconds the pi boots and the state changes to "ready" when the script starts and sets the ready state. You scan a barcode and the state immediately changes to "busy". It looks to see if you have access, and if so triggers the door, and goes back to "ready". If you don't have access, it just goes back to "ready" without granting access. Basically if you are holding a can of vanilla coke zero you have access (as that's the UPC we granted). If you are holding a jug of tide or something else, you don't have access. We put a time.wait in our lookup function to simulate a database lookup or back end call to an authentication server.