NeoPixel fireflies jar with Raspberry Pi | HackSpace 40

This twinkly tutorial is fresh from the latest issue of HackSpace magazine, out now.

Adding flashing lights to a project is a great way to make it a little more visually appealing, and WS2812B LEDs (sometimes known as NeoPixels) are a great way to do that.

They have their own mini communications protocol, so you can control lots of them with just a single pin on your microcontroller, and there’s a handy library for Pico MicroPython that lets you control them.

First, you need to grab the library from hsmag.cc/PicoPython and copy the PY file to your Pico device. You can do this by opening the file in Thonny and clicking Save As, and then selecting your MicroPython device and calling it ws2812b.py.

You create an object with the following parameters: number of LEDs, state machine ID, and GPIO number, in that order. So, to create a strip of ten LEDs on state machine 0 and GPIO 0, you use:

pixels = ws2812b.ws2812b(10,0,0)

This object has two methods: show() which sends the data to the strip, and set_pixel which sets the colour values for a particular LED. The parameters are LED number, red, green, blue, with the colours taking values between 0 and 255.

At the time of writing, there’s an issue using this library in the interpreter. The author is investigating, but it’s best to run it from saved files to ensure everything runs properly. Create a file with the following and run it:

import ws2812b
import time

pixels = ws2812b.ws2812b(10,0,0)
pixels.set_pixel(5,10,0,0)
pixels.show()
time.sleep(2)
pixels.set_pixel(5,0,10,0)
pixels.show()
time.sleep(2)
pixels.fill(0,0,10)
pixels.show()
time.sleep(2)

So, now we can light up some LEDs, let’s take a look at how to turn this into an interesting light fixture.

We originally created the fireflies example in the WS2812B project for Christmas tree lights, but once the festive season was over, we liked them so much that we wanted to keep them going year round. Obviously, we can’t just keep a tree up all the time, so we needed another way to display them. We’re using them on thin-wire WS2812B LEDs that are available from direct-from-China sellers, but they should work on other types of WS2812B-compatible LEDs.

There are some other methods in the WS2812B module, such as set_pixel_line_gradient() to add effects to your projects

For display, we’ve put the string of LEDs into a glass demijohn that we used to use for brewing, but any large glass jar would work. This gives an effect inspired by fireflies trapped in a jar. You can just download the code and run it (it’s in the examples folder in the above repository), but let’s take a look and see how it works. The first part of the code sets everything up:

import time
import ws2812b
import random

bright_div = 20
numpix = 50 # Number of NeoPixels
strip = ws2812b.ws2812b(numpix, 0,0)

colors = [
[232, 100, 255], # Purple
[200, 200, 20], # Yellow
[30, 200, 200], # Blue
[150,50,10],
[50,200,10],
]

max_len=20
min_len = 5

flashing = []

num_flashes = 10

You can change numpix, and the details for creating the WS2812B object, to whatever’s suitable for your setup. The colors array holds the different colours that you want your LEDs to flash (in red, green, blue format). You can add to these or change them. We like the subtle pastels of this palette, but you can make it bolder by having more pure colours.

The max_len and min_ len variables control the length of time each light flashes for. They’re not in any units (other than iterations of the main loop), so you may need a little trial and error to get settings that are pleasing for you. The remaining code is what actually does the work of flashing each LED:

for i in range(num_flashes):
pix = random.randint(0, numpix - 1)
col = random.randint(1, len(colors) - 1)
flash_len = random.randint(min_len, max_len)
flashing.append([pix, colors[col], flash_len, 0, 1])

strip.fill(0,0,0)

while True:
strip.show()
for i in range(num_flashes):

    pix = flashing[i][0]
    brightness = (flashing[i][3]/flashing[i][2])
    colr = (int(flashing[i][1][0]*brightness),
            int(flashing[i][1][1]*brightness),
            int(flashing[i][1][2]*brightness))
    strip.set_pixel(pix, colr[0], colr[1], colr[2])

    if flashing[i][2] == flashing[i][3]:
        flashing[i][4] = -1
    if flashing[i][3] == 0 and flashing[i][4] == -1:
        pix = random.randint(0, numpix - 1)
        col = random.randint(0, len(colors) - 1)
        flash_len = random.randint(min_len, max_len)
        flashing[i] = [pix, colors[col], flash_len, 0, 1]
    flashing[i][3] = flashing[i][3] + flashing[i][4]
    time.sleep(0.005)

The flashing list contains an entry for every LED that’s currently flashing. It stores the LED position colour, length of the flash, current position of the flash, and whether it’s getting brighter or dimmer. These are initially seeded with random data; then we start a loop that keeps updating the display.

That’s all there is to it. You can tweak this code or create your very own custom display.

Issue 40 of Hackspace Magazine is out NOW

Front cover of Hack space magazine featuring Pico on pink and black background

Each month, HackSpace magazine brings you the best projects, tips, tricks and tutorials from the makersphere. You can get it from the Raspberry Pi Press online store, The Raspberry Pi store in Cambridge, or your local newsagents.

Each issue is free to download from the HackSpace magazine website.

2 comments
Jump to the comment form

Avatar

Does anybody have a link to the ” thin-wire WS2812B”?

Reply to Jerry Wasinger

Avatar

There’s one which might suit at https://shop.pimoroni.com/products/rgb-led-wire , though the author referred to “direct from China sellers” who might be lower cost (if slower/less reliable delivery). But note the warning on the Pimoroni page about possible risks of shorting if bundled together. I have a set (from Pimoroni) and checked with a test meter; the wire appears to be formed of multiple strands of fine enamelled wire. The wire between LEDs is therefore insulated. If not damaged, I suspect that risks of shorting would only be where the data wire is cut at each LED (to change from DOUT to DIN). You can see these cut ends in the photo on the Pimoroni page. Note that I have not (yet) tested these LEDS with the pico.

Reply to Chris Wallwork

Leave a Comment