Page 1 of 2

Working WS2812 LED Example

Posted: Fri Jun 06, 2014 10:34 am
by Markus Gritsch
Hi,

here is some MicroPython code which drives a WS2812 LED:

Code: Select all

# MicroPython example for controlling one WS2812 LED.
# Method similar to http://www.espruino.com/WS2811

from pyb import SPI

spi = SPI(1, SPI.MASTER, baudrate=6400000, polarity=0, phase=1)
spi.send(chr(0x00))

def byte2bits(byte):
    b0 = chr(0x03)
    b1 = chr(0x0F)
    bits = ''
    mask = 0x80
    while mask != 0:
        bits += b0 if ( byte & mask ) == 0 else b1
        mask >>= 1
    return bits

def sendColor(red, green, blue):
    spi.send(byte2bits(green)+byte2bits(red)+byte2bits(blue))

import math

n = 0
while True:
    r = int((1 + math.sin(n * 0.1324)) * 127)
    g = int((1 + math.sin(n * 0.1654)) * 127)
    b = int((1 + math.sin(n * 0.1)) * 127)
    sendColor(r, g, b)
    n += 1
    pyb.delay(20)
2014-06-06_12-10-59-554.png
2014-06-06_12-10-59-554.png (63.23 KiB) Viewed 19260 times
IMG_5759_800x600_80.jpg
IMG_5759_800x600_80.jpg (93.43 KiB) Viewed 19260 times
Have fun,
Markus

Re: Working WS2812 LED Example

Posted: Sun Jun 08, 2014 1:56 pm
by pfalcon
Markus Gritsch, great work and report on results! It only misses a video (heck, it's about changing colors, I wanna see that!) to deserve an entry in "Projects" forum. Well, just kidding - surely, it deserves to be there already, and please kindly consider postinf future posts like this there - that's where new users will look for expiration and ideas, and we don't want them to think there's lack of cool things to do with MicroPython, right? Thanks!

Re: Working WS2812 LED Example

Posted: Fri Jun 13, 2014 8:19 am
by fma
Hi Markus!

Thanks for sharing your code. I'm using it to drive a 11x11 leds matrix. For now, I only soldered 11 leds, and you code works fine (I didn't see the issue you reported here, except when the SD card is enumarated/mounted, which is not really surprising).

Just to say that I'm very interested in any progress you can make on this project ;)

Re: Working WS2812 LED Example

Posted: Fri Jun 13, 2014 10:58 am
by Markus Gritsch
The PCBs finally arrived and driving 10 LEDs works with an acceptable frame-rate: http://youtu.be/GHmoE9ui4NU

Driving over hundred LEDs might be difficult with my approach, since it will get quite slow, and I am not sure if there is enough memory to hold the data string without any optimization.

Here is the updated code:

Code: Select all

# MicroPython example for controlling one WS2812 LED.
# Method similar to http://www.espruino.com/WS2811

from pyb import SPI

spi = SPI(1, SPI.MASTER, baudrate=6400000, polarity=0, phase=1)
spi.send(chr(0x00))

def byte2bits(byte):
    b0 = chr(0x03)
    b1 = chr(0x0F)
    bits = ''
    mask = 0x80
    while mask != 0:
        bits += b0 if ( byte & mask ) == 0 else b1
        mask >>= 1
    return bits

import math
import gc

buf = bytearray(10 * 3)
n = 0
while True:
    pos = 0
    while pos < len( buf ):
        red = int((1 + math.sin((n + pos) * 0.1324)) * 127)
        green = int((1 + math.sin((n + pos) * 0.1654)) * 127)
        blue = int((1 + math.sin((n + pos) * 0.1)) * 127)
        buf[pos] = green; buf[pos + 1] = red; buf[pos + 2] = blue
        pos += 3
    data = ''.join(list(map(byte2bits, buf)))
    pyb.disable_irq()
    spi.send(data)
    pyb.enable_irq()
    gc.collect()
    print(n)
    n += 1

Re: Working WS2812 LED Example

Posted: Fri Jun 13, 2014 11:12 am
by fma
Nice!

I tried to activate 121 leds, and I get a out of memory error :mrgreen:

I also tried to use code emitters on byte2bits() function, but none of them work; I don't remember the errors. What can cause a problem, here?

Re: Working WS2812 LED Example

Posted: Sat Jun 14, 2014 8:01 pm
by Markus Gritsch
fma wrote:What can cause a problem, here?
Follow the directions pfalcon gave here: http://forum.micropython.org/viewtopic. ... p=621#p621

Re: Working WS2812 LED Example

Posted: Sun Jun 15, 2014 1:18 pm
by randomhuman
Nice one, works well with Adafruit "Neopixels" as well, they use that same LED.

