Reputation: 55
Update:
So I was successful in implementing threading to allow the GUI to remain unblocked while the process is running. Now i am trying to figure out how to get this while loop to break and still function properly.
I tried implementing a second variable the while statement sees, as a flag, to attempt to break the while loop after running the PUMP function inside of it once. However, now the PUMP function doesn't run at all. The GPIO pin never goes high. What I'm looking for this to do:
-Press button. -Sets Flag to 1 -Runs RUN() function in thread if float switch is high/ signals low water if float switch is low - RUN() checks status of flag and float switch while running PUMP() function - PUMP() turns GPIO pin high, and after 5 secs, calls the OFF() function - OFF () sets flag to 0 and also sets pump GPIO to low
If during the PUMP() the float switch goes low, it should trigger and call the LOW() function, stopping the pump by setting GPIO pin to low and displaying status. This also sets the flag to 0 as well.
Code:
from tkinter import *
import tkinter.font
import RPi.GPIO as GPIO
import threading
#Variables
Flag = 0
#GPIO Setup
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
GPIO.setup(16, GPIO.OUT) #Water Pump
GPIO.setup(18, GPIO.IN) #Tank Float Switch
GPIO.output(16, GPIO.LOW)
#Window Setup
win = Tk()
win.title("Pump Test")
win.geometry("150x150+0+0")
#Label Setup
Label (win, text="Water System", fg="red", bg="black", font="24").grid(row=0, column=0)
#Functions
def RUN ():
while GPIO.input(18) and Flag == 1:
PUMP()
if Flag == 0:
OFF()
elif GPIO.input(18) == False:
LOW()
def OFF ():
Flag = 0
GPIO.output(16, GPIO.LOW)
WTR.config(text="Water", bg="grey")
def LOW ():
Flag = 0
GPIO.output(16, GPIO.LOW)
WTR.config(text="LOW WATER", bg="red")
def WTR ():
Flag = 1
if GPIO.input(18):
threading.Thread(target=RUN).start()
if GPIO.input(18)== False:
threading.Thread(target=LOW).start()
def PUMP ():
GPIO.output(16, GPIO.HIGH)
win.after(5000, OFF)
#Buttons
WTR = Button(win, text="Water", bg="grey", command = WTR, height = 2, width = 8)
WTR.grid(row=1, column=0) #Water Pump Control
mainloop()
Upvotes: 2
Views: 83
Reputation: 55
Thanks @patthoyts and @RolandSmith for the insight that helped lead me to the answer.
2 things they stated were helpful - Not using time.sleep and making sure I was using a global variable.
With some re-work on the Flag idea, and not sleeping but creating a check function to see how much time has passed. Deleted threading for now as it wasn't dire to the GUI process. Big shout out to Tom Slick with the behind the scenes help as well!
from tkinter import *
import tkinter.font
import RPi.GPIO as GPIO
import time
GPIO Setup
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
GPIO.setup(16, GPIO.OUT) # Water Pump
GPIO.setup(18, GPIO.IN) # Tank Float Switch
GPIO.output(16, GPIO.LOW)
time_to_run = 60 # time to run the pump in seconds
time_running = time.time() - 1
#Window Setup
win = Tk()
win.title("Pump Test")
win.geometry("150x150+0+0")
#Label Setup
Label (win, text="Water System", fg="red", bg="black", font="24").grid(row=0, column=0)
#Functions
def off():
WTR.config(text="LOW WATER", bg="red")
GPIO.output(16, GPIO.LOW)
def wtr():
global time_running
time_running = time.time() + time_to_run
if GPIO.input(18) and not GPIO.input(16):
pump_on()
elif GPIO.input(16):
pump_off()
def pump_on():
WTR.config(text="Water", bg="green")
GPIO.output(16, GPIO.HIGH)
# added to turn the pump off manualy
def pump_off():
WTR.config(text="Water", bg="grey")
GPIO.output(16, GPIO.LOW)
def check_pump():
# if the water level goes low turn the pump off
# and show low water level
if not GPIO.input(18):
off()
# if a set time has passed then shut the pump off it it is running
if time_running <= time.time() and GPIO.input(16):
pump_off()
win.after(100, check_pump) # call this function every 100 miliseconds
#Buttons
WTR = Button(win, text="Water", bg="grey", command = wtr, height = 2, width = 8)
WTR.grid(row=1, column=0) #Water Pump Control
check_pump() # must be called once to start the after function
mainloop()
Upvotes: 0
Reputation: 43495
Your Flag
is a module-level variable.
If you want to modify that in a function (without passing it as an argument), you need to mark it as global in the function.
Observe:
In [1]: flag = 0
In [2]: def foo(num):
...: flag = num
...:
In [3]: flag
Out[3]: 0
In [4]: foo(4)
In [5]: flag
Out[5]: 0
In [6]: foo(12)
In [7]: flag
Out[7]: 0
Calling foo
sets a flag
, but this is local to the function! It has no effect on the module-level object.
You have to explicitly say that you want to modify the module-level variable:
In [8]: def bar(num):
...: global flag
...: flag = num
...:
In [9]: bar(4)
In [10]: flag
Out[10]: 4
In [11]: bar(7)
In [12]: flag
Out[12]: 7
Upvotes: 1
Reputation: 33193
To ensure a UI remains responsive to user events (mouse clicks etc) and also to system events (like exposure and repainting) you should never enter a long lived loop in a function and never use sleep
. Instead Tkinter provides the after
method to allow you to schedule something to be done after some interval. This call adds your call into the event queue and its gets processed in due time by the code called by mainloop
. For something that should occur after a delay obviously after(num_millis)
is used. If you need to poll the state of a pin, then use a short time and in the handler, set another after
call to call your polling function again. Note you can cancel after
calls provided you retain the id
value that is returned when you call the method.
Don't use time.sleep
. No UI events will be processed during the sleep and the UI will be dead. Use after
.
Upvotes: 2