Search code examples
pythonasynchronousraspberry-pipython-multithreadinggpio

Python threading class for GPIO Led blink


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.


Solution

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