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:
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:
on() or blink() is calledblink() is called again the blinking on/off sequence is not disturbedoff() 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:
Thread - you are creating a new thread in the pin=... line.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__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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With