Python 3 Subprocess does not terminate with signal.SIGINT - python-3.x

I've read many of the questions here on SO, but none of the solutions seem to solve my problem. I have a file called "light_led.py" which looks like this
import RPi.GPIO as gpio
import time
# Set the mode for the GPIO numbering
gpio.setmode(gpio.BCM)
pin = 23
# Set the GPIO as output
gpio.setup(pin, gpio.OUT, initial=gpio.LOW)
try:
while True: # Run forever
gpio.output(pin, gpio.HIGH) # Turn on
time.sleep(1) # Sleep for 1 second
gpio.output(pin, gpio.LOW) # Turn off
time.sleep(1) # Sleep for 1 second
except KeyboardInterrupt:
# cleanup all GPIO
gpio.cleanup()
print('Cleaned up')
It simply toggles an LED on and off. This file is called in the following script
import RPi.GPIO as gpio
import time
import subprocess
import signal
# Set the mode for the GPIO numbering
gpio.setmode(gpio.BCM)
pin = 24
# Set the GPIO as input w/ pull up
gpio.setup(pin, gpio.IN, pull_up_down = gpio.PUD_UP)
# Initialize
pin_prev = gpio.input(pin)
while True:
pin = gpio.input(pin)
if (pin == 0) and (pin_prev == 1):
process = subprocess.Popen('python <PATH-TO-FILE>/light_led.py', shell=True)
print('Should be blinking')
if (pin == 1) and (pin_prev == 0):
process.send_signal(signal.SIGINT)
print('Should be off')
# Update previous state
pin_prev = pin
# Slight pause to debounce
time.sleep(0.05)
The files run fine and do what they're suppose to. The only problem I'm having is that I can't stop the blinking light by "flipping the switch". In other words, I'm having trouble with this process.send_signal(signal.SIGINT). There are not errors, so I don't have much to report.
I've tried process.terminate() and os.kill(process.pid, signal.SIGINT), both with no luck. I just want to stop the subprocess from running and continue with the main toggle script. From what I read, "signal.SIGINT" should invoke the "KeyboardInterrupt" in the subprocess...but maybe not?

Related

Stop a running script in python3 from another script

I have 2 scripts - one is running and when I press a button, a second script is launched.
That is working; but I would like to kill that second script when I release the button again.
The killing of the second script seems not to be working
below code of script 1
import RPi.GPIO as GPIO
import time
from time import sleep
import subprocess, os
from subprocess import check_call
buttonPin = 5
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False) #Display no error messages
GPIO.setup(buttonPin, GPIO.IN) # initialize button pin We have a physical 10k Resistor so no pull-up is required
try:
run = 0
while True:
if GPIO.input(buttonPin)==0 and run == 0:
print("Button pressed start second script")
subprocess.call(['python3', "2ndPython.py"])
run=1
while GPIO.input(buttonPin)==0:
time.sleep(0.01)
if GPIO.input(buttonPin)==1 and run == 1:
run = 0
print("Button NOT pressed - kill second script")
check_call(["pkill", "-9", "-f", "python3", "2ndPython.py"]) # Stop script
while GPIO.input(buttonPin)==1:
time.sleep(0.01)
except KeyboardInterrupt:
GPIO.cleanup()
code of my second script that I would like to kill when I release the button.
import time
from time import sleep
def main():
count = 0
while True:
count = count +1
# do whatever the script does
print("You have started this program X " + str(count) + " times now")
time.sleep(2)
if __name__ == "__main__":
main()
Can't seem to find why the second script is not killed.

Non-latching GPIO output that needs to disregard input

