Search code examples
pythonpython-3.xraspberry-pipython-multithreadinggpio

Run functions for LED flashing and Buzzer sound simultaneously


I'm working on a little Raspberry Pi security project that has LEDs, a piezoelectric buzzer, a 9 digit keypad, and an LCD screen (Full Source Here: https://github.com/kevbo423/RPHSP/blob/master/Keypad.py).

In short, when a user enters an incorrect password with the keypad, I want the LEDs to flash and the buzzer to go off at the same time. I can't for the life of me figure out how to make that happen. I have the functions for the LEDs and the Buzzer created and individually working, but can't get them to go off simultaneously.

# Function for error LED feedback
def errorLED():
    for i in range(3):
        GPIO.output(6,1)
        time.sleep(0.2)
        GPIO.output(6,0)
        GPIO.output(12,1)
        time.sleep(0.2)
        GPIO.output(12,0)
# Function for Buzzer Alarm Output
def buzzerAlarm():
    for i in range(3):
        GPIO.output(21,1)
        time.sleep(0.5)
        GPIO.output(21,0)
        time.sleep(0.5)

I looked into using the Threading module, but ran into issues when I tried to execute the thread for the Buzzer beeping multiple times. Below is the test code I created to try and run the Buzzer thread multiple times...

# Buzzer Test
buzzerSound = threading.Thread(target=buzzerAlarm())
buzzerSound.start()
time.sleep(3)
buzzerSound.start()
time.sleep(3)
buzzerSound.start()
time.sleep(3)

...and the error message I received.

Traceback (most recent call last):
  File "Keypad.py", line 277, in <module>
    buzzerSound.start()
  File "/usr/lib/python2.7/threading.py", line 730, in start
    raise RuntimeError("threads can only be started once")
RuntimeError: threads can only be started once

Am I on the right track with the threading module? If so, should I be killing the Buzzer thread inside of the BuzzerAlarm() function or should I not be using .start() to run the function? Or do I need to be doing something else entirely? If threading isn't the best way to go, I'm open to suggestions.


Solution

  • From the documentation:

    start()

    It must be called at most once per thread object.

    Therefore you should create a new Thread object if you want to start it again. For example:

    buzzer_thread = threading.Thread(target=buzzerAlarm())
    led_thread = threading.Thread(target=errorLED())
    led_thread.start()
    buzzer_thread.start()
    time.sleep(3)
    
    # Second time:
    buzzer_thread = threading.Thread(target=buzzerAlarm())
    led_thread = threading.Thread(target=errorLED())
    led_thread.start()
    buzzer_thread.start()
    

    You may consider refactoring this into a function:

    def buzzer_and_led():
        buzzer_thread = threading.Thread(target=buzzerAlarm())
        led_thread = threading.Thread(target=errorLED())
        led_thread.start()
        buzzer_thread.start()
        # Wait until both the buzzer and the LED stop
        while led_thread.is_alive() or buzzer_thread.is_alive():
           pass