Controlling NeoPixel LED



  • Hello,
    I am new with Pycom and working on a project which requires the use of a Sipy board. I have to blink LEDs connected to VCC, GND and Pin 22 through three wires. I do not know if these are WS2812 LEDs but thought of using the corresponding library (https://github.com/JanBednarik/micropython-ws2812/blob/master/ws2812.py). That or the Adafruit_Blinka librabry. However, both libraries seem to be working only for Wipy or Lopy board and also with the use of a SPI connection (which I am not using, but I could if easier). That is why I would like to know if it is possible to control those LEDs with Sipy and without using SPI ?
    I can provide you with further information if you need. Thank you all for your help


  • Global Moderator

    @brossingo I simplified the neopixel demo code using RMT a little bit. Also, I made a similar short function using SPI. It works to some extend (better than the other SPI driver), but the problem is the unpredictable timing of SPI. It had gaps between the words it sends, which the disturbs the communication.
    Here is the RMT variant

    from machine import RMT
    from time import sleep
    
    def send_rmt(data, rmt):
        from time import sleep_us
        no_pixel = len(data)
        bits = bytearray(no_pixel * 24 * 2)
        duration =  bytearray(no_pixel * 24 * 2)
        index = 0
        for pixel in data:
            for byte in pixel:
                mask = 0x80
                while mask:
                    bits[index] = 1
                    bits[index+1] = 0
                    if byte & mask:
                        duration[index] = 8
                        duration[index+1] = 4
                    else:
                        duration[index] = 4
                        duration[index+1] = 8
                    index += 2
                    mask >>= 1
        sleep_us(60) # wait for the reset time
        rmt.pulses_send(tuple(duration), tuple(bits))
        sleep_us(60) # wait for the reset time
    
    data = 6 * [
        (127, 0, 0),    # green
        (0, 127, 0),    # red
        (0, 0, 127),    # blue
        ]
    
    blank = 24 * [(0,0,0)]
    
    rmt = RMT(channel=3, gpio="P11", tx_idle_level=0)
    
    while True:
        send_rmt(data, rmt)
        sleep(1)
        send_rmt(blank, rmt)
        sleep(1)
    

    And here is the SPI variant:

    from machine import SPI, disable_irq, enable_irq
    from time import sleep, sleep_us
    
    def send_spi(data, spi):
        no_pixel = len(data)
        buffer = bytearray(no_pixel * 3 * 4)
        index = 0
        for pixel in data:
            for byte in pixel:
                bits = 0
                mask = 0x80
                while mask:
                    bits <<= 4
                    if byte & mask:
                        bits |= 0x0e
                    else:
                        bits |= 0x08
                    mask >>= 1
                buffer[index] = (bits >> 24) & 0xff
                buffer[index + 1] = (bits >> 16) & 0xff
                buffer[index + 2] = (bits >> 8) & 0xff
                buffer[index + 3] = bits & 0xff
                index += 4
        sleep_us(60) # ensure initial reset
        state = disable_irq()
        spi.write(buffer)
        enable_irq(state)
    
    data = 6 * [
        (85, 0, 0),    # green
        (0, 85, 0),    # red
        (0, 0, 85),    # blue
        (85, 85, 85),   # white
        ]
    
    blank = 24 * [(0,0,0)]
    spi = SPI(SPI.MASTER, baudrate=3200000, polarity = 0, firstbit = SPI.MSB, bits=32)
    
    while True:
        send_spi(data, spi)
        sleep(1)
        send_spi(blank, spi)
        sleep(1)
    

  • Global Moderator

    @brossingo Now I drew the skeleton of a module which uses RMT to send the data using a single GPIO port. It is not overly effective in matters of RAM usage, but it works well and has reliable timing. I tested that with a 24 NeoPixel device.
    Connection:
    WS2812 -> xxPy
    Vcc -> Vin (5v)
    GND -> GND
    Data -> GPIO (here: P11)

    from machine import RMT
    from time import sleep
    
    def send_rmt(data, rmt):
        no_pixel = len(data)
        bits = bytearray(no_pixel * 24 * 2 + 2)
        duration =  bytearray(no_pixel * 24 * 2 + 2)
        bits[0] = 0
        duration[0] = 255
        bits[0] = 1
        duration[1] = 255 # reset 51 µs
        index = 2
        for pixel in data:
            for byte in pixel:
                mask = 0x80
                while mask:
                    bits[index] = 1
                    bits[index+1] = 0
                    if byte & mask:
                        duration[index] = 8
                        duration[index+1] = 4
                    else:
                        duration[index] = 5
                        duration[index+1] = 9
                    index += 2
                    mask >>= 1
        rmt.pulses_send(tuple(duration), tuple(bits))
    
    data = [
        (127, 0, 0),    # green
        (0, 127, 0),    # red
        (0, 0, 127),    # blue
        (127, 127, 127),   # white
        (127, 0, 0),    # green
        (0, 127, 0),    # red
        (0, 0, 127),    # blue
        (127, 127, 127),   # white
        (127, 0, 0),    # green
        (0, 127, 0),    # red
        (0, 0, 127),    # blue
        (127, 127, 127),   # white
        (127, 0, 0),    # green
        (0, 127, 0),    # red
        (0, 0, 127),    # blue
        (127, 127, 127),   # white
        (127, 0, 0),    # green
        (0, 127, 0),    # red
        (0, 0, 127),    # blue
        (127, 127, 127),   # white
        (127, 0, 0),    # green
        (0, 127, 0),    # red
        (0, 0, 127),    # blue
        (127, 127, 127),   # white
    ]
    
    blank = 24 * [(0,0,0)]
    rmt = RMT(channel=3, gpio="P11", tx_idle_level=0)
    
    while True:
        send_rmt(data, rmt)
        sleep(1)
        send_rmt(blank, rmt)
        sleep(1)
    

  • Global Moderator

    @brossingo Using this module https://raw.githubusercontent.com/Gadgetoid/wipy-WS2812/master/ws2812alt.py with a few test lines I could get it working with SPI, but not well. Only certain brightness values can be used.

    from ws2812 import WS2812
    chain = WS2812(ledNumber=4)
    data = [
        (127, 0, 0),    # red
        (0, 127, 0),    # green
        (0, 0, 127),    # blue
        (127, 127, 127),   # white
    ]
    chain.show(data)
    

    Vcc of neopixel to Vin,
    GND to GND
    Data to P11
    So I'll give RMT a chance.



  • @brossingo is using WS2812 LEDs a requirement? There are alternatives that use a standard SPI interface instead of the timing-sensitive interface of the WS2812.


  • Global Moderator

    @brossingo You cannot find the neopixel_write() method on a pycom device, because that is specific to the esp module of micropython.org's firmware. I will try tonight to use the SPI lib you cited in your post.
    See also this post: https://forum.pycom.io/topic/2196/rgb/2
    There have been examples with pycom device to use the RMT module. If you search in the forum for WS2812 or neopixel, you'll find that. But the code there addresses the controller directly, and since then a more comfortable support for using the RMT has been implemented.



  • @robert-hh Thanks for answering. As I said, I did not want to use a SPI connection. My 2 LEDs only share 3 wires. I wanted to use this neopixel module :

    import neopixel_write
    from neopixel_write import neopixel_write
    
    class NeoPixel:
        def __init__(self, pin, n):
            self.pin = pin
            self.n = n
            self.buf = bytearray(n * 3)
            self.pin.init(pin.OUT)
    
        def __setitem__(self, index, val):
            r, g, b = val
            self.buf[index * 3] = g
            self.buf[index * 3 + 1] = r
            self.buf[index * 3 + 2] = b
    
        def __getitem__(self, index):
            i = index * 3
            return self.buf[i + 1], self.buf[i], self.buf[i + 2]
    
        def fill(self, color):
            r, g, b = color
            for i in range(len(self.buf) / 3):
                self.buf[i * 3] = g
                self.buf[i * 3 + 1] = r
                self.buf[i * 3 + 2] = b
    
        def write(self):
            neopixel_write(self.pin, self.buf, True)
    
    
    

    It seems really appropriate, but can't find a proper definition of the neopixel_write function which is really useful. I only have this for now (https://github.com/adafruit/Adafruit_Blinka/blob/master/src/neopixel_write.py) (i ignored everything above the definition of the function itstelf). The thing is, when I run it, I got this error message : NameError: name ':_neopixel' is not defined.



  • This post is deleted!

  • Global Moderator

    @brossingo You can use all Pycom devices with a generic peripheral like WS2812 LED's. The only thing you have to care about is whtjher a certain Pin is used by board internal peripherals. P22 on the SiPy can be used for the LEDs. The WS2812 needs 5v for operation, so you have to connect it to Vin. The data input may work with 3.3V levels. if not, you will need a level shifter or reduve the supply voltage of the WS2812 to about 4V. A simple 1N4001 Diode between Vin of the SiPy and Vcc of the WD2812 would do that reduction.
    The example you linked if for a Pyboard device. When used with a SiPy, it requires chages in the sections, where the SPI object is instantiated. You may also look at https://github.com/nickovs/ws2812-SPI/blob/master/neoSPI.py, which is also for a different MicroPython dialect, but more close to the PyCom PySpeak.


Log in to reply
 

Pycom on Twitter