Rcmaveric
Posts: 5
Joined: Sun Oct 25, 2020 12:17 am
Location: Jax

Event triggered by how long GPIO device is active.

Wed Dec 02, 2020 6:26 pm

I am using LDRs to trigger callbacks inside my tkinter gui to trigger Relays and Buzzers. I am trying to figure out how create a function that will actively time the LDRs active state so that after x amount of time it will trigger additional events. Or count down while the device is active is another idea. Basicly the LDR will sense light and trip a relay, if the ldr is active say after 2 seconds the buzzer will sound and the gui will be reconfigure to to give a visual que. Pressing the button will clear the alarm.

here is small sample block of code pieced together from the larger program:

Code: Select all

import gpiozero as gp
import tkinter as tk
import time
from tkinter import ttk


S4 = gp.Button(24) #Binding Sensor
Ldr3 = gp.LightSensor(19) #Case Feeder 
Buzzer = gp.PWMOutputDevice(18, frequency=6000)
Relay2 = gp.OutputDevice(6, initial_value=False, active_high=True)

class Alarm (tk.LabelFrame):
    def __init__(self, master):
        tk.LabelFrame.__init__(self, master, text="Cautions")
        self.master = master
     #Alerts
      #Binding: This will be the same concept as the LDR's but will track how long the switch is inactive. So if switch is inactive for 1.5 seconds then event is triggered.
        binding_warning = ttk.LabelFrame(self, text="Binding")
        binding_warning.pack(side = "left") 
        binding_label = tk.Button(binding_warning, text="Good", background="green", height=2, width=5)
        binding_label.pack(fill = "both")

      #Low Cases
        low_case_warning = ttk.LabelFrame(self, text="Bullets")
        low_case_warning.pack(side = "left")
        self.low_case_label = tk.Button(low_case_warning, command = self.Low_Cases_Reset,
                                        text="Good", background="green", 
                                        height=2, width=5)
        self.low_case_label.pack(fill = "both")
        Ldr3.when_dark = Relay2.off 
        Ldr3.when_light = self.Low_Cases

    def Low_Cases(self): #Doesnt work. Should turn the Relay on. If the relay is active for longer than a second then set alarm and trip button alarm visual
        Relay2.on
        if (Relay2.value == 1):
            time.sleep(1)
            Buzzer.pulse()
            self.low_case_label.configure(bg="red", text = "Alert")
            print("Low Cases")
        
    def Low_Cases_Reset(self): # This will silence the buzzer and change the button back to green.
        Buzzer.off()
        self.low_case_label.configure(bg="green", text = "Good")
     
