EDIT: I SOLVED IT! DON'T MAKE THE SAME MISTAKE AS ME
replace this line:
p = multiprocessing.Process(target=check(mval,mname))
with:
p = multiprocessing.Process(target=check, args=(mval,mname))
---------------------------------------------------------------------------------------
.
.
.
I am making a robot with Raspberry Pi and some microswitches and I want to check the microswitches if they are triggered so I'm using the multiprocessing module, the process class to be exact with a few infinite while loops and the problem is that one starts and waits until it is triggered and ends and starts the next one instead of all of them starting and running independently. Here is my code so far.
import multiprocessing
import time
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD)
GPIO.setwarnings(False)
GPIO.setup(32, GPIO.IN, pull_up_down = GPIO.PUD_UP)
GPIO.setup(7, GPIO.IN, pull_up_down = GPIO.PUD_UP)
GPIO.setup(12, GPIO.IN, pull_up_down = GPIO.PUD_UP)
GPIO.setup(36, GPIO.IN, pull_up_down = GPIO.PUD_UP)
def check(mval, mname):
while True:
time.sleep(0.01)
check = GPIO.input(mval)
if check == False:
print("triggered " + mname)
with open("tmp.txt", "w"):
pass
f = open("tmp.txt", "w")
f.write(mname)
f.close()
break
def startcheck(mval, mname):
p = multiprocessing.Process(target=check(mval,mname))
p.start()
p.join()
startcheck(32, "m1")
startcheck(7, "m2")
startcheck(12, "m3")
startcheck(36, "m4")
The join() function causes each loop to terminate before the next one starts. From the standard library docs:
"join([timeout])
If the optional argument timeout is None (the default), the method blocks until the process whose join() method is called terminates. If timeout is a positive number, it blocks at most timeout seconds.
A process can be joined many times.
A process cannot join itself because this would cause a deadlock. It is an error to attempt to join a process before it has been started."
Solution: remove the join() line.
Related
I am attempting to create a daemon that will execute my script at boot.
using this code as my template Running a python script
my python script works interactively when a user is logged in.
def wait_for_network():
while os.system("ping -c 1 8.8.8.8") != 0:
time.sleep(1)
return
from getmac import get_mac_address
from datetime import datetime
import RPi.GPIO as GPIO
import os
import time
import random
import RPi.GPIO as GPIO
import requests
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(5, GPIO.IN)
GPIO.setup(6, GPIO.IN)
GPIO.setup(23, GPIO.IN)
GPIO.setup(24, GPIO.IN)
GPIO.setup(14, GPIO.IN)
eth_mac = get_mac_address()
#print(eth_mac)
API_ENDPOINT = "https://xxxxxx.com/handlers/receiveStatus.ashx"
CUSTOMER_KEY = "1234567890"
# Define a callback function that will be called by the GPIO
# event system:
def onButton(channel):
if channel == 14:
dt_string = (datetime.now().strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3])
data = {'ID':CUSTOMER_KEY,
'UUID':str(eth_mac),
'DT':dt_string,
'A':str(GPIO.input(6)),
'B':str(GPIO.input(24)),
'C':str(GPIO.input(23)),
'D':str(GPIO.input(5))
}
r = requests.post(url = API_ENDPOINT, data = data)
#print r.text
#print data
GPIO.add_event_detect(14, GPIO.RISING, callback=onButton, bouncetime=20)
#input()
My question is this - the #input() do i need it when running as a daemon?
With it, the script runs until the users presses ctrl-c to break out of.
When I comment it out, the script runs once then returns to the prompt.
The GPIO is making a thread and the main thread needs to wait for it. That was done with the input(). What you can do instead is to make a loop that sleeps instead in place of the input().
while True:
time.sleep(1)
That will hold the process from exiting until a ctrl c happens.
I'm trying to make a python script that starts mdk3 when a switch is flipped on (LOW) and kills it when it's switched off (HIGH). However, the mdk3 command always starts when the script is started, regardless of the switch's position. Starting the script with the switch in the ON position and then switching it OFF while it's running kills the command, as expected. However, it does not begin running again when switching it back ON. Interestingly, the text set to print functions exactly as would be expected. My code is as follows:
import RPi.GPIO as GPIO
import time
import subprocess
import os
import signal
FSU = 'sudo mdk3 mon0 d'
pro = 'subprocess.Popen(FSU, stdout=subprocess.PIPE, shell=True, preexec_fn=os.setsid)'
# tell the GPIO module that we want to use the chip's numbering scheme
GPIO.setmode(GPIO.BCM)
# Set up GPIO16 as an input with internal pull-up resistor to hold it HIGH until it is pulled down to GND by the connected switch
GPIO.setup(16, GPIO.IN, pull_up_down=GPIO.PUD_UP)
running = False
while True:
if GPIO.input(16) == GPIO.LOW:
if running == False:
print('Button was pushed!')
pro
running = True
time.sleep(0.1)
elif running == True:
print('The process is running.')
time.sleep(0.1)
elif GPIO.input(16) == GPIO.HIGH and running == True:
os.killpg(os.getpgid(pro.pid), signal.SIGTERM)
print('Process killed.')
running = False
time.sleep(0.1)
elif running == False:
print('The process is not running.')
time.sleep(0.1)
else:
print('Critical error.')
time.sleep(0.1)
The reason I'm using my own loop to poll the GPIO pins instead of the event detection built-in in the RPi.GPIO library is because it has caused me nothing but problems and doing it myself seemed simpler. Any help would be appreciated.
Edit: I'm not sure why I didn't think of putting that call into quotes. Now it doesn't run on script start, but it only runs once. As in: I start the script with the switch OFF, and it doesn't run (as expected). I switch it ON and it runs. I switch it back OFF and it successfully kills the script. However, switching it back ON doesn't restart the script. Sorry if this is a bad explanation.
subprocess.Popen() starts the process as soon as it is called and also returns the process.
So, you can simply start the process again in the loop when it needs to be running by calling the same function again.
Slightly modifying your code:
import RPi.GPIO as GPIO
import time
import subprocess
import os
import signal
proc = subprocess.Popen(FSU, stdout=subprocess.PIPE, shell=True, preexec_fn=os.setsid)
FSU = 'sudo mdk3 mon0 d'
# tell the GPIO module that we want to use the chip's numbering scheme
GPIO.setmode(GPIO.BCM)
# Set up GPIO16 as an input with internal pull-up resistor to hold it HIGH until it is pulled down to GND by the connected switch
GPIO.setup(16, GPIO.IN, pull_up_down=GPIO.PUD_UP)
running = False
while True:
if GPIO.input(16) == GPIO.LOW:
if running == False:
print('Button was pushed!')
# declare the proc variabe again and also start the process
proc = subprocess.Popen(FSU, stdout=subprocess.PIPE, shell=True, preexec_fn=os.setsid)
running = True
time.sleep(0.1)
elif running == True:
print('The process is running.')
time.sleep(0.1)
elif GPIO.input(16) == GPIO.HIGH and running == True:
os.killpg(os.getpgid(pro.pid), signal.SIGTERM)
print('Process killed.')
running = False
time.sleep(0.1)
elif running == False:
print('The process is not running.')
time.sleep(0.1)
else:
print('Critical error.')
time.sleep(0.1)
More on subprocess
If I got it right, Python doesn't accept a process to be started out of a process?! For example:
def function1():
while True:
wait_for_condition
#then....
process2.start()
def function2():
does something
process2.join()
process1 = multiprocessing.Process(target=function1,))
process2 = multiprocessing.Process(target=function2,))
process1.start()
In my test python denied to open a process out of a process.
Is there a solution with another way to solve this?
If not - Id have another way to go, but this way would include a modification of the electronics (connect one output to one input and use this to let a process wait for an event and then start. ... but I think this is not a clean way. Its more kind of an workaround. And I'd have a little risk to cause a shortcut if Input and Output is not set correctly).
Edit:
The Task:
Having three processes parallel. These wait for an input at one attached sensor each.
If one of these processes get an input change they should reset a counter (LED_counter) and start another process (LED_process) in not already started. After that the process waits for an input change again.
Beside that...
The LED_process starts to active one output and counting down the LED_counter. If the LED_counter reaches zero, the process terminates. If the code starts again it must be able to restart from the top of the code.
Edit 2:
Latest try with threading (don't be confused by some german words). If I try this code -> the different threads mixes in some strange way together. But for now I can't find a mistake. Same code with multiprocessing works fine:
import RPi.GPIO as GPIO
import time
import threading
import sys
LED_time = 10 #LEDs active time
#Sensor Inputs
SGT = 25
SGA = 23
SHT = 12
GPIO.setmode(GPIO.BCM)
GPIO.setup(SGT, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(SGA, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(SHT, GPIO.IN, pull_up_down=GPIO.PUD_UP)
def Sens_check(Sensor,Name):
print("Thread_{} aktiv".format(Name))
while True:
GPIO.wait_for_edge(Sensor, GPIO.FALLING)
#LcGT.value = LED_time
print("{} Offen".format(Name))
time.sleep(0.1)
GPIO.wait_for_edge(SGT, GPIO.RISING)
print("{} Geschlossen".format(Name))
time.sleep(0.1)
SensGT_Thread = threading.Thread(
target=Sens_check,
args=(SGT,"Gartentor",))
SensGA_Thread = threading.Thread(
target=Sens_check,
args=(SGA,"Garage",))
SensHT_Thread = threading.Thread(
target=Sens_check,
args=(SHT,"Haustuere",))
try:
SensGT_Thread.start()
time.sleep(0.1)
SensGA_Thread.start()
time.sleep(0.1)
SensHT_Thread.start()
SensGT_Thread.join()
SensGA_Thread.join()
SensHT_Thread.join()
except:
print("FAILURE")
finally:
sys.exit(1)
Processes can only be started within the process they were created in. In the code provided, process2 was created in the main process, and yet tried to be started within another one (process1). Also, processes cannot be restarted, so they should be created each time .start is used.
Here's an example of starting processes within a process:
import multiprocessing
import time
def function1():
print("Starting more processes")
sub_procs = [multiprocessing.Process(target=function2) for _ in range(5)]
for proc in sub_procs:
proc.start()
for proc in sub_procs:
proc.join()
print("Done with more processes")
def function2():
print("Doing work")
time.sleep(1) # work
print("Done with work")
print("Starting one subprocess")
process1 = multiprocessing.Process(target=function1)
process1.start()
print("Moving on without joining")
"""Output of this:
Starting one subprocess
Moving on without joining
Starting more processes
Doing work
Doing work
Doing work
Doing work
Doing work
Done with work
Done with work
Done with work
Done with work
Done with work
Done with more processes
"""
I come here after after trying in all directions to come out of this problem without any results, unfortunately.
What I have in mind:
I need a class, named Led, that in the constructor simply accept a GPIO pin and offer method for:
Light On
Light Off
Blinking
What I do:
I have build this class in this way:
import RPi.GPIO as GPIO
import time
import threading
from threading import Thread
class Led(Thread):
def __init__(self, led_pin):
Thread.__init__(self)
self.pin_stop = threading.Event()
self.__led_pin = led_pin
GPIO.setmode(GPIO.BCM)
GPIO.setup(self.__led_pin, GPIO.OUT)
def low(self, pin):
GPIO.setup(pin, GPIO.OUT)
GPIO.output(pin, GPIO.LOW)
def blink(self, time_on=0.050, time_off=1):
pin = threading.Thread(name='ledblink',target=self.__blink_pin, args=(time_on, time_off, self.pin_stop))
pin.start()
def __blink_pin(self, time_on, time_off, pin_stop):
while not pin_stop.is_set():
GPIO.output(self.__led_pin, GPIO.HIGH)
time.sleep(time_on)
GPIO.output(self.__led_pin, GPIO.LOW)
time.sleep(time_off)
def __stop(self):
self.pin_stop.set()
def reset(self):
GPIO.cleanup()
def off(self):
self.__stop()
def on(self):
self.__stop()
GPIO.output(self.__led_pin, GPIO.LOW)
GPIO.output(self.__led_pin, GPIO.HIGH)
where the blink method is responsible to indefinitely blink the led until an Off or On method call.
And run this simple code:
from classes.leds import Led
import time
from random import randint
Led16 = Led(16)
def main():
while True:
if (randint(0, 1) == 1):
Led16.blink()
else:
Led16.off()
time.sleep(2)
if __name__ == "__main__":
main()
What happens:
The Led object seem to spawn a new thread every time that a method is called with the effect that GPIO line become shared between multiple threads.
What is my wish:
I want to keep blinking led asynchronous (obviously) and have the control on Led16() object status, maybe without creating new threads each time I cal its method, but at reached this point I am bit confused.
Thank to help me understanding how to reach this goal.
You are creating lots of threads because your blink() creates a new thread every time it is called and the old one isn't stopped.
I suppose there are a couple of options with the thread:
Create the thread just once - for example in __init__() - and it runs continuously on the blinking time intervals (i.e. sleeping most of the time) reading an instance variable and setting the LED correspondingly. To change the led state, the blink(), on() and off() control the led by setting this instance variable to on/off/blinking.
In the blink routine, if the thread is already running either don't create a new thread, or stop the old thread (and wait for it to finish) and then start a new one.
Things you will have to handle are that you want the behaviour to be that:
If the led is off then the led turns on as soon as on() or blink() is called
If the led is blinking and blink() is called again the blinking on/off sequence is not disturbed
If blinking and off() is called I would want the on-cycle if it has started to run to completion, i.e. the led should not be turned off immediately because that might be a very short flash which would look odd.
The catch with creating a new thread is waiting for the old one to finish, and it just feels simplest to create the thread just once in __init__() and have it running continuously. When the led is on or off, the time period is shortened (to value FAST_CYCLE) so that when the led is turned off or on it reacts quickly because the sleep() is for a short time.
Some other points about your code:
I don't think you need to make your class inherit from Thread - you are creating a new thread in the pin=... line.
If you add comments as you write the code, usually makes it easier to understand what is going on when reading the code.
If you keep a reference to the thread (i.e. self.pin = threading.Thread not pin = threading.Thread) then in the reset() you can use join() to make sure it has exited before you continue
As the blink time periods can change and the thread has to use the latest values, use self to read them every time rather than pass them as arguments to the __blink_pin() routine, and if doing that you might as well use self to get the pin_stop semaphore too.
Something like this (untested):
import RPi.GPIO as GPIO
import time
import threading
from threading import Thread
class Led(object):
LED_OFF = 0
LED_ON = 1
LED_FLASHING = 2
# the short time sleep to use when the led is on or off to ensure the led responds quickly to changes to blinking
FAST_CYCLE = 0.05
def __init__(self, led_pin):
# create the semaphore used to make thread exit
self.pin_stop = threading.Event()
# the pin for the LED
self.__led_pin = led_pin
# initialise the pin and turn the led off
GPIO.setmode(GPIO.BCM)
GPIO.setup(self.__led_pin, GPIO.OUT)
# the mode for the led - off/on/flashing
self.__ledmode = Led.LED_OFF
# make sure the LED is off (this also initialises the times for the thread)
self.off()
# create the thread, keep a reference to it for when we need to exit
self.__thread = threading.Thread(name='ledblink',target=self.__blink_pin)
# start the thread
self.__thread.start()
def blink(self, time_on=0.050, time_off=1):
# blinking will start at the next first period
# because turning the led on now might look funny because we don't know
# when the next first period will start - the blink routine does all the
# timing so that will 'just work'
self.__ledmode = Led.LED_FLASHING
self.__time_on = time_on
self.__time_off = time_off
def off(self):
self.__ledmode = LED_OFF
# set the cycle times short so changes to ledmode are picked up quickly
self.__time_on = Led.FAST_CYCLE
self.__time_off = Led.FAST_CYCLE
# could turn the LED off immediately, might make for a short flicker on if was blinking
def on(self):
self.__ledmode = LED_ON
# set the cycle times short so changes to ledmode are picked up quickly
self.__time_on = Led.FAST_CYCLE
self.__time_off = Led.FAST_CYCLE
# could turn the LED on immediately, might make for a short flicker off if was blinking
def reset(self):
# set the semaphore so the thread will exit after sleep has completed
self.pin_stop.set()
# wait for the thread to exit
self.__thread.join()
# now clean up the GPIO
GPIO.cleanup()
############################################################################
# below here are private methods
def __turnledon(self, pin):
GPIO.output(pin, GPIO.LOW)
def __turnledoff(self, pin):
GPIO.output(pin, GPIO.HIGH)
# this does all the work
# If blinking, there are two sleeps in each loop
# if on or off, there is only one sleep to ensure quick response to blink()
def __blink_pin(self):
while not self.pin_stop.is_set():
# the first period is when the LED will be on if blinking
if self.__ledmode == Led.LED_ON or self.__ledmode == Led.LED_FLASHING:
self.__turnledon()
else:
self.__turnledoff()
# this is the first sleep - the 'on' time when blinking
time.sleep(self.__time_on)
# only if blinking, turn led off and do a second sleep for the off time
if self.__ledmode == Led.LED_FLASHING:
self.__turnledoff()
# do an extra check that the stop semaphore hasn't been set before the off-time sleep
if not self.pin_stop.is_set():
# this is the second sleep - off time when blinking
time.sleep(self.__time_off)
For who need this in the future I have do some little adjustment to the proposed code and seem to work fine as expected.
Again thanks to #barny
import RPi.GPIO as GPIO
import time
import threading
class Led(object):
LED_OFF = 0
LED_ON = 1
LED_FLASHING = 2
# the short time sleep to use when the led is on or off to ensure the led responds quickly to changes to blinking
FAST_CYCLE = 0.05
def __init__(self, led_pin):
# create the semaphore used to make thread exit
self.pin_stop = threading.Event()
# the pin for the LED
self.__led_pin = led_pin
# initialise the pin and turn the led off
GPIO.setmode(GPIO.BCM)
GPIO.setup(self.__led_pin, GPIO.OUT)
# the mode for the led - off/on/flashing
self.__ledmode = Led.LED_OFF
# make sure the LED is off (this also initialises the times for the thread)
self.off()
# create the thread, keep a reference to it for when we need to exit
self.__thread = threading.Thread(name='ledblink',target=self.__blink_pin)
# start the thread
self.__thread.start()
def blink(self, time_on=0.050, time_off=1):
# blinking will start at the next first period
# because turning the led on now might look funny because we don't know
# when the next first period will start - the blink routine does all the
# timing so that will 'just work'
self.__ledmode = Led.LED_FLASHING
self.__time_on = time_on
self.__time_off = time_off
def off(self):
self.__ledmode = self.LED_OFF
# set the cycle times short so changes to ledmode are picked up quickly
self.__time_on = Led.FAST_CYCLE
self.__time_off = Led.FAST_CYCLE
# could turn the LED off immediately, might make for a short flicker on if was blinking
def on(self):
self.__ledmode = self.LED_ON
# set the cycle times short so changes to ledmode are picked up quickly
self.__time_on = Led.FAST_CYCLE
self.__time_off = Led.FAST_CYCLE
# could turn the LED on immediately, might make for a short flicker off if was blinking
def reset(self):
# set the semaphore so the thread will exit after sleep has completed
self.pin_stop.set()
# wait for the thread to exit
self.__thread.join()
# now clean up the GPIO
GPIO.cleanup()
I'm not sure it will be helpfull, but I came up with that (using gpiozero)
from gpiozero import LED
import time
import threading
class LEDplus():
def __init__(self,pinnumber):
self.led = LED(pinnumber)
self.__loop = True
self.__threading = threading.Thread(target=self.__blink)
def on(self,):
self.__loop = False
self.maybejoin()
self.led.on()
def off(self, ):
self.__loop = False
self.maybejoin()
self.led.off()
def maybejoin(self,):
if self.__threading.isAlive():
self.__threading.join()
def blink(self, pitch):
self.__threading = threading.Thread(target=self.__blink, args=(pitch, ))
self.__threading.start()
def __blink(self, pitch=.25):
self.__loop = True
while self.__loop:
self.led.toggle()
time.sleep(pitch/2)
self.led.off()
green = LEDplus(18)
green.blink(1)
On the answer of Alessandro Mendolia, just missing the private methods of the class. Added below with some fixes. The __turnledon() does not need an argument - it can access the self.__led_pin already stored in initialization.
############################################################################
# below here are private methods
def __turnledon(self):
GPIO.output(self.__led_pin, GPIO.LOW)
def __turnledoff(self):
GPIO.output(self.__led_pin , GPIO.HIGH)
# this does all the work
# If blinking, there are two sleeps in each loop
# if on or off, there is only one sleep to ensure quick response to blink()
def __blink_pin(self):
while not self.pin_stop.is_set():
# the first period is when the LED will be on if blinking
if self.__ledmode == BlinkerLed.LED_ON or self.__ledmode == BlinkerLed.LED_FLASHING:
self.__turnledon()
else:
self.__turnledoff()
# this is the first sleep - the 'on' time when blinking
time.sleep(self.__time_on)
# only if blinking, turn led off and do a second sleep for the off time
if self.__ledmode == BlinkerLed.LED_FLASHING:
self.__turnledoff()
# do an extra check that the stop semaphore hasn't been set before the off-time sleep
if not self.pin_stop.is_set():
# this is the second sleep - off time when blinking
time.sleep(self.__time_off)
I have a problem with my code working with raspberry pi.
I just started with python so i need some help.
This is the code:
import RPi.GPIO as GPIO
import time
GPIO.setmode(GPIO.BCM)
led1=22
led2=17
GPIO.setup(led1, GPIO.OUT)
GPIO.setup(led2, GPIO.OUT)
def blink():
GPIO.output(led1, 1)
time.sleep(1)
GPIO.output(led1, 0)
GPIO.output(led2, 1)
time.sleep(1)
GPIO.output(led2, 0)
while(blink):
blink()
try:
main()
except KeyboardInterrupt:
GPIO.cleanup()
when I run this error appear in the console:
RuntimeWarning: This channel is already in use, continuing anyway. Use
GPIO.setwarnings(False) to disable warnings. GPIO.setup(led1,
GPIO.OUT) and:
RuntimeWarning: This channel is already in use, continuing anyway. Use
GPIO.setwarnings(False) to disable warnings. GPIO.setup(led2,
GPIO.OUT)
If I understand correctly the command GPIO.cleanup() should reset all pin of GPIO port and turn off the led.
but this in not happening in fact one of the led remain on.
How can change my code to resolve this issue?
Here is a little help, how to effectively separate your functions, and make them more general. Although this is a working Python script I provided, I didn't tested it on my raspi, but I think it will work -- anyway, let me know if there were any problems!
import RPi.GPIO as GPIO
import time
# Module level constants
LED1 = 22
LED2 = 17
# Sets up pins as outputs
def setup(*leds):
GPIO.cleanup()
GPIO.setmode(GPIO.BCM)
for led in leds:
GPIO.setup(led, GPIO.OUT)
GPIO.output(led, GPIO.LOW)
# Turn on and off the leds
def blink(*leds):
# Blink all leds passed
for led in leds:
GPIO.output(led, GPIO.HIGH)
time.sleep(1)
GPIO.output(led, GPIO.LOW)
if __name__ == '__main__':
# Setup leds
setup(LED1, LED2)
# Run blinking forever
try:
while True:
blink(LED1, LED2)
# Stop on Ctrl+C and clean up
except KeyboardInterrupt:
GPIO.cleanup()
A friendly recommendation:
There is a dedicated Raspberry Pi StackExchange site too: https://raspberrypi.stackexchange.com/
You don't seem to have included main in your question. However the problem may occur if the programs exits for some reason other than KeyboardInterrupt. It's better to free the resource in a finally block
try:
main()
except KeyboardInterrupt:
pass
finally:
GPIO.cleanup()
You are calling main() function but it's not declared (defined), you are using while(blink). So You need to delete the "main()" and put the "Try" before your main function which is the while(blink) loop. Don't forget the proper tabs there.