Unpredictable Blinkt! results w/ > 1 process controlling LEDs

Hi!

I’m using a Blinkt! and pi Zero W to create a means to indicate when my various home automation bits are in certain states (wemo switches and plugs on, garage door open, etc…)

Everything works fine when I’ve got one script running, but when I run another that is controlling a different set of LEDs, things get unpredictable. LEDs come on w/ different brightnesses and colors, unpredictable durations, etc. I thought that putting everything in one script may be helpful in case there was some initialization process that was messing with things, but this yields some crazy results as well. Here’s the code I wrote to have a simple test case that I thought had the best chance of showing that it was something that I was doing - 2 functions controlling 2 different groups of LEDs, running in parallel:

from time import sleep
from threading import Thread
from blinkt import set_pixel, show

def red():
while True:
    for l in range(0,4,1):
            set_pixel(l, 1, 0, 0)
            show()
            sleep(.25)
    for l in range(3,-1,-1):
            set_pixel(l, 0, 0, 0)
            show()
            sleep(.25)
def blue():
while True:
    for l in range(4,8,1):
            set_pixel(l, 0, 0, 1)
            show()
            sleep(.25)
    for l in range(7,3,-1):
            set_pixel(l, 0, 0, 0)
            show()
            sleep(.25)

if __name__ == '__main__':
    Thread(target = red).start()
    Thread(target = blue).start()

Things sometimes start out as I’d expect, but get crazy eventually…Any thoughts on how I can make this work? Is my only option to write a single function to deal with LEDs, and feed information on what should be lit from others?

Thanks in advance!

Calling show() from different threads without any lock (mutex) will sooner or later cause funny effects. The reason is that show() needs to shift the pixel RGB and brightness values to the hardware, and this isn’t protected by any lock.

So, one way may be to put the same mutex around calls to show(). If I remember correctly, the set_pixel() method should be safe.

I’d suspect that the library doesn’t support multi-threading and that writing pixels simultaneously is messing things up. You’ll want a single thread responsible for doing the setting of LEDs with others potentially sending messages in to this one. I’ve got some code that might be appropriate, I’ll see if I can dig it up.

Alternatively, @thediveo 's idea might be easier.

FWIW you can do reversed(range(0,4,1))

Sorry for the noob’dness, but can you point me toward how to do that? Looks like mutex is deprecated in recent versions of python? Thanks!

Thanks for the reversed pointer as well, @major_tomm

threading.Lock should be fine in your case, it’s current as of Python 3.x. A Lock cannot be used recursively, but I understand your example to not needing recursion.

My usual approach to this pattern/problem is to have a single thread responsible for running show() periodically, in your case the simplest approach might be to remove show() from both threads and, instead, place it in a while True loop in the main thread with an appropriate time.sleep(1.0/30) where 30 is your desired Blinkt! update rate in updates per second leaving you with:

from time import sleep
from threading import Thread
from blinkt import set_pixel, show

FPS = 30

def red():
while True:
    for l in range(0,4,1):
            set_pixel(l, 1, 0, 0)
            sleep(.25)
    for l in range(3,-1,-1):
            set_pixel(l, 0, 0, 0)
            sleep(.25)
def blue():
while True:
    for l in range(4,8,1):
            set_pixel(l, 0, 0, 1)
            sleep(.25)
    for l in range(7,3,-1):
            set_pixel(l, 0, 0, 0)
            sleep(.25)

if __name__ == '__main__':
    Thread(target = red).start()
    Thread(target = blue).start()
    while True:
        show()
        time.sleep(1.0/FPS)

In this case your pixel colour changes will not be synced to updates, resulting in some imprecise timing, but since anything timed with time.sleep() is not especially precise in the first place that’s kind of academic.

(Note: time.sleep() is imprecise because you’re not taking into account the amount of time it takes for the rest of the code to run, which can be variable depending on the Pi you happen to be running it on)

2 Likes

Duh! Fantastic! Thank you!

o/

Hello guys,

I am in the same situation but using 2 different processes (2 different python scripts) running simultaneously and accessing the blinkt leds. when 1 process dies, it switches off all leds upon failure, catching this event, other process will switch on an led for indicating failure of 1st process. This is not working as expected. When I debugged, it is entering the set_pixel function, setting all rgb and brightness values correctly. but led light is not coming up. could you please help me with this.

This bug was fixed, and your issue is unrelated.