Snek and Neopixels

(click on the picture to see the movie)

Adafruit sells a bunch of things using the Neopixel name that incorporate Worldsemi WS2812B full-color LEDs with built-in drivers. These devices use a 1-wire link to program a 24-bit rgb value and can be daisy-chained to connect as many devices as you like using only one GPIO.

Bit-banging Neopixels

The one-wire protocol used by Neopixels has three signals:

  • Short high followed by long low for a 0 bit
  • Long high followed by a short low for a 1 bit
  • Really long low for a reset code

Short pulses are about 400ns, long pulses are around 800ns. The reset pulse is anything over about 50us.

I'd like to use some nice clocked signal coming out of the part to generate these pulses. A SPI output would be ideal; set the bit rate to 400ns and then send three SPI bits for each LED bit, either 100 or 110. Alas, none of the boards I've got connect the Neopixels to a pin that can be used as for SPI MOSI.

As a fallback, I tried using DMAC to toggle the GPIO outputs. Alas, on the SAMD21G part included in these boards, the DMAC controller can't actually write to the GPIO control registers. There's a missing connection inside the chip.

So, like all of the examples I found, I fell back to driving the GPIO registers directly with the processor, relying on a carefully written sequence of operations to get the timing within the tolerance required by the Neopixels. I have to disable interrupts during this process to avoid messing up the timing though.

Current Snek Neopixel API

I looked at the Circuit Python Neopixel API to see if there was anything I could adapt for Snek. That API uses 3-element tuples for the R,G,B values, and then places those in a list, one for each pixel in the chain. That seemed like a good idea. However, that API also has a lot of allocation churn, with new colors being created in newly allocated lists. Doing that with Snek would probably be too slow as Snek uses a garbage collector for allocation.

So, we'll allow mutable lists inside of a list or tuple, then Neopixel colors can be changed by modifying the value within the per-Neopixel lists.

Snek doesn't have objects, so we'll just create a function to send color data for a list of Neopixels out a pin. We'll use the existing Snek GPIO function, talkto, to select the pin. Finally, I'm using color values from 0-1 instead of 0-255 to make this API work more like the other analog interfaces.

> pixels = ([0.2, 0, 0],)
> talkto(NEOPIXEL)
> neopixel(pixels)

That make the first Neopixel a not-quite-blinding red. Now we can turn it green with:

> pixels[0][0] = 0
> pixels[0][1] = 0.2
> neopixel(pixels)

You can, of course, use tuples like with Circuit Python:

> pixels = [(0.2, 0, 0)]
> talkto(NEOPIXEL)
> neopixel(pixels)
> pixels[0] = (0, 0.2, 0)
> neopixel(pixels)

This does allocate a new list though.

Snek on Circuit Playground Express

As you can see in the pictures above, Snek is running on the Adafruit Circuit Playground Express. This board has a bunch of built-in hardware. At this point, I've got the buttons, switches, lights and analog input sensors (temperature and light intensity) all working. I don't have the motion sensor or audio bits going. I'll probably leave those pieces until after Snek v1.0 has been released.