Re: Working WS2812 LED Example

Posted: Sun Jun 15, 2014 4:14 pm
by randomhuman
It is possible to run the SPI bus at half the baud rate, as the Espruino example was doing. This requires only four bits to be sent per actual bit of color data, which should help with the memory requirements. This might not be the best way to do it, but what I have looks like this:

Code: Select all

from pyb import SPI

spi = SPI(2, SPI.MASTER, baudrate=3200000, polarity=0, phase=1)
spi.send(chr(0x00))

def convert_byte_4(byte, zero_bits, one_bits):
    bits = bytearray(4)
    mask = 0x80
    for bit in range(0, 8, 2):
        bit1 = (zero_bits if (byte & (mask >> bit) == 0) else one_bits)
        bit2 = (zero_bits if (byte & (mask >> (bit + 1)) == 0) else one_bits)
        bits[bit//2] = bit1 << 4 | bit2
    return bits

def send_color(red, green, blue):
    ba = bytearray()
    for byte in (green, red, blue):
        converted = convert_byte_4(byte, 0x01, 0x3)
        for b in converted:
            ba.append(b)
    spi.send(ba)


Re: Working WS2812 LED Example

Posted: Mon Jul 07, 2014 2:47 pm
by Markus Gritsch
Just for completeness, here is the code without the big string concatenation. However, it still runs 2.5 times faster with the explicit call to gc.collect() in place.

Code: Select all

# MicroPython example for controlling one WS2812 LED.
# Method similar to http://www.espruino.com/WS2811

import pyb
from pyb import SPI

spi = SPI(1, SPI.MASTER, baudrate=6400000, polarity=0, phase=1)
spi.send(chr(0x00))

import math
import gc

led_count = 10
buf = bytearray(led_count * 3)
spi_data = bytearray(len(buf) * 8)

def buf2data():
    b0 = 0x03
    b1 = 0x0F
    i = 0
    size = len(spi_data)
    while i < size:
        byte = buf[ i >> 3 ]
        mask = 0x80
        while mask != 0:
            spi_data[ i ] = b0 if ( byte & mask ) == 0 else b1
            mask >>= 1
            i += 1

def send_buf():
    buf2data()
    pyb.disable_irq()
    spi.send(spi_data)
    pyb.enable_irq()
    gc.collect()

n = 0

def step():
    global n
    pos = 0
    while pos < len( buf ):
        shift = 0
        red = int((1 + math.sin((n + pos) * 0.1324)) * 127) >> shift
        green = int((1 + math.sin((n + pos) * 0.1654)) * 127) >> shift
        blue = int((1 + math.sin((n + pos) * 0.1)) * 127) >> shift
        buf[pos] = green; buf[pos + 1] = red; buf[pos + 2] = blue
        pos += 3
    send_buf()
    print(n)
    n += 1

def loop():
    while True:
        step()
        pyb.delay(10)

loop()

Re: Working WS2812 LED Example

Posted: Mon Jul 07, 2014 10:25 pm
by kfricke
Slight modification makes a rudimentary clock on a led ring. The time is off by a bunch of units, but works like a charm. I do use one of those 12x LED rings.

Code: Select all

# MicroPython example for controlling one WS2812 LED.
# Method similar to http://www.espruino.com/WS2811

import pyb
from pyb import SPI

spi = SPI(1, SPI.MASTER, baudrate=6400000, polarity=0, phase=1)
spi.send(chr(0x00))

import math
import gc

led_count = 12
buf = bytearray(led_count * 3)
spi_data = bytearray(len(buf) * 8)

def buf2data():
    b0 = 0x03
    b1 = 0x0F
    i = 0
    size = len(spi_data)
    while i < size:
        byte = buf[ i >> 3 ]
        mask = 0x80
        while mask != 0:
            spi_data[ i ] = b0 if ( byte & mask ) == 0 else b1
            mask >>= 1
            i += 1

def send_buf():
    buf2data()
    pyb.disable_irq()
    spi.send(spi_data)
    pyb.enable_irq()
    gc.collect()

rtc = pyb.RTC()

def step():
    global rtc
    
    dt = rtc.datetime()
    hour = dt[4]
    minute = dt[5]
    second = dt[6]
    print(hour, minute, second)
    
    pos = 0
    for i in range(0, len(buf)):
        buf[i] = 0
        
    buf[int(hour / 2) * 3 + 1] = 8
    buf[int(minute / 5) * 3 + 2] = 8
    buf[int(second / 5) * 3] = (second % 2) * 16
    
    send_buf()

def loop():
    while True:
        step()
        pyb.delay(500)

loop()