Wikijunior:Raspberry Pi/Raspberry Pi Python Individually Controllable LEDs (ws2811)

Tutorial by Andrew Oakley & Jonathan Teague - Public Domain
2020-03-14 www.cotswoldjam.org

This tutorial will cover using individually controllable strings of LEDs based on the ws2811 controller, also known as 'NeoPixels'.

The kit

edit
 

In your bag you will find the following components:

  • ×3 M-F Jumper leads (pin to socket)
  • ×1 3-way 3A connector strip
  • ×1 String of 3 5V ws2811 LEDs

Putting it together

edit

Step 1: Take the jumper lead that goes to the WHITE wire to the LEDs and plug the socket (female end) into pin 6 (GND) on the Pi.

Step 2: Take the jumper lead that goes to the RED wire to the LEDs and plug the socket (female end) into pin 2 (5V) on the Pi.

Step 3: Take the jumper lead that goes to the GREEN wire to the LEDs and plug the socket (female end) into pin 12 (GPIO 18) on the Pi.

     

The program

edit

Power up your Raspberry Pi. From the desktop menu, select Programming – Python 3 (IDLE). Then use File, New File to create a new program.

Type in the following program then use File, Save to save your program as the name of your choice (don't forget to put .py on the end).

import board, neopixel, time
pixels = neopixel.NeoPixel(board.D18, 3, auto_write=False, pixel_order=neopixel.RGB)

pixels[0] = (255, 0, 0)
pixels[1] = (0, 255, 0)
pixels[2] = (0, 0, 255)
pixels.show()

time.sleep(2)

pixels.fill((0, 0, 0))
pixels.show()

You can also find this program in the Pi Home folder python/addrleds/neopixels-start.py

Running your program.

To run the program start a Terminal – click the   icon on the top then once the terminal window opens type:

sudo python3 thenameyouchose.py

If it's worked you should see the 3 LEDs light up red, green and blue for two seconds and then turn off.

What does the program do?

edit

The imports load the NeoPixels libraries (and time so we can insert a delay).

pixels = neopixel.NeoPixel(board.D18, 3, auto_write=False, pixel_order=neopixel.RGB)

This tells the library we've connected our LED string to GPIO 18, there are 3 LEDs and auto_write=False means only update the LEDs when we call pixels.show(). Lastly, we note that the colours are in the order Red, Green, Blue (RGB).

pixels[0] = (255, 0, 0)

This sets the first LED to RED, the 3 values are (R, G, B) – so each of the 3 LEDs lights up a different colour.

pixels.fill((0, 0, 0))

This sets all the pixels to 0 which means off, if we don't do this the LEDs will stay on.

What next?

edit

Now do your own thing! Make a disco sequence!

How about a traffic light sequence? Note that yellow light is a mix of red and green light, e.g: (255,255,0)

 

Hint: Put it in a while True: block so it goes on forever. And don’t forget to indent your code within the while block like this line is!

Hold down the CTRL key and press C to stop the while True: sequence.

You can find an example program in the Pi Home folder python/addrleds/neopixels-traffic.py.

How about a sequence of lights that measures 20 seconds, the time you need to do a good job of washing your hands?

You can find an example program in the Pi Home folder python/addrleds/neopixels-countdown.py.

If you stop your program and the lights are still on, you can use the neopixels-off.py program to turn them off.

How does it work?

edit

Each LED is connected to a ws2811 integrated circuit (IC) – it's the IC that's called a ws2811, the LED is just a three-colour LED. The Pi generates a long sequence of 0's and 1's. 24 bits for each LED (3 sets of 8-bits for each colour Red|Green|Blue), so for our string of 3 LED the Pi will generate 3 × 24 bits = 72 bits.

The first device in the string takes the fist 24 bits and uses those to set the Red|Green|Blue on its LED. It then passes the remaining 48 bits on to the second LED. That in turn takes what are now the first 24 bits from what it has received as its RGB and passes on the remaning 24 bits to the last LED.

For full details the datasheet is here: http://www.world-semi.com/DownLoadFile/129

Making it work at home

edit

If you're not using our ready-made SD card, you need to do some setup from Raspbian Buster:

  1. Raspberry menu – Preferences – Raspberry Pi Configuration – Interfaces – SPI enable – I2C enable – OK
  2. Raspberry menu – Shutdown – Reboot

Then install the libraries:

sudo pip3 install rpi_ws281x adafruit-circuitpython-neopixel

You can download our sample code from: www.cotswoldjam.org/downloads/2020-03

Notes on using ws2811 in the real world

edit

We’ve only given you a short string of LEDs as that’s all the Pi can reliably power. If you're using a long strip of ws2811 or ws2812 then there are two things you'll need to consider.

  1. ws2811 strips can be 5V or 12V, ws2812 are 5V. The Pi can only provide 5V at a low current sufficient to drive a few LEDs. To drive long strips at 5V or any at 12V you'll need to provide additional power, possibly at multiple points along the strip. If you're going to run a strip all round your bedroom ceiling 😊 then the Pi is only going to provide the control logic for the LEDs not the power.
  2. The control logic for ws2811 & ws2812 is specified at 5V, the Pi GPIO pins are 3.3V. For this tutorial we get away with using GPIO but to be sure it's going to work you would probably want to use a logic level shifter to turn the Pi's 3.3V into 5V.

Files

edit

neopixels-countdown.py

edit
#!/usr/bin/python3
import board, neopixel, time
pixels = neopixel.NeoPixel(board.D18, 3, auto_write=False, pixel_order=neopixel.RGB)

# Count 20 seconds for washing our hands!

# Start with pixels off
pixels.fill((0, 0, 0))
pixels.show()

# First 5 seconds are red - count three forward
# Notice how we start counting from zero
# and stop before we reach 3
for x in range(0,3):
  pixels.fill((0, 0, 0))
  pixels[x] = (255, 0, 0)
  pixels.show()
  time.sleep(1)

# Now count two back
# We start at 1
# and stop before we reach -1
# and count -1 each time
for x in range(1,-1,-1):
  pixels.fill((0, 0, 0))
  pixels[x] = (255, 0, 0)
  pixels.show()
  time.sleep(1)

# Next 5 seconds are yellow
for x in range(0,3):
  pixels.fill((0, 0, 0))
  pixels[x] = (255, 255, 0)
  pixels.show()
  time.sleep(1)
for x in range(1,-1,-1):
  pixels.fill((0, 0, 0))
  pixels[x] = (255, 255, 0)
  pixels.show()
  time.sleep(1)

# Next 5 seconds are green
for x in range(0,3):
  pixels.fill((0, 0, 0))
  pixels[x] = (0, 255, 0)
  pixels.show()
  time.sleep(1)
for x in range(1,-1,-1):
  pixels.fill((0, 0, 0))
  pixels[x] = (0, 255, 0)
  pixels.show()
  time.sleep(1)

# Final 5 seconds are blue
for x in range(0,3):
  pixels.fill((0, 0, 0))
  pixels[x] = (0, 0, 255)
  pixels.show()
  time.sleep(1)
for x in range(1,-1,-1):
  pixels.fill((0, 0, 0))
  pixels[x] = (0, 0, 255)
  pixels.show()
  time.sleep(1)

# Finish with pixels off
pixels.fill((0, 0, 0))
pixels.show()

neopixels-demo.py

edit
#!/usr/bin/python3
import board
import neopixel
from random import randrange
import time


# NeoPixels must be connected to D10, D12, D18 or D21 to work.
pixel_pin = board.D18

# The number of NeoPixels
num_pixels = 3

# The order of the pixel colors - RGB or GRB. Some NeoPixels have red and green reversed!
ORDER = neopixel.GRB

pixels = neopixel.NeoPixel(pixel_pin, num_pixels, auto_write=False,
                           pixel_order=ORDER)


def wheel(pos):
    # Input a value 0 to 255 to get a color value.
    # The colours are a transition r -> g -> b -> back to r.
    if pos < 0 or pos > 255:
        r = g = b = 0
    elif pos < 85:
        r = int(pos * 3)
        g = int(255 - pos*3)
        b = 0
    elif pos < 170:
        pos -= 85
        r = int(255 - pos*3)
        g = 0
        b = int(pos*3)
    else:
        pos -= 170
        r = 0
        g = int(pos*3)
        b = int(255 - pos*3)
    return (r, g, b)


def rainbow_cycle(wait):
    for j in range(255):
        for i in range(num_pixels):
            pixel_index = (i * 256 // num_pixels) + j
            pixels[i] = wheel(pixel_index & 255)
        pixels.show()
        time.sleep(wait)


def fade_in_out(wait):
    for j in range(255):
        pixels[0] = (j,0,0)
        pixels[1] = (0,j,0)
        pixels[2] = (0,0,j)
        pixels.show()
        time.sleep(wait)
    for j in range(254,0,-1):
        pixels[0] = (j,0,0)
        pixels[1] = (0,j,0)
        pixels[2] = (0,0,j)
        pixels.show()
        time.sleep(wait)


def random_leds(times, wait):
    for i in range(times):
        for j in range(num_pixels):
            pixels[j] = (randrange(256),randrange(256),randrange(256))
        pixels.show()
        time.sleep(wait)


while True:
    pixels.fill((255, 0, 0))   # Red = 255, Green = 0, Blue = 0
    pixels.show()
    time.sleep(1)

    pixels.fill((0, 255, 0))
    pixels.show()
    time.sleep(1)

    pixels.fill((0, 0, 255))
    pixels.show()
    time.sleep(1)

    pixels.fill((255, 255, 255))
    pixels.show()
    time.sleep(1)

    rainbow_cycle(0.02)

    fade_in_out(0.02)

    for i in range(num_pixels):
        pixels.fill((0,0,0))
        pixels[i] = (255, 0, 0)
        pixels.show()
        time.sleep(1)

    random_leds(100, 0.1)

neopixels-explained.py

edit
#!/usr/bin/python3
# To set everything up in Raspbian Buster:
#   Raspberry menu - Preferences - Raspberry pi configuration - SPI enable - I2C enable
#   Raspberry menu - Shutdown - Reboot
# Then install the libraries:
#   sudo pip3 install rpi_ws281x adafruit-circuitpython-neopixel
# Note that you may need to use sudo to run programs using the neopixel library
# You can manage without sudo by just using rpi_ws281x, but it's not as easy as neopixel

import board, neopixel, time

# Our strip is on pin 18
# There are 3 LED in our strip
# Don't automatically update the pixels without the show() command
# Colours are in Red Green Blue order (some strips use GRB)
pixels = neopixel.NeoPixel(board.D18, 3, auto_write=False, pixel_order=neopixel.RGB)

pixels[0] = (255, 0, 0)
pixels[1] = (0, 255, 0)
pixels[2] = (0, 0, 255)
pixels.show()

time.sleep(2)

pixels.fill((0, 0, 0))
pixels.show()

neopixels-off.py

edit
#!/usr/bin/python3
import board, neopixel, time
pixels = neopixel.NeoPixel(board.D18, 3, auto_write=False, pixel_order=neopixel.RGB)
pixels.fill((0, 0, 0))
pixels.show()

neopixels-start.py

edit
import board, neopixel, time
pixels = neopixel.NeoPixel(board.D18, 3, auto_write=False, pixel_order=neopixel.RGB)

pixels[0] = (255, 0, 0)
pixels[1] = (0, 255, 0)
pixels[2] = (0, 0, 255)
pixels.show()

time.sleep(2)

pixels.fill((0, 0, 0))
pixels.show()

neopixels-traffic.py

edit
import board, neopixel, time
pixels = neopixel.NeoPixel(board.D18, 3, auto_write=False, pixel_order=neopixel.RGB)

while True:
  pixels[0] = (255, 0, 0)
  pixels[1] = (0, 0, 0)
  pixels[2] = (0, 0, 0)
  pixels.show()
  time.sleep(10)

  pixels[0] = (255, 0, 0)
  pixels[1] = (255, 255, 0)
  pixels[2] = (0, 0, 0)
  pixels.show()
  time.sleep(2)

  pixels[0] = (0, 0, 0)
  pixels[1] = (0, 0, 0)
  pixels[2] = (0, 255, 0)
  pixels.show()
  time.sleep(14)

  pixels[0] = (0, 0, 0)
  pixels[1] = (255, 255, 0)
  pixels[2] = (0, 0, 0)
  pixels.show()
  time.sleep(3)