WS2812 driver implementations can be found in every nook and cranny of the internet. If a million of something is good, a million and one is better. However, all Neopixel implementations are not created equal. As with any driver, maximum leverage of hardware capabilities is paramount, and support for the Neopixel is no exception. Bit-banging drivers are great, as long as you don’t need your processor for anything else, but we can do better. Serial peripheral controllers are the right choice to generate the bits required to control each Neopixel in your long string of colored light emissions and SPI is an obvious choice. Often, though, SPI masters have a limitation on the maximum number of bytes that can be sent for each transaction and this can either limit the number of Neopixels you can control, or require multiple chained SPI DMA transactions for an efficient driver implementation.
Most SoCs support digital audio ouput via standard interfaces such as I2S. Although it may not be intuitively obvious to the casual observer, I2S is an ideal choice of peripheral controller for efficient generation of bitstreams to control large (1000+) strings of Neopixels via microcontrollers with limited processing resources. I2S is designed to send continuous streams of digitized audio data to a CODEC, generating sound. High-fidelity audio utilizes sample rates on the order of 384k samples (bits) per second, however I2S peripherals like the one found on the ESP32 family of microcontrollers support much higher sample rates, enabling the use of the I2S controller to generate a bitstream that can be used to control Neopixels.

Referring to the timing diagram above, the digital waveform required to send one bit to a WS2812 is comprised of a “high” transmission for a specific duration followed by a “low” transmission of a different duration. A sample rate of 2.6 Mbps (385 nanoseconds per bit) hits the sweet-spot for the WS2812, allowing generation of both the long and short pulses required to represent the WS2812 “0 code” and “1 code”. Each data bit sent to the WS2812 is generated from 3 I2S bits, 0b100 for a “0 code” and 0b110″ for a “1 code”.
In order to perform a pixel update, each WS2812 pixel requires 3 bytes of data, one byte per color: (Green [7 downto 0], Red [7 downto 0], Blue [7 downto 0]). Since our I2S waveform requires 3 bits per WS2812 bit, the resulting waveform requires nine I2S bytes to control each Neopixel plus 50 bytes of “all zeros” at the end of the transmission to activate the new Neopixel settings via the WS2812 “RET code”. For a string of 256 pixels, this equates to 2354 bytes per transfer and at 2.6 Mbps, each update requires approximately 7.3ms. With a configuration of 256 LEDs, a refresh rate of over 130 “frames” (update of every Neopixel) per second is supported with this design. With 512 LEDs, a refresh rate of nearly 70 “frames” per second is achieved.
Let’s see this in action with a 8×32 WS2812b matrix controlled by an ESP32 using a driver (see ESP Component Registry or Github) implementing the design described above.

I wonder what Neopixel bitstreams would sound like if an audio CODEC were connected to the output of the I2S peripheral controller? Sounds like a project for tomorrow.
Leave a Reply