用于GPIO的Python线程类Led闪烁

时间:2017-10-26 13:59:33

标签: python asynchronous raspberry-pi python-multithreading gpio

不幸的是,在我向所有方向努力解决这个问题后没有任何结果我来到这里。

我的想法:

我需要一个名为Led的类,在构造函数中只需接受GPIO引脚并提供以下方法:

  • Light On
  • 关灯
  • 闪烁

我做了什么:

我用这种方式构建了这个类:

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)

其中blink方法负责无限期地闪烁指示灯,直到Off或On方法调用。

运行这个简单的代码:

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

会发生什么:

每次调用方法时,Led对象似乎都会产生一个新线程,其效果是GPIO线在多个线程之间共享。

我的愿望是什么:

我想保持闪烁导致异步(显然)并控制Led16()对象状态,可能在每次调用其方法时都不创建新线程,但是到了这一点我感到有点困惑。

感谢帮助我了解如何实现这一目标。

4 个答案:

答案 0 :(得分:1)

您正在创建大量线程,因为blink()每次调用时都会创建一个新线程,并且旧线程不会停止。

我想这个帖子有几个选项:

  1. 仅创建一次线程 - 例如在__init__()中 - 并且它在闪烁的时间间隔(即大部分时间休眠)上连续运行,读取实例变量并相应地设置LED。要更改LED状态,blink()on()off()会通过将此实例变量设置为开/关/闪烁来控制LED。

  2. 在闪烁例程中,如果线程已在运行,或者不创建新线程,或者停止旧线程(并等待它完成),然后再启动一个新线程。

  3. 您需要处理的事情是您希望行为是:

    • 如果指示灯已关闭,则只要on()blink()被调用,指示灯就会亮起
    • 如果指示灯闪烁且再次调用blink(),则不会打扰闪烁的开/关序列
    • 如果闪烁并且off()被调用,我会想要开启周期,如果它已经开始运行完成,即不应该立即关闭LED,因为这可能是一个非常短的闪光看起来很奇怪

    创建一个新线程的问题是等待旧线程完成,只需在__init__()中创建一次线程并让它连续运行就感觉最简单。当LED打开或关闭时,时间段会缩短(到值FAST_CYCLE),这样当LED关闭或打开时会因为sleep()很短时间而快速反应。

    关于您的代码的其他一些观点:

    • 我认为你不需要让你的类继承自Thread - 你正在pin=...行创建一个新线程。
    • 如果您在编写代码时添加注释,通常可以更容易地理解在阅读代码时发生的事情。
    • 如果您保留对该主题的引用(即self.pin = threading.Thread而非pin = threading.Thread),那么在reset()中您可以使用join()确保它已退出,然后再继续
    • 由于眨眼时间段可以改变并且线程必须使用最新值,因此每次都要使用self来读取它们,而不是将它们作为参数传递给__blink_pin()例程,如果这样做,你也可以使用self来获取pin_stop信号量。

    像这样(未经测试):

    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)
    

答案 1 :(得分:1)

answer of Alessandro Mendolia上,只是缺少该类的私有方法。在下面添加了一些修复程序。 pip install AST不需要参数-它可以访问已经存储在初始化中的from ast import literal_eval data = "0xAB15" out = literal_eval(data) print(out)

43797

答案 2 :(得分:0)

对于将来需要这个的人,我对提议的代码做了一些调整,似乎按预期工作正常。

再次感谢@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()

答案 3 :(得分:0)

我不确定是否会有所帮助,但我想出了这一点(使用SELECT b.id, b.title, b.time AS borrow_time, MIN(r.time) AS return_time FROM ( SELECT id, title, time FROM books WHERE event = 'BORROW' ) b OUTER JOIN ( SELECT id, time FROM books WHERE event = 'RETURN' ) r ON b.id = r.id AND b.time < r.time GROUP BY b.id, b.title, borrow_time ORDER BY borrow_time;

gpiozero