The History
I looked back at previous posts and it appears I have been working on this project for nearly 3 years!
The first part, being able to open the garage doors using wifi from a browser on my phone, has been up and running with various degrees of success for most of that time, but other areas have taken longer to get right.
So, what is this project? A few years ago I had our garage doors replaced with more modern sectional doors, fully insulated and much quieter than the old ones. The new doors came with a total of 6 wireless controls, 4 keyfob controls, two of which are on our key rings, and two of which are by the sunvisors of our two vehicles. There are also two wall mounted wireless controls. All 6 of these controls control both doors, so altogether a flexible package. However, just occasionally I will be outside the house without my keys and need to get into the garage to grab some tools or similar, which can mean a long walk around the house to get back in through the only unlocked door! The doors come with an optional WIFI ready controller, and an app for controlling the doors, but I felt the cost at the time was a bit prohibitive.
Feeling like an adventure and a foray into home automation, I decided to create a do it yourself solution. The garage doors come with a connector allowing a momentary switch to be wired across it, allowing the door to be opened without using the wireless remotes (just like in the old days!). I have some experience in working with Arduino and Raspberry PI processors, so I started planning a solution.
These are previous posts about the development
The Doors
I have no skills in writing phone apps, but I am experienced in quite technical web page design and writing, and if I was to use the web approach, I was probably better using a Raspberry PI.
I have already documented the development of this part of the project, the result of which was an Onion Omega2+ internet of things (IOT) computer, running a web server (a lite version that came with the Omega), and PHP and Python. The Omega was plugged into an expansion board, and a 2 relay hat attached.
The Web Page
The web page has evolved into a single piece of HTML with a few buttons, using CSS to give a consistent simple look. It can live on any web server on the network, but is not on the door opener Omega any longer. When a button to open a door is pressed, a Javascript function is called, which makes an asynchronous call to a PHP script on the web server on the door control Omega. This PHP script executes a Python script, which activates one of the two relays (very briefly), toggling the door open or closed. The web page can be used from any device with a web browser connected to the same wifi network, so two smart phones, a multitude of Windows 10 and Ubuntu computers, and even a Panasonic Smart TV can all control the automation!
This screen shot shows the complete webpage. The driveway camera is selected, and the inside lights switch has been activated.
The Cameras
Sounds great, but to make it a bit safer, I needed to be able to see that no one and no thing was close to the doors when opening or shutting. So I set up a Logitech C525 webcam on the Omega, pointing at the inside of the two doors. MJPG-Streamer was used to stream the image over the wifi system. The web page described above has a button that toggles the streamed image on and off on the web page. A second camera was attached to a second Omega2+ to show the driveway outside of the garage. This was an identical set up to the previous camera and Omega, just without the relays. Sadly, this second Omega gave up the ghost a few days ago and has been replaced, more about this later.
The Lights
So two garage doors controlled, and two cameras to make it safer…
But inside the garage is dark most of the time, and outside is also dark some of the time, and the cameras are not much use then. What we needed was to be able to turn the inside and outside lights on and off. Sounds like a job for some more relays. When I swapped the Raspberry PI with its 4 relay hat out, and replaced it with the Omega2+ and its 2 relay hat, I was left with a spare set of PI and relays – how convenient! The 4 relay hat worked beautifully with the low voltage low amperage current used to trigger the manual door opening, and the relays were rated up to 10 amps at 230v, so were perfect to switch lights on and off. The Seeed relay hat had some other benefits, the main one being the ability to be interrogated to find the present state of any of the relays. This would prove useful.
The two lights that need to be turned on and off are already switched on and off via normal switches, in fact they each can be turned on and off in two places using a clever bit of wiring. I didn’t want to lose any of this functionality, but I wanted to add a third switch in the circuit, controlled by the PI which can override, and be overridden by, the existing switches.

Our hallway lights are controlled by no less than 5 switches, any one of which can turn the lights on if they are off, or turn them off if they are on, regardless of which switch previously switched them off or on. This is achieved by an interesting, mind blowingly simple but at the same time just mindblowing piece of wiring. In the diagram below, the Crossover Switch can be repeated as many times as you like between the Main Switch and the Remote Switch, and each switch will reverse the current state of the circuit when operated.