class App(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("AMMMO Computer")
        self.calls = Alarm(self)
        self.calls.pack()
        
if __name__ == "__main__":
    app=App()
    app.mainloop() 


Here is another sample of code i was using for a different alarm. I needed a time delay so false alarms would not be tripped. It was until I was trying to incorporate it into my code did I learn that that this code doesn't work like I thought it did. Though i could probably use the threshold value to prevent false alarms but i wanted a time function as an extra safe guard.

Code: Select all

startTime = time.time()

while True:
    if ldr.value > .8:
        endTime = time.time()
        #LDR.value has to be above .8 for 3 seconds to sound alarm. This prevents false alarms during primer feeding.
        if (endTime - startTime > 3):
            sleep(.2)
            buzzer.pulse(n=1)
            print("Low Primers")
            
    else:
        sleep(.2)
        buzzer.off()
        
It was not until i started playing with it more in depth that I found first time through the timing would be right. After that it would just be instant alarm and changing the > value had no affect. Only restarting the script would make the first go around accurate again. So that means its merely capturing the present time active and not actively tracking it. Basically the code is reactive and not proactive. Most other time examples only show how to capture how longs something happened after the fact. Is there a way track time in real time or add a timer function that counts down tripping the additional events. I think I may need to research threading to get this to work, but would that interfere with GPIOZero's threaded call backs? Would I be better off dropping GPIOZero and going to RPI.GPIO to get the functionality i am looking for? I have only just briefly started looking at the RPI.GPIO documentation. I saw in RPI.GPIO i could tie mulitiply functions to the same edge event. Then I could have one event close the relay. Then i could have another even that counts down to trigger the second event that is interrupted if the relay is triggered open.

This is another sample code i tried. The idea was the relay would close when light and open when dark. Then it would sleep for the time while the relay was active and then set the alarm. It doesn't work though:

Code: Select all

    Ldr3.when_dark = Relay2.off
    Ldr3.when_light = self.Low_Cases
        
def Low_Cases(self): #Doesnt work.
        Relay2.on #
        if (Relay2.value == 1): 
            time.sleep(2)
            Buzzer.pulse()
            self.low_case_label.configure(bg="red", text = "Alert")
            print("Low Cases")
            
            
I am running the current Python 3 and current GPIOZERO libraries and I use VSCode for the coding. I am an experience analog system electrician but inexperienced coder. So far I have found that the answers are out there it just takes me a week to get the question right to find it. This problem though has me completely stumped. I am not sure of where to begin to solve it because more than once things didnt quite work how I though.

I can post a link to full program if needed also.
Speak softly and carry a big stick; you will go far.

Theodore Roosevelt

DarkElvenAngel
Posts: 1396
Joined: Tue Mar 20, 2018 9:53 pm

Re: Event triggered by how long GPIO device is active.

Wed Dec 02, 2020 6:35 pm

If you want to stick with Python for this have a look at the argon one script. It does what you want. It uses shorter times than your asking for but modified it should work.

https://github.com/kounch/argonone/blob ... ned.py#L27 this might be helpful

Rcmaveric
Posts: 5
Joined: Sun Oct 25, 2020 12:17 am
Location: Jax

Re: Event triggered by how long GPIO device is active.

Thu Dec 03, 2020 7:45 pm

Awesome!! Thank you so much.

Code: Select all

import gpiozero as gp
import time


Ldr3 = gp.LightSensor(19) #Case Feeder 
Buzzer = gp.PWMOutputDevice(18, frequency=6000)
Relay2 = gp.OutputDevice(6, initial_value=False, active_high=True)

Relay2.source = Ldr3 #Ties the LDR to the relay better than when_light. 

while True: #Now we track and time the while active time.
    pulsetime = 0
    Ldr3.wait_for_light()
    time.sleep(0.1)
    while Ldr3.light_detected == True:
        time.sleep(0.2) #adjust this to change counter speed.
        pulsetime += 1
        print (pulsetime) # to check how it was counting. Wont be used latter.
        if pulsetime >= 15:
            Buzzer.pulse()
            #This would also trigger the GUI alert.
        if Ldr3.light_detected == False:
            Buzzer.off() #This part wont be needed in the GUI since pressing the button resets the alert
            
Counter is now working correctly and repeatedly. I am headed in the right direction now. I am researching threading with tkinter and how to tie this all together. Then I can have thread for each alert that can trigger a change in the GUI.
Speak softly and carry a big stick; you will go far.

Theodore Roosevelt

Rcmaveric
Posts: 5
Joined: Sun Oct 25, 2020 12:17 am
Location: Jax

Re: Event triggered by how long GPIO device is active.

Sat Dec 05, 2020 6:17 pm

I got everything working the way I wanted by making the time count down and using thread daemon. Without the count down portion the thread wouldn't idle and unfreeze the main thread. It would also cause lock problems.
example:

Code: Select all

import gpiozero as gp
import tkinter as tk
import time
from tkinter import ttk
import threading


Ldr3 = gp.LightSensor(26) #Case Feeder
Buzzer = gp.PWMOutputDevice(18, frequency=6000)
Relay2 = gp.OutputDevice(12, initial_value=False, active_high=True)#Cases

class Alarm (tk.LabelFrame):
    def __init__(self, master):
        tk.LabelFrame.__init__(self, master, text="Cautions")
        self.master = master
    #Alerts
      #Low Cases
        low_case_warning = ttk.LabelFrame(self, text="Bullets")
        low_case_warning.pack(side = "left")
        self.low_case_label = tk.Button(low_case_warning, command = self.Low_Cases_Reset,
                                        text="Good", background="green", 
                                        height=2, width=5)
        self.low_case_label.pack(fill = "both")
        Relay2.source = Ldr3
       #Threads
        self.T1 = threading.Thread(target=self.Low_Cases, daemon=True)
        self.T1.start()
                
    def Low_Cases(self): #Currently Works if thread is a daemon.
        while True:
            pulsetime = 15
            Ldr3.wait_for_light()
            time.sleep(0.2)
            while Ldr3.light_detected == True:
                if pulsetime <= -1: #Without this the threads wouldn't idle and release lock and keep resetting alerts. Now the alerts can be clear even if LDR is still alive.
                    pass
                if pulsetime >= 0:
                    time.sleep(0.2)
                    pulsetime -= 1
                    print ("Case Timer = " + str(pulsetime)) #will not be used in main project
                    if pulsetime <= 0: 
                        self.low_case_label.configure(bg="red", text = "Alert")
                        Buzzer.pulse()
                    if Ldr3.light_detected == False: 
                        pass
    

    def Low_Cases_Reset(self):
        Buzzer.off()
        self.low_case_label.configure(bg="green", text = "Good")
   
class App(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("AMMMO Computer")
        self.calls = Alarm(self)
        self.calls.pack()

if __name__ == "__main__":
    app=App()
    app.mainloop()
Speak softly and carry a big stick; you will go far.

Theodore Roosevelt

mmkw43
Posts: 696
Joined: Tue Dec 24, 2013 6:18 pm

Re: Event triggered by how long GPIO device is active.

Sun Dec 06, 2020 10:04 pm

I use counters a lot rather than time.sleep

var = var + 1
makes it easy to "associate" the value with something and doesn't halt the program.

Return to “Python”