5

I am trying to make a door swipe card system in Python for my Raspberry Pi. I broke the program into two: A Door Alarm and a Card Swipe Recording system. The two programs work individually but how do I combine the two programs into one python file? I've tried threading but it doesn't seem to work.

Below are the programs: 1.) Door Alarm: If door is left open for a certain duration, an led will blink, then an alarm will ring

import time
import RPi.GPIO as gpio

led = 37
buzzer = 11
door = 16

gpio.setmode(gpio.BOARD)
gpio.setwarnings(False)
gpio.setup(buzzer, gpio.OUT)
gpio.setup(led, gpio.OUT)
gpio.setup(door, gpio.IN, pull_up_down=gpio.PUD_UP)


def blink(buzzer):
    gpio.output(buzzer, True)
    time.sleep(0.1)
    gpio.output(buzzer, False)
    time.sleep(0.1)
    return

def blink(led):
    gpio.output(led, True)
    time.sleep(1)
    gpio.output(led, False)
    time.sleep(1)
    return

while True:
    if gpio.input(door):
        time.sleep(3)
        for i in range(0,5):
                blink(led)
        for i in range (0,5):
                blink(buzzer)
    else:
        gpio.output(buzzer, False)

gpio.cleanup()      

2.) Card Swipe Recording System: When someone swipes their card, the led blinks and a picture is taken

import datetime
import time
import os
import RPi.GPIO as gpio

led = 37
t = datetime.datetime.now()

gpio.setmode(gpio.BOARD)
gpio.setwarnings(False)
gpio.setup(led, gpio.OUT)

def blink(led):
    gpio.output(led, True)
    time.sleep(0.1)
    gpio.output(led, False)
    time.sleep(0.1)

while True:
    card = raw_input()
    f = open("Laptop Sign Out" + '.txt', 'a')
    f.write("OneCard Number: " + card[1:10] + " Time: " + t.strftime("%m-%d-%Y %H:%M:%S"))
    f.write('\n')
    f.write(';')
    f.write('\n')
    f.close()
    time.sleep(1)
    for i in range(0,3):
        blink(led)
    os.system('fswebcam ~/Desktop/Photos/%H%M%S.jpeg')
    time.sleep(3)

gpio.cleanup()

(UPDATE) Also, below is my attempt at threading:

import time
import RPi.GPIO as gpio
import os
import datetime
from threading import Thread

led = 37
buzzer = 11
door = 16
t = datetime.datetime.now()

gpio.setmode(gpio.BOARD)
gpio.setwarnings(False)
gpio.setup(buzzer, gpio.OUT)
gpio.setup(led, gpio.OUT)
gpio.setup(door, gpio.IN, pull_up_down=gpio.PUD_UP)


def blink(buzzer):
    gpio.output(buzzer, True)
    time.sleep(0.1)
    gpio.output(buzzer, False)
    time.sleep(0.1)
    return

def blink(led):
    gpio.output(led, True)
    time.sleep(1)
    gpio.output(led, False)
    time.sleep(1)
    return

def doorsensor():
    while True:
        if gpio.input(door):
            time.sleep(3)
            for i in range(0,5):
                    blink(led)
            for i in range (0,5):
                    blink(buzzer)
        else:
            gpio.output(buzzer, False)

def cardreader():
    while True:
        card = raw_input()
        f = open("Laptop Sign Out" + '.txt', 'a')
        f.write("OneCard Number: " + card[1:10] + " Time: " + t.strftime("%m-%d-%Y %H:%M:%S"))
        f.write('\n')
        f.write(';')
        f.write('\n')
        f.close()
        time.sleep(1)
        for i in range(0,3):
            blink(led)
        os.system('fswebcam ~/Desktop/Photos/%H%M%S.jpeg')
        time.sleep(3)

f1 = Thread(target = doorsensor())
f2 = Thread(target = cardreader())

f2.start()
f1.start()


gpio.cleanup()   
Draenokh
  • 79
  • 1
  • 8
  • 2
    Please post the threading code you tried – Dziugas Jul 05 '17 at 14:18
  • 1
    Can you show your `threading` attempt? And what is the reason you want to have both programs in one file? – a_guest Jul 05 '17 at 14:19
  • 3
    If I may ask why do you think these should *not* be 2 different programs? – DeepSpace Jul 05 '17 at 14:19
  • For this project, I was asked to combine the 2 programs into one for the sake of convenience. However, I've been stuck on this combining issue for a few days and I've decided to ask the internet for help. – Draenokh Jul 05 '17 at 14:29
  • Could it be that your program exits more or less as soon as it starts? I think you need to `join` one of your threads. – Right leg Jul 05 '17 at 14:44

4 Answers4

1

I'm presenting a thread-less approach. The idea is to turn your while bodies into update functions, and call them alternatively.

First off, your door loop becomes

def update_door():
    if gpio.input(door):
        time.sleep(3)
        for i in range(0,5):
                blink(led)
        for i in range (0,5):
                blink(buzzer)
    else:
        gpio.output(buzzer, False)