New to programming, so I'll try my best to explain:
I'm trying to use a Pi Zero in my solar setup to switch the power-on input on an x86 motherboard (basically operating as the power button). The idea is that a battery voltage monitor sets pin 14 on the Pi high when the battery is at a high enough voltage. The script reads this input then switches an output (26) on the Pi, which in turn closes a relay for one second, which will then activate the power-on input on the motherboard.
The problem is that I cannot get the output (26) to turn off after one second. The script won't ignore pin 14, and just keeps the output (26) high. (Pin 14 is naturally held high by the battery monitor until the battery level falls down below a certain threshold)
import RPi.GPIO as GPIO
from time import sleep
GPIO.setmode(GPIO.BCM)
GPIO.setup(14,GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(26,GPIO.OUT)
GPIO.output(26, 1)
while True:
if GPIO.input(14)==1:
GPIO.output(26, 0)
sleep(1)
GPIO.output(26, 1)
else:
GPIO.output(26, 1)
GPIO.cleanup()
Here's one possible solution that polls the value of GPIO14 1/second
and uses the variable power_on to figure out how we respond to the
value:
import RPi.GPIO as GPIO
from time import sleep
GPIO.setmode(GPIO.BCM)
GPIO.setup(14,GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(26,GPIO.OUT)
GPIO.output(26, 1)
power_on = False
while True:
if not power_on:
if GPIO.input(14) == 1:
GPIO.output(26, 0)
sleep(1)
GPIO.output(26, 1)
power_on = True
else:
if GPIO.input(14) == 0:
power_off = True
time.sleep(1)
# Note that you're never going to reach this line.
GPIO.cleanup()
Alternatively, you can have your code block until pin 14 changes, leading to something like this:
import RPi.GPIO as GPIO
from time import sleep
GPIO.setmode(GPIO.BCM)
GPIO.setup(14,GPIO.IN, pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(26,GPIO.OUT)
GPIO.output(26, 1)
while True:
GPIO.wait_for_edge(14, GPIO.RISING)
GPIO.output(26, 0)
sleep(1)
GPIO.output(26, 1)
# Note that you're never going to reach this line.
GPIO.cleanup()
This uses GPIO.wait_for_edge() to wait for a rising edge on GPIO14 (a low ➡️ high transition), and then triggers your relay.

Raspberry Pi relays and Python, how do I turn off and on a relay in separate functions?

I'm new to Raspberry Pi and are working on a Pi3 program that will switch on and off an irrigation pump though a 1 channel relay.
I've made a function that works well, that takes a delay variable and starts the relay then waits for the delay until it stops the relay again.
However, I also need to be able to turn the relay on in another function, and leave it on until the user turns it off again, and I can't seem to get that working.
When I try it, nothing happens with the relay (except for the normal having power indicating led on it).
This is my working function:
#This function switches on the relay for a set period of time, and then shuts it off again
def relay_control(wait_time):
# Sleeping for a second
time.sleep(1)
print("Relay control function")
# We will be using the BCM GPIO numbering
GPIO.setmode(GPIO.BCM)
# Selecting which GPIO to target
GPIO_CONTROL = 6
# Set CONTROL to OUTPUT mode
GPIO.setup(GPIO_CONTROL, GPIO.OUT)
# Starting the relay
GPIO.output(GPIO_CONTROL, True)
# Sleeping for set amount of time
try:
time.sleep(wait_time)
except:
time.sleep(60)
print("Setting delay failed, using default 60 seconds")
# Stopping the relay
GPIO.output(GPIO_CONTROL, False)
# Cleanup
GPIO.cleanup()
And then I have tried these two functions, but it just doesn't work:
#This function switches on the relay - Doesn't currently work
def relay_on():
# Sleeping for a second
time.sleep(1)
# We will be using the BCM GPIO numbering
GPIO.setmode(GPIO.BCM)
# Selecting which GPIO to target
GPIO_CONTROL = 6
# Set CONTROL to OUTPUT mode
GPIO.setup(GPIO_CONTROL, GPIO.OUT)
#Starting the relay
GPIO.output(GPIO_CONTROL, False)
#Logging the event
logging.basicConfig(format='%(asctime)s %(message)s', filename='/home/pi/GardenBrain/events.log', level=logging.INFO)
logging.info('Relay has been manually switched on, from functions.py')
time.sleep(5)
#Cleanup
GPIO.cleanup()
#This function switches on the relay off - Doesn't currently work
def relay_off():
# Sleeping for a second
time.sleep(1)
# We will be using the BCM GPIO numbering
GPIO.setmode(GPIO.BCM)
# Selecting which GPIO to target
GPIO_CONTROL = 6
# Set CONTROL to OUTPUT mode
GPIO.setup(GPIO_CONTROL, GPIO.OUT)
#Stopping the relay
GPIO.output(GPIO_CONTROL, True)
#Logging the event
logging.basicConfig(format='%(asctime)s %(message)s', filename='/home/pi/GardenBrain/events.log', level=logging.INFO)
logging.info('Relay has been manually switched off, from functions.py')
#Cleanup
GPIO.cleanup()
Can anyone see what the problem is and help me get it working?
Any help is greatly appreciated!
Also, the working function seem to freeze my Raspberry Pi for a while. It's all powered though the GPIO pins, and the system has a 7" touchscreen display (which the system is being USB powered through), the mentioned 230v 1-channel relay and a Sense Hat.
I solved it myself by writing the function in a different way. This one works well and both starts and stops the relay as intended.
#This function switches on the relay on or off and expects the argument 'on' or 'off'
def relay_manual(action):
# Selecting which GPIO to target
GPIO_CONTROL = 6
if action == "on":
# Sleeping for a second
time.sleep(1)
# We will be using the BCM GPIO numbering
GPIO.setmode(GPIO.BCM)
# Set CONTROL to OUTPUT mode
GPIO.setup(GPIO_CONTROL, GPIO.OUT)
#Starting the relay
GPIO.output(GPIO_CONTROL, True)
#Logging the event
logging.basicConfig(format='%(asctime)s %(message)s', filename='/home/pi/GardenBrain/events.log', level=logging.INFO)
logging.info('Relay has been manually switched on, from functions.py')
elif action == "off":
try:
#Stopping the relay
GPIO.output(GPIO_CONTROL, False)
except:
# We will be using the BCM GPIO numbering
GPIO.setmode(GPIO.BCM)
# Set CONTROL to OUTPUT mode
GPIO.setup(GPIO_CONTROL, GPIO.OUT)
#Starting the relay
GPIO.output(GPIO_CONTROL, False)
#Logging the event
logging.basicConfig(format='%(asctime)s %(message)s', filename='/home/pi/GardenBrain/events.log', level=logging.INFO)
logging.info('Relay has been manually switched off, from functions.py')
#Cleanup
GPIO.cleanup()
You should move the code GPIO.setmode() and GPIO.setup() out of your functions as it only need to be setup once.
This is how the code some looks like:
def relay_on():
GPIO.output(GPIO_CONTROL, False)
logging.basicConfig(format='%(asctime)s %(message)s', filename='/home/pi/GardenBrain/events.log', level=logging.INFO)
logging.info('Relay has been manually switched on, from functions.py')
def relay_off():
GPIO.output(GPIO_CONTROL, True)
logging.basicConfig(format='%(asctime)s %(message)s', filename='/home/pi/GardenBrain/events.log', level=logging.INFO)
logging.info('Relay has been manually switched off, from functions.py')
# The main program setup the GPIO
GPIO_CONTROL = 6
GPIO.setmode(GPIO.BCM)
GPIO.setup(GPIO_CONTROL, GPIO.OUT)
# Get input from user to turn on or off relay
while True:
data = input("Input 'on'/'off' to turn on/off relay, or 'exit' to end ")
if data == 'on':
relay_on()
elif data == 'off':
relay_off()
elif data == 'exit':
# program end, clean up GPIO
GPIO.cleanup()

Python ability to manage 2 or more physical buttons

I am making a camera. I am using a RPi 3 and python. I am using 2 GPIO attached to momentary switches. With the first button I want to be able to take pictures. The second one runs a script to shutdown the system, but IT IS NOT WORKING. This is the code. Please help.
import RPi.GPIO as GPIO
from gpiozero import Button
import time
import os
from time import sleep
from picamera import PiCamera
GPIO.setmode(GPIO.BCM)
GPIO.setup(17, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)
GPIO.setup(18, GPIO.IN, pull_up_down = GPIO.PUD_DOWN)
button1 = Button(17)
button2 = Button(18)
camera = PiCamera()
channel = 1
if GPIO.input(18):
button2.wait_for_press()
print("Here I will run the shutdown code")
if GPIO.input(17):
frame = 1
while True:
try:
button1.wait_for_press()
camera.capture('/home/pi/Vinpics/frame%03d.jpg' % frame)
frame += 1
except KeyboardInterrupt:
print("Just a place holder")

UPDATE 2: How Do I Stop a Function in a while loop?

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()
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.
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
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()

Resources