Skip to content

SOS Computer Rescue

Signs of Success Ltd

Menu
  • Home
  • SOS Computer Rescue
  • SOS Blog
    • LOMO Lubitel 166B Digital Camera – Page 1
    • Computer Stuff
      • Arduino
      • Onion Omega – Internet of Things
      • Programming
      • Raspberry Pi
      • Windows 10
    • Electric Bikes
      • Yuba Mundo eV4
      • Dual Battery Management System
    • Other Technology
    • MiniCab Miev
      • Squirrel – and our South Island camping road trip
      • Road Trip 2021 – Central Otago
      • Otago 2022 – another roadtrip
    • Sign Making
    • Registry Restore for all occasions
    • Login
  • About us
    • About us
    • SOS Electric Vehicles
    • SOS Company vehicle – Yuba Mundo V4 Electric Bike
    • Glover Family Webpage
  • Get in touch
  • What People Say
Menu

Garage Automation Project – near as dammit complete!

Posted on September 30, 2020October 8, 2020 by Dave

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

Garage door opener – tiny computer project

Garage Door opener – intermission

Garage Door Opener – a tinier computer project

Onion Omega – next steps

Raspberry PI and Raspbian Lite Buster

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.

two switch system

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.

three switch system

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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
 
<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

1
2
3
4
5
6
7
8
9
<?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.

1
2
3
4
5
6
7
8
9
10
11
12
13
 
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.

1
2
3
4
5
6
7
8
9
 
<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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#!/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

1
2
3
#!/usr/bin/python
from Relays import *
light_status ("light1")

The python code to perform relay actions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
 
# =========================================================
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
 
#!/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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
 
#!/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()
 

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

©2022 SOS Computer Rescue | Built using WordPress and Responsive Blogily theme by Superb