The Crossover Switch in its manual form is a two gang, double throw switch, in other words two switches connected together, so a single rocker moves both switches at the same time. This can be replicated with two relays, activated together within the program code. With 4 relays in the hat, it gives two pairs to insert into each two switch circuit currently controlling the lights. Problem solved.
I could describe setting up the Raspberry Pi Model B and the SEEED Relay hat, but I won’t. I went through the process, got it working on the bench top and installed it into lighting circuits. Success – lights switch on and off as expected – for a while. After a few hours of use, the SEEED relay hat froze, causing the Raspberry PI to freeze in sympathy, and in extreme circumstances freezing my WIFI router!A few weeks investigation and research later, I discovered others were having the same problem. It appears that when mains voltage passes through the relay contacts. induction is set up in the relay board, which inteferes with the operation of the electronics. SEEED know about this, and very generously offerred to replace the relay board when I raised it with them. I didn’t take them up on the offer, they are based in China, and postage back to them made it not worthwhile.
What I did do was order a different, simpler more robust PI Relay Hat, from a company called BC Robotics in Canada. This one works using the GPIO pins rather than IC2 communication. I soldered on the 40 pin GPIO header connection ready to install on the Pi Model B, before I realised the PI Model B uses a 26 pin GPIO header! Bugger, the 3.5mm AV socket interfered with any possibilty of just using the first 26 pins. Fortunately, I had a Raspberry PI Zero Wifi on hand, intended for a later phase of the project, so that was duly set up with a web server, python and PHP, and RPi-GPIO, and all my code rewritten to work with GPIO instead of the IC2 interface. As mentioned above, the SEEED relay hat has this nice feature that allows the current state of any relay to be interrogated. I used this to keep a visual track of which lights were activated using the relays, showing the buttons on the web page in yellow when they were. This feature is not available using the GPIO pins, despite many web pages online saying it is. As the python scripts run as discreet process, with no continuity between one use and the next, normal programming variables were of no use either. In the end I used a simple work round, creating a file when a relay was activated, and deleting the file when it was deactivated. As the file is empty, it takes up no resources, in fact I am not sure it ever exists other than in some sort of cache somewhere.
The web page Lights buttons work the same way as the Door buttons, asynchronous javascript calls a PHP script, which executes a python script to activate the correct relay. The main difference is that this routine returns a status of the relay, showing it as on or off. The web page uses this to make the button yellow or blue. Additionally, when the page is first loaded the PHP script executes a python script that returns the relay status, setting the lights buttons to the correct colour.
The new PI and Hat work perfectly, with no long term issues.
Camera again
As I mentioned above, the second Onion Omega2+ failed (as I was moving it from one location to a better one). I tried to resurrect it, and in the end it bricked itself. As I don’t have an ethernet port for it, I am not able to re write the boot code, so I have abandoned it for now.
However, the PI Zero Wifi used to control the lights is in exactly the right place to run the second camera too. So I bought an On The Go (OTG) to USB-A adapter, and plugged the camera in. I installed Motion on the PI, and set up the Logitech C525 to feed and image to be streamed over the wifi. I changed both streams to use an image 640 by 360 pixels, which is the ratio used by both 720P cameras. I reduced the frame rate to 5FPS to give the Omega2+ and the PI some chance of processing the feeds correctly.
Moving the second camera to the PI, and setting up the cameras correctly has been a brilliant move, both streams are virtually real time, maybe 0.5 to 1 second delay, very good resolution, and great images. The doors and lights work almost instantly, and the Asynchonous Javascript in the webpage makes for an instant update on the screens.
Bonus Feature!
One morning I was lying awake wondering what to do next, and other research was going through my mind at the same time, when I realised I desperately needed a fingerprint recognition system outside the garage doors that would open the garage doors in response to the correct finger being presented. I mean, who doesn’t need this?
A company called Waveshare in China supplied the relevant hardware, and some software that passed itself off as a demonstration of the hardware capabilities. I had a Raspberry PI Model2B just waiting to repurposed, and I built up the required hardware and software to run the fingerprint reader. The fingerprint reader can remember up to 1000 fingerprints, each one of which can be assigned what it calls a “permit”, a single byte which can have up to 255 values. This allows an action to be recorded against a fingerprint. When a finger is pressed onto the reader, it is validated, and if valid, the “permit” is passed to the PI. The Python code on the PI, calls the PHP code on the door server, which uses the Python code on there to open the correct door, there are also permits to switch the lights on using the Lights server. The code that came with the Finger Print reader was a bit basic, and in places incorrect, so I rewrote it to make it more flexible and complete. I gave it the capability of recording the user’s name, which finger they are recording, and allowing the action to be chosen from a drop down. The interface is only available on a Linux terminal, so setting up a new user and fingerprint is still a bit of a specialist task, but it is a lot more straightforward and easy to use than the software that was supplied.
The hardware has been boxed up, weatherproofed, and is on the door post outside the garage. So I can now get into the garage even without my cell phone.
Job Done!
The Coding
The web page HTML.
The HTML could reside on any of the three computers above, all of them have web servers built in, and all are capable of running it. Just for fun though, it runs on a fourth computer, a recycled desktop set up as a NAS drive, using Ubuntu 20.04 Server edition, running headless. As a NAS drive it is really lightly loaded, and is on 24/7. The operating system runs off an SSD, and the main drives for the file server spin down after a few minutes of no use.
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Tiny Net</title>
<style type="text/css">
body {background-color:lightblue; font-family: arial;}
h1 {text-align: center; color:blue;}
h3 {text-align: center; color:blue;}
img {width:100%; height:auto; display: block; margin: 5 auto;}
.inputform {width:100%; margin:auto; }
.buttonline {width:300px; margin:auto;}
.button {height:30px; width:100px; margin:10px 23px 10px 23px;
background-color: blue; color:white;}
.notification {margin:auto; text-align:center; color:blue; margin:15px;}
.footer {
width: 100%;
bottom: 10px;
text-align: center;
font-size: 8pt;
}
</style>
<script>
function callpython(pythonscript,location,controlid) {
if (location == 'doors') {
url = "http://192.168.1.99/";}
else {
url = "http://192.168.1.95/";}
url = url + "phpcallpython.php?pythonscript=" + pythonscript;
// document.getElementById("debugtext1").innerHTML = url;
var xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
if (this.responseText == 'On') {
document.getElementById(controlid).style.backgroundColor = 'yellow';
document.getElementById(controlid).style.color = 'black';}
if (this.responseText == 'Off') {
document.getElementById(controlid).style.backgroundColor = 'blue';
document.getElementById(controlid).style.color = 'white';}
// document.getElementById("debugtext2").innerHTML = this.responseText;
}
}
xmlhttp.open("GET", url, true);
xmlhttp.send();
}
function checkrelaystatus () {
callpython('light1_status.py','lights','button3');
callpython('light2_status.py','lights','button4');
}
function showgarage () {
if (document.getElementById("garagecam").innerHTML != "") {
document.getElementById("garagecam").innerHTML = "";
} else {
document.getElementById("garagecam").innerHTML = '<img src= "http://192.168.1.99:8080/?action=stream"/>';
}
}
function showdriveway () {
if (document.getElementById("drivecam").innerHTML != "") {
document.getElementById("drivecam").innerHTML = "";
} else {
document.getElementById("drivecam").innerHTML = '<img src= "http://192.168.1.95:8081/?action=stream"/>';
}
}
</script>
</head>
<body onload="checkrelaystatus()">
<div class="inputform">
<h3>Lights</h3>
<div class="buttonline">
<button class="button" id="button4" onclick="callpython('light2_toggle.py','lights','button4')">Inside</button>
<button class="button" id="button3" onclick="callpython('light1_toggle.py','lights','button3');">Outside</button>
</div>
<h3>Camera</h3>
<div class="buttonline">
<button class="button" onclick="showgarage()">Garage</button>
<button class="button" onclick="showdriveway()">Driveway</button>
</div>
<h3>Action</h3>
<div class="buttonline">
<button class="button" onclick="callpython('relay_flick_1.py','doors')">North</button>
<button class="button" onclick="callpython('relay_flick_2.py','doors')">South</button>
</div>
<div class="notification" id="debugtext1"></div>
<div class="notification" id="debugtext2"></div>
</div>
<div id="garagecam"></div>
<div id="drivecam"></div>
<div id="myfooter" class="footer">
Copyright Dave Glover (2020<?php if(date('Y') != 2020){echo ' - '.date('Y');}?>)
</div>
</body>
</html>
The PHP used on the Doors server.
The first line allows cross origin access via Javascript. Not actually essential in this context, but it is there anyway.
The code executes a python script, on the Omega system there is no need to run the command as Sudo as there is in Raspbian
<?php
header("Access-Control-Allow-Origin:*");
$pythonscript = $_REQUEST["pythonscript"];
$pythonexec = "python";
$pythonlib = "/www/omegarelay/";
$cmd = $pythonexec." ".$pythonlib.$pythonscript;
exec($cmd, $output);
?>
The python used to open a door on the Doors server.
This uses Omega specific run time libraries (as the Relay Hat is an Onion Omega product).
A similar piece of code opens the second door.
from OmegaExpansion import relayExp
import sys
import time
addr = 7
relayExp.setVerbosity(0)
ret = relayExp.checkInit(addr)
if (ret != 1):
ret = relayExp.driverInit(addr)
ret = relayExp.setChannel(addr, 0, 1)
time.sleep(.2)
ret = relayExp.setChannel(addr, 0, 0)
The PHP used on the Lights server.
The first line is required in this case, as this function returns values used on the webpage with the Javascript on it
Note the Sudo in the command.
<php header("Access-Control-Allow-Origin:*");
$pythonscript = $_REQUEST["pythonscript"];
$pythonexec = "/usr/bin/python3.7";
$pythonlib = "/var/www/pirelay/";
$cmd = 'sudo '.$pythonexec." ".$pythonlib.$pythonscript;
echo exec($cmd);
?>
The Python to turn a light on
#!/usr/bin/python
'''*****************************************************************************************************************
********************************************************************************************************************'''
from Relays import *
if os.path.exists("light1"):
relay_off(relay1)
relay_off(relay2)
light_unset("light1")
print ('Off')
else:
relay_on(relay1)
relay_on(relay2)
light_set("light1")
print ('On')
The Python to check the status of a relay
#!/usr/bin/python
from Relays import *
light_status ("light1")
The python code to perform relay actions
# =========================================================
import RPi.GPIO as GPIO
import os
relay1 = 4
relay2 = 17
relay3 = 27
relay4 = 22
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
def relay_on(relay_num):
GPIO.setup(relay_num, GPIO.OUT)
GPIO.output(relay_num, GPIO.HIGH)
def relay_off(relay_num):
GPIO.setup(relay_num, GPIO.OUT)
GPIO.output(relay_num, GPIO.LOW)
def light_status(light_num):
if os.path.exists(light_num):
print ("On")
else:
print ("Off")
def light_set(light_num):
f = open(light_num, "x")
f.close()
def light_unset(light_num):
if os.path.exists(light_num):
os.remove(light_num)
Finally, the finger print reader code.
First the main module
#!/usr/bin/env python3.5
# -*- coding:utf-8 -*-
# Fingerprint reader by Dave Glover, May 2020
# Derived from test source provided by Waveshare here -
# https://www.waveshare.com/wiki/File:Capacitive-Fingerprint-Reader-Code.7z
# Notable corrections - ACK_USER_EXIST is value 0x07 not 0x06
# - Code based on count of users fails when a facility to delete
# fingerprints is added. A new function to find the highest userid in use is required
# before new fingerprints can be registered.
# Notable additions or alterations
# 1. A userfile of user names and the finger being used is kept in synch with the system provided user detail
# 2. A function to clear an individual user is provided, and to clear all users.
# 3. Two LEDs are catered for, so that success or failure when using the FingerPrint reader sensor can be seen
# and when a fingerprint is recognised, a request using a URL on another machine will open garage doors
# 4. The program defaults to sleep mode when started which means the application
# can be started at boot time, and it will be in automatic mode by default.
# The following line can be added to the rc.local file before the 'exit 0' line
# in order to start it at boot, with all output redirected into GNU screen
# su pi -c 'screen -d -m -S test /python path/python3.7 /your path/main.py'
# Add this line "screen -r" to the end of .bashrc to start automatically after SSH&login
from Capacitive_Fingerprint_Reader import *
def main():
GPIO.output(Finger_RST_Pin, GPIO.LOW)
time.sleep(1)
GPIO.output(Finger_RST_Pin, GPIO.HIGH)
time.sleep(1) # Wait for module to start
while SetCompareLevel(5) != 5:
print ("***ERROR***: system type error")
time.sleep(5)
print (" Compare Level: %d " % GetCompareLevel());
print (" Fingerprints on system: %d " % GetUserCount())
m=showmenu()
thread_Auto_Verify_Finger = threading.Thread(target=Auto_Verify_Finger,args=())
thread_Auto_Verify_Finger.setDaemon(True)
thread_Auto_Verify_Finger.start()
while True:
print (" Please input command, from the menu:",end = "\n ")
PC_Command_RxBuf.append(input())
Analysis_PC_Command()
del PC_Command_RxBuf[:] # Clear PC_Command_RxBuf and prepare for the next time
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
if ser != None:
ser.close()
GPIO.cleanup()
print("\n\n Fingerprint Reader Terminated \n")
sys.exit()
And then the real work
#!/usr/bin/env python3.5
# -*- coding:utf-8 -*-
# Fingerprint reader by Dave Glover, May 2020
# Derived from test source provided by Waveshare here -
# https://www.waveshare.com/wiki/File:Capacitive-Fingerprint-Reader-Code.7z
import serial
import time
import threading
import sys
import RPi.GPIO as GPIO
import os
TRUE = 1
FALSE = 0
# Basic response message definition
ACK_SUCCESS = 0x00
ACK_FAIL = 0x01
ACK_FULL = 0x04
ACK_NO_USER = 0x05
ACK_USER_EXIST = 0x07
ACK_TIMEOUT = 0x08
ACK_GO_OUT = 0x0F # The center of the fingerprint is out of alignment with sensor
# User information definition
ACK_NORTH = 0x01
ACK_SOUTH = 0x02
ACK_INSIDE = 0x03
ACK_OUTSIDE = 0x04
USER_MAX_CNT = 1000 # Maximum fingerprint number
# Command definition
CMD_HEAD = 0xF5
CMD_TAIL = 0xF5
CMD_ADD_1 = 0x01
CMD_ADD_2 = 0x02
CMD_ADD_3 = 0x03
CMD_MATCH = 0x0C
CMD_DEL = 0x04
CMD_DEL_ALL = 0x05
CMD_USER_CNT = 0x09
CMD_COM_LEV = 0x28
CMD_GET_ALL_USERS = 0x2B
CMD_LP_MODE = 0x2C
CMD_TIMEOUT = 0x2E
CMD_FINGER_DETECTED = 0x14
Finger_WAKE_Pin = 23
Finger_RST_Pin = 24
Green_LED_Pin = 18
Red_LED_Pin = 17
GPIO.setmode(GPIO.BCM)
GPIO.setup(Finger_WAKE_Pin, GPIO.IN)
GPIO.setup(Finger_RST_Pin, GPIO.OUT)
GPIO.setup(Finger_RST_Pin, GPIO.OUT, initial=GPIO.HIGH)
GPIO.setup(Green_LED_Pin, GPIO.OUT)
GPIO.setup(Green_LED_Pin, GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(Red_LED_Pin, GPIO.OUT)
GPIO.setup(Red_LED_Pin, GPIO.OUT, initial=GPIO.LOW)
g_rx_buf = []
PC_Command_RxBuf = []
Finger_SleepFlag = 1
userid = 0
permits = 0
rLock = threading.RLock()
ser = serial.Serial("/dev/ttyAMA0", 19200)
##
#urls for each action registerd in ACK-PERMIT
#specifies IP of server, module called, and name of script to be run
#
urls = ["http://192.168.1.99/phpcallpython.php?pythonscript=relay_flick_1.py",
"http://192.168.1.99/phpcallpython.php?pythonscript=relay_flick_2.py",
"http://192.168.1.95/phpcallpython.php?pythonscript=light1_toggle.py",
"http://192.168.1.95/phpcallpython.php?pythonscript=light2_toggle.py"]
#***************************************************************************
# @brief send a command, and wait for the response of module
#***************************************************************************/
def TxAndRxCmd(command_buf, rx_bytes_need, timeout):
global g_rx_buf
CheckSum = 0
tx_buf = []
tx_buf.append(CMD_HEAD)
for byte in command_buf:
tx_buf.append(byte)
CheckSum ^= byte
tx_buf.append(CheckSum)
tx_buf.append(CMD_TAIL)
ser.flushInput()
ser.write(tx_buf)
g_rx_buf = []
time_before = time.time()
time_after = time.time()
while time_after - time_before < timeout and len(g_rx_buf) < rx_bytes_need:
# Waiting for response
bytes_can_recv = ser.inWaiting()
if bytes_can_recv != 0:
g_rx_buf += ser.read(bytes_can_recv)
time_after = time.time()
if len(g_rx_buf) != rx_bytes_need:
return ACK_TIMEOUT
if g_rx_buf[0] != CMD_HEAD:
return ACK_FAIL
if g_rx_buf[rx_bytes_need - 1] != CMD_TAIL:
return ACK_FAIL
if g_rx_buf[1] != tx_buf[1]:
return ACK_FAIL
CheckSum = 0
for index, byte in enumerate(g_rx_buf):
if index == 0:
continue
if index == 6:
if CheckSum != byte:
return ACK_FAIL
CheckSum ^= byte
return ACK_SUCCESS;
#***************************************************************************
# @brief Show the text menu
#***************************************************************************/
def showmenu():
if Finger_SleepFlag == 1:
print ("\n Module in sleep mode. Only WAKE is valid\n")
print (" Main Menu")
print (" WAKE : Wake up to allow commands")
else:
print ("\n Module is awake. All commands valid\n")
print (" Main Menu")
print (" COUNT : Count existing fingerprints")
print (" ADD : Register fingerprint")
print (" MATCH : Fingerprint matching")
print (" CLEAR : Clear fingerprints ")
print (" LIST : List Users")
print (" SLEEP : Switch to background sleep mode")
#***************************************************************************
# @brief Get Compare Level
#***************************************************************************/
def GetCompareLevel():
global g_rx_buf
command_buf = [CMD_COM_LEV, 0, 0, 1, 0]
r = TxAndRxCmd(command_buf, 8, 0.1)
if r == ACK_TIMEOUT:
return ACK_TIMEOUT
if r == ACK_SUCCESS and g_rx_buf[4] == ACK_SUCCESS:
return g_rx_buf[3]
else:
return 0xFF
#***************************************************************************
# @brief Set Compare Level,the default value is 5,
# can be set to 0-9, the bigger, the stricter
#***************************************************************************/
def SetCompareLevel(level):
global g_rx_buf
command_buf = [CMD_COM_LEV, 0, level, 0, 0]
r = TxAndRxCmd(command_buf, 8, 0.1)
if r == ACK_TIMEOUT:
return ACK_TIMEOUT
if r == ACK_SUCCESS and g_rx_buf[4] == ACK_SUCCESS:
return g_rx_buf[3]
else:
return 0xFF
#***************************************************************************
# @brief Query the number of existing fingerprints
#***************************************************************************/
def GetUserCount():
global g_rx_buf
command_buf = [CMD_USER_CNT, 0, 0, 0, 0]
r = TxAndRxCmd(command_buf, 8, 0.1)
if r == ACK_TIMEOUT:
return ACK_TIMEOUT
if r == ACK_SUCCESS and g_rx_buf[4] == ACK_SUCCESS:
return g_rx_buf[3]
else:
return 0xFF
#***************************************************************************
# @brief Read through the list of users to find the highest userid in use
#***************************************************************************/
def GetLastUser():
global g_rx_buf
#Get a count of users
lastuser = 0
r=GetUserCount()
#calculate length of buffer to read user details
if r == 0:
return lastuser
else:
lenbuffer = 13 + (int(r)*3)
command_buf = [CMD_GET_ALL_USERS, 0, 0, 0, 0]
r = TxAndRxCmd(command_buf, lenbuffer, 1)
if r == ACK_TIMEOUT:
return ACK_TIMEOUT
if r == ACK_SUCCESS and g_rx_buf[4] == ACK_SUCCESS:
#print ("Success ", g_rx_buf, " ", len(g_rx_buf))
#Extract the userid count from data header
countuser = (g_rx_buf[9] * 256) + g_rx_buf[10]
i = 1
#loop through each tripe of userid * permission
while i < countuser + 1:
B1 = 8 + i*3
B2 = B1 + 1
#extract userid from data bytes
thisuser = (g_rx_buf[B1] * 256) + g_rx_buf[B2]
#check if this is the new highest
if thisuser > lastuser:
lastuser = thisuser
i = i + 1
return lastuser
else:
return 0xFF
#***************************************************************************
# @brief Get the time that fingerprint collection wait timeout
#***************************************************************************/
def GetTimeOut():
global g_rx_buf
command_buf = [CMD_TIMEOUT, 0, 0, 1, 0]
r = TxAndRxCmd(command_buf, 8, 0.1)
if r == ACK_TIMEOUT:
return ACK_TIMEOUT
if r == ACK_SUCCESS and g_rx_buf[4] == ACK_SUCCESS:
return g_rx_buf[3]
else:
return 0xFF
#***************************************************************************
# @brief Register fingerprint
#***************************************************************************/
def AddUser():
global g_rx_buf
global userid
global permits
userid = GetLastUser() + 1
if userid >= USER_MAX_CNT:
return ACK_FULL
print (" Users Name?")
username = input()
print (" Enter Digit e.g LT, LI, LM, LR, LL")
print (" or RT, RI, RM, RR, RL")
userdigit = input()
print (" Enter door(North or South) or Light (Inside or Outside)")
ActionText = " "
ActionText = input()
if ActionText == "North" or ActionText == "N" or ActionText == "n":
permits = ACK_NORTH
elif ActionText == "South" or ActionText == "S" or ActionText == "s":
permits = ACK_SOUTH
elif ActionText == "Inside" or ActionText == "I" or ActionText == "i":
permits = ACK_INSIDE
elif ActionText == "Outside" or ActionText == "O" or ActionText == "0":
permits = ACK_OUTSIDE
else:
permits = ACK_NORTH # default to north door
command_buf = [CMD_ADD_1, 0, userid, permits, 0]
print (" Place finger on sensor")
r = TxAndRxCmd(command_buf, 8, 5)
if r == ACK_TIMEOUT:
return ACK_TIMEOUT
if r == ACK_SUCCESS and g_rx_buf[4] == ACK_USER_EXIST:
return ACK_USER_EXIST
time.sleep(2)
if r == ACK_SUCCESS and g_rx_buf[4] == ACK_SUCCESS:
print (" Place finger again")
command_buf[0] = CMD_ADD_2
r = TxAndRxCmd(command_buf, 8, 5)
if r == ACK_TIMEOUT:
return ACK_TIMEOUT
time.sleep(2)
if r == ACK_SUCCESS and g_rx_buf[4] == ACK_SUCCESS:
print (" Place finger yet again")
command_buf[0] = CMD_ADD_3
r = TxAndRxCmd(command_buf, 8, 5)
if r == ACK_TIMEOUT:
return ACK_TIMEOUT
if r == ACK_SUCCESS and g_rx_buf[4] == ACK_SUCCESS:
#append user details to userfile
file = open("/var/www/pyfinger/userfile", "at")
userrecord = ",".join([str(userid), username, userdigit, str(permits), "\n"])
file.writelines(userrecord)
file.close
return ACK_SUCCESS
else:
return ACK_FAIL
else:
return ACK_FAIL
else:
return ACK_FAIL
#***************************************************************************
# @brief read the user file to find a matching user name for a fingerprint
#***************************************************************************/
def MatchUserDetails(userid):
infile = open("/var/www/pyfinger/userfile", "rt")
for line in infile.readlines():
newline = line
splitstr =newline.split(",",1)
uid = int(splitstr[0])
if uid == userid:
return line
infile.close
#***************************************************************************
# @brief delete user name etc for a deleted fingerpprint
#***************************************************************************/
def DeleteUserDetails(userid):
import os
infile = open("/var/www/pyfinger/userfile", "rt")
outfile = open("/var/www/pyfinger/newuserfile", "at")
for line in infile.readlines():
newline = line
splitstr =newline.split(",",1)
uid = int(splitstr[0])
if uid != userid:
outfile.write(newline)
infile.close
outfile.close
os.remove ("/var/www/pyfinger/userfile")
os.rename (r'/var/www/pyfinger/newuserfile',r'/var/www/pyfinger/userfile')
#***************************************************************************
# @brief List all the user names and setails on the userfile
#***************************************************************************/
def listusers():
if os.path.isfile("/var/www/pyfinger/userfile"):
file = open("/var/www/pyfinger/userfile", "rt")
print (" List of users")
print (" Userid Name Digit Permission")
for line in file.readlines():
splitstr =line.split(",")
userid = splitstr[0]
username = splitstr[1]
userdigit = splitstr[2]
userpermits = splitstr[3]
print (" ",userid, " ", username, " ", userdigit, " ", userpermits)
file.close
print (" End of list\n")
else:
print(" No Users Found")
#***************************************************************************
# @brief Clear fingerprints
#***************************************************************************/
def ClearUsers():
global g_rx_buf
count = GetLastUser()
print (" Delete users")
if count == 0:
print (" Nothing to delete")
return ACK_SUCCESS
else:
r=listusers()
print (" Enter userid from list (A for all)")
userid = input()
if userid =="A" or userid =="a":
userid = FALSE
command_buf = [CMD_DEL_ALL, 0, 0, 0, 0]
elif userid.isdigit():
userid = int(userid)
if (userid in range (1,count+1)):
command_buf = [CMD_DEL, 0, userid, 0, 0]
else:
print (" not a valid number")
return ACK_FAIL
else:
print (" Not a valid number")
return ACK_FAIL
r = TxAndRxCmd(command_buf, 8, 0.1)
if r == ACK_TIMEOUT:
return ACK_TIMEOUT
if r == ACK_SUCCESS and g_rx_buf[4] == ACK_SUCCESS:
if userid == FALSE:
print (" Deleting all users")
os.remove ("/var/www/pyfinger/userfile")
else:
print (" Deleting user ", userid)
r=DeleteUserDetails(userid)
return ACK_SUCCESS
else:
return ACK_FAIL
#***************************************************************************
# @brief Check if user ID is between 1 and 3
#***************************************************************************/
def IsPermittedUser(permits):
if permits == ACK_NORTH or permits == ACK_SOUTH or permits == ACK_INSIDE or permits == ACK_OUTSIDE:
return TRUE
else:
return FALSE
#***************************************************************************
# @brief Fingerprint matching
#***************************************************************************/
def VerifyUser():
global g_rx_buf
global userid
global permits
command_buf = [CMD_MATCH, 0, 0, 0, 0]
r = TxAndRxCmd(command_buf, 8, 5);
if r == ACK_TIMEOUT:
return ACK_TIMEOUT
elif r == ACK_SUCCESS and IsPermittedUser(g_rx_buf[4]):
permits = (g_rx_buf[4])
userid = (g_rx_buf[2]) * 256 + (g_rx_buf[3])
return ACK_SUCCESS
elif g_rx_buf[4] == ACK_NO_USER:
return ACK_NO_USER
elif g_rx_buf[4] == ACK_TIMEOUT:
return ACK_TIMEOUT
else:
return ACK_GO_OUT # The center of the fingerprint is out of alignment with sensor
#***************************************************************************
# @brief Read a finger print using the verify, then open the relevant door
# and flick the green LED on , or show a failure message with a red LED
#***************************************************************************/
def Fingerprint_Read():
global userid
global permits
print (" Read fingerprint")
print (" Waiting for finger")
r = VerifyUser()
if r == ACK_SUCCESS:
print (" Matched Userid ", MatchUserDetails(userid))
r = turn_green_led_on()
o = Perform_Action(permits)
if o == "failed":
r = turn_red_led_on()
print (" Failed URL/HTTP error")
elif r == ACK_NO_USER:
r = turn_red_led_on()
print (" Failed: fingerprint not found")
elif r == ACK_TIMEOUT:
r = turn_red_led_on()
print (" Failed: Time out !")
elif r == ACK_GO_OUT:
r = turn_red_led_on()
print (" Failed: Not centred or flat")
#***************************************************************************
# @brief Analysis the command from PC terminal
#***************************************************************************/
def Analysis_PC_Command():
global Finger_SleepFlag
global userid
global permits
cmd = PC_Command_RxBuf[0].upper()
if cmd == "COUNT" and Finger_SleepFlag != 1:
print (" Fingerprints on system: %d" % GetUserCount())
elif cmd == "ADD" and Finger_SleepFlag != 1:
print (" Add fingerprint")
r = AddUser()
if r == ACK_SUCCESS:
print (" Added Userid ", userid, " Permission ", permits)
elif r == ACK_TIMEOUT:
print (" Timed out!")
elif r == ACK_USER_EXIST:
print (" User finger already exists!")
elif r == ACK_FAIL:
print (" Failed: Centre finger on sensor")
elif r == ACK_FULL:
print (" Failed: fingerprint library full")
elif cmd == "MATCH" and Finger_SleepFlag != 1:
r=Fingerprint_Read()
time.sleep(1)
r = turn_green_led_off()
r = turn_red_led_off()
elif cmd == "CLEAR" and Finger_SleepFlag != 1:
ClearUsers()
elif cmd == "SLEEP" and Finger_SleepFlag != 1:
GPIO.output(Finger_RST_Pin, GPIO.LOW)
Finger_SleepFlag = 1
m=showmenu()
elif cmd == "WAKE":
if rLock.acquire(blocking=True, timeout=0.6) == True:
Finger_SleepFlag = 0
GPIO.output(Finger_RST_Pin, GPIO.HIGH)
time.sleep(1) # Wait for module to start
rLock.release()
m=showmenu()
elif cmd == "LIST" and Finger_SleepFlag != 1:
l =listusers()
else:
print (" Menu item not found")
m=showmenu()
#***************************************************************************
# @brief Flick the relay to open the garage door that corresponds to
# the fingerprints permission (1 North. 2 South
#***************************************************************************/
def Perform_Action(action):
import urllib.request
from urllib.error import URLError
# URL of the php code on the relevant server.
# If opening a door use garage door opener Onion Omega 2S webserver,
# If switching on a light use the TinyNet Raspberry PI server
#see URLS array at top of program
# All of these use EXEC SUDO to call a python script to perform the correct action
# this php module and python scripts are used by web pages to flick relays on and off.
url = urls[action-1]
# request the URL to be opened. No data or resonse is available
# but it needs the return variable to avoid timeouts
# network errors and HTTP errors are caught by the exceptions
try: r = urllib.request.urlopen (url)
except urllib.error.URLError as e:
print(e.reason)
return "failed"
else:
print ("URL = ", url)
return "OK"
#***************************************************************************
# @brief Turn LEDs on and off depending on status of finger print verification
#***************************************************************************/
def turn_green_led_on():
GPIO.output(Green_LED_Pin, GPIO.HIGH)
def turn_green_led_off():
GPIO.output(Green_LED_Pin, GPIO.LOW)
def turn_red_led_on():
GPIO.output(Red_LED_Pin, GPIO.HIGH)
def turn_red_led_off():
GPIO.output(Red_LED_Pin, GPIO.LOW)
#***************************************************************************
# @brief If you enter the sleep mode, then open the Automatic wake-up function of the finger,
# begin to check if the finger is pressed, and then start the module and match
#***************************************************************************/
def Auto_Verify_Finger():
global Finger_SleepFlag
# Put module into sleep mode by default to enable script to run at start up
# and go straight into fingerprint recognition mode
GPIO.output(Finger_RST_Pin, GPIO.LOW)
time.sleep(1)
Finger_SleepFlag = 1
while True:
if rLock.acquire() == True:
# If you enter the sleep mode, then open the Automatic wake-up function of the finger,
# begin to check if the finger is pressed, and then start the module and match
if Finger_SleepFlag == 1:
if GPIO.input(Finger_WAKE_Pin) == 1: # If you press your finger
time.sleep(0.01)
if GPIO.input(Finger_WAKE_Pin) == 1:
GPIO.output(Finger_RST_Pin, GPIO.HIGH) # Pull up the RST to start the module and start matching the fingers
time.sleep(1) # Wait for module to start
r=Fingerprint_Read()
#After the matching action is completed, drag RST down to sleep
GPIO.output(Finger_RST_Pin, GPIO.LOW)
#Sleep for 5 seconds to avoid kicking off the next loop before the pin is set low
#and give time for user to see LEDs
time.sleep (5)
r = turn_green_led_off()
r = turn_red_led_off()
rLock.release()