Then your card swipe recording system becomes

def update_card():
    card = raw_input()
    f = open("Laptop Sign Out" + '.txt', 'a')
    f.write("OneCard Number: " + card[1:10] + " Time: " + t.strftime("%m-%d-%Y %H:%M:%S"))
    f.write('\n')
    f.write(';')
    f.write('\n')
    f.close()
    time.sleep(1)
    for i in range(0,3):
        blink(led)
    os.system('fswebcam ~/Desktop/Photos/%H%M%S.jpeg')
    time.sleep(3)

Finally, your main loop becomes:

while True:
    update_door()
    update_card()

But a problem arises: time.sleep in update_card will delay update_door as well. Here, you have three solutions:

1 - It's ok if update_door is delayed

Well, ok.

2 - It's not ok if update_door is delayed, but it's ok if update_card is not delayed

Then just remove time.sleep(3).

3 - You need update_door not to be delayed, and update_card to be delayed

Then you can set a manual timer, using the time module.

lastCardUpdate = time.time()
while True:
    update_door()
    now = time.time()
    if now - lastCardUpdate >= 3:
        update_card()
        lastCardUpdate = now

But raw_input in update_card is a blocking method, waiting for a user input. If you do need this user input to happen every three seconds, then this approach cannot be used. If you can move it before the while, ie outside of the update_card function, then it's fine. Else, you will indeed need to use threads.

Right leg
  • 16,080
  • 7
  • 48
  • 81
  • After dealing with the various bloink functions and also, will `raw_input` block? – doctorlove Jul 05 '17 at 14:32
  • @doctorlove Oh. Of course it will. Well, a thread will be necessary then. I'm editing my answer. – Right leg Jul 05 '17 at 14:39
  • @Draenokh Certainly because of `raw_input`. – Right leg Jul 05 '17 at 14:43
  • It's ok if update_door is delayed. I tried your code. The update_card function works fine but the update_door function doesn't work. Any idea why? There's no warning of any errors in my code within my terminal – Draenokh Jul 05 '17 at 14:46
  • @Draenokh As i wrote in my last paragraph, `raw_input` is a blocking function, so I suspect it to halt the execution of `update_door`. Because of that blocking method, you do need to use multithreading. Check my other answer in which I address that problem. – Right leg Jul 05 '17 at 14:50
1

You need to pass your thread functions as the target arguments, not their return values:

import sleep

f1 = Thread(target=doorsensor) # Remove parentheses after doorsensor
f1.daemon = True
f1.start()
f2 = Thread(target=cardreader) # Remove parentheses after cardreader
f2.daemon = True
f2.start()

# Use a try block to catch Ctrl+C
try:
    # Use a while loop to keep the program from exiting and killing the threads
    while True:
        time.sleep(1.0)
except KeyboardInterrupt:
    pass

gpio.cleanup()

The daemon property is set on each thread so that the program will exit when only the daemon threads are left:

A thread can be flagged as a “daemon thread”. The significance of this flag is that the entire Python program exits when only daemon threads are left. The initial value is inherited from the creating thread. The flag can be set through the daemon property.

0

If you are attempting to run these two programs simultaneously, then you will have to use either threading or multiprocessing, which you say you have attempted. If you have, may we see your attempt as we may be then able to help you with your issue there.

One other issue is that all of your methods are named Blink, which is not allowed in Python, in python all of your methods should have different names.

Edit: For threading make sure to type threading.Thread(target = target) as your code

Professor_Joykill
  • 919
  • 3
  • 8
  • 20
  • It's absolutely allowed to define a method several time using the same name. Every `def` will overwrite the previous value of the function. – Right leg Jul 05 '17 at 14:25
  • I've posted my attempt at threading. Please let me know what you think – Draenokh Jul 05 '17 at 14:27
  • @ Right Leg Sorry I thought you can not use the same name. Cause if you wanted to call all of the methods wouldn't there be an issue unless you use certain work arounds as discussed in this [stack exchange discussion](https://stackoverflow.com/questions/22377338/how-to-write-same-name-methods-with-different-parameters-in-python) – Professor_Joykill Jul 05 '17 at 14:28
0

Regardless of any other mistake, you need to join one of your threads once they have been started.

f1 = Thread(target = doorsensor())
f2 = Thread(target = cardreader())

f2.start()
f1.start()

f1.join()

What does f1.join() do?

Basically, it tells Python to wait until f1 has finished running. If you don't do so, the program will start f1 and f2, then will exit. Upon exiting, Python will release all the resources, including those two threads, whose execution will stop.

Right leg
  • 16,080
  • 7
  • 48
  • 81
  • I've tried that and the cardreader doesn't work at all. I suspect that it might be the os.system call messing with the Thread but do you have any clue as to why the cardreader doesn't work. – Draenokh Jul 05 '17 at 15:24
  • Turns out that only the first thread that is declared will work. Say if f2 is declared first (f1 = Thread(target = doorsensor()), then only f2 will work. The next declared thread won't work. Any idea why? – Draenokh Jul 05 '17 at 15:35