8

I've connected red and green leds via GPIO and I can successfully switch them on/off using RPi.GPIO - now I'd like to:

  • start an led blinking on/off every 1 second
  • have the script continue its execution
  • stop the led from blinking on another signal

A similar example is the project Raspberry Pi E-mail Notifier Using LEDs, except in that case the leds are lit solidly, and the script just waits until the next check:

if newmails > NEWMAIL_OFFSET:
  GPIO.output(GREEN_LED, True)
  GPIO.output(RED_LED, False)
else:
  GPIO.output(GREEN_LED, False)
  GPIO.output(RED_LED, True)
time.sleep(MAIL_CHECK_FREQ)

Is it possible to do something like this pseudo-code?

cont = True
while cont:
  check for mail
  if new mail:
    flashLed(green)
  else:
    flashLed(red)

  do other stuff while the relevant light blinks (not just time.sleep)

def flashLed(colour):
    stop blinking the other colour
    start blinking this colour
    return to the calling program

For this to work I imagine that the flashLed function would need to start the relevant led flashing, then return execution to the main script body.

Is this possible using Python? Is there a better approach?

5 Answers 5

7

Thanks to @user451777 for the tip on threading. I modified the Signaling Between Threads script and came up with the following:

import threading, time
from random import randint


def flashLed(e, t):
    """flash the specified led every second"""
    while not e.isSet():
        print(colour)
        time.sleep(0.5)
        event_is_set = e.wait(t)
        if event_is_set:
            print('stop led from flashing')
        else:
            print('leds off')
            time.sleep(0.5)

colour = "red"
e = threading.Event()
t = threading.Thread(name='non-block', target=flashLed, args=(e, 2))
t.start()

for i in range(0, 10):
    # Randomly assign red or green every 10 seconds
    randomNumber = randint(0,10)
    print(randomNumber)
    if(randomNumber < 5):
        colour = "green"
    else:
        colour = "red"
    time.sleep(10)

e.set()

This simulates turning the relevant led on/off in one thread while the program execution continues in the main thread. Crucially, the flashLed function doesn't lock up execution while the leds are blinking.

The main thread uses a global variable to set the led colour, with the other thread using the global variable to switch on the relevant led. I believe global variables are generally frowned upon, so I'd be interested to hear of any better approaches.

2
  • Stephen thank you for posting your solution - extremely helpful
    – jpw
    Jan 14, 2016 at 19:21
  • event_is_set = e.wait(t) should be replaced with event_is_set = e.is_set(). Otherwise, you are adding a delay of t for every call when the flag is off.
    – max
    Apr 16, 2017 at 19:46
4

I'm relatively new to Python, but I have been able to flash the LEDs while continuing execution by using Pulse Width Modulation(PWM) which is built into the RPi.GPIO module. This in my opinion was a much easier and simpler way than threading as other anwsers suggest, however both methods would work well.

Simply create a PWM object with the desired frequency and then call PWM.start(DC) and PWM.ChangeDutyCycle(DC) to start/alter the LED duty cycle. Continue on with your code and then call PWM.stop() to stop the LED.

    #setup GPIO pins and import GPIO module
    g = GPIO.PWM(pin1,1)
    r = GPIO.PWM(pin2,1)
    g.start(0)
    r.start(0)
    greenStatus = False
    redStatus = False

    def flashLED(color):
        global greenStatus, redStatus
        if color == 'green':
            if redStatus:
                r.ChangeDutyCycle(0)
                redStatus = False
            if not greenStatus:
                g.ChangeDutyCycle(50)
                greenStatus = True
       elif color == 'red':
           if greenStatus:
               g.ChangeDutyCycle(0)
               greenStatus = False
           if not redStatus:
               r.ChangeDutyCycle(50)
               redStatus = True
    try:    
        while True:
            if newMail():
                flashLED('green')
            else:
                flashLED('red')
            #do other stuff here and LED will continue flashing
    except KeyboardInterrupt:
        g.stop()
        r.stop()
        GPIO.cleanup()

Basically this code starts both LEDs with a duty cycle of 0 so they appear off. Every time flashLED(color) is called, it checks to make sure the opposite LED is off, if its not then its duty cycle is reset to 0. Then it turns on the other LED if it is not already turned on. The code is wrapped in a try/except so that at the end both PWM objects are stopped and the GPIO pins are cleaned up.

3

I would recommend threading. I've not used it in Python to give example but it will created another thread of execution that runs parallel to the main thread of execution. Hopefully some code examples follows for you.

http://pymotw.com/2/threading/

2

@Stephen Lead, re you question on globals. Sometimes, with things like threading functions globals seem like a reasonable options as the alternatives can be more complicated. However it's a good habit to always explicitly declare them as global

def flashLed(e, t):
    """flash the specified led every second"""
    global colour
    while not e.isSet():

because at some stage you will try to assign to the variable and struggle to figure out why it doesn't seem to have any effect.

You can pass arguments to the function that are altered by other parts of your program or are altered by your function but the arguments have to be objects that are mutable. The easiest example would be a list, then you end up with the, only slightly artificial:

def flashLed(e, t, colour):
    ...
        print(colour[0])

colour = ["red"]
e = threading.Event()
t = threading.Thread(name='non-block', target=flashLed, args=(e, 2, colour))
...
        if(randomNumber < 5):
            colour[0] = "green"

If you had more extensive information to communicate between the two threads then a list might seem more logical. Queues are also fairly simple to use and worth checking out in the docs.

1

You could do something like this:

while True:
    if newMail(): # returns true if new mail, false if not
        flashLED("green")
    else:
        flashLED("red")

def flashLED(colour):
    if colour == "red":
        pin = 18 # replace number with GPIO number
    if colour == "green":
        pin = 24 # replace number with GPIO number
    GPIO.output(pin,True) # replace pin with GPIO number
    time.sleep(1)
    GPIO.output(pin,False)

The advantage of this is there is no threading involved. You would have to build newMail() yourself, unless there is a similar function available in a library (this answer provides a solution for Gmail and POP)

2
  • Thanks for the tip. But unless I'm mistaken, that will only blink the leds once, then exit the function. I was hoping to start the led blinking, keep it blinking while I did other tasks, then stop the blinking at a later signal. It looks like this may actually require a separate thread Mar 25, 2015 at 22:00
  • 1
    That code will keep the LED blinking, once every time the loop executes. If you want to do other tasks, just put them after the if/else.
    – developius
    Mar 26, 2015 at 10:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.