Learn

A WS2812B NeoPixel rainbow on an ESP32

  • 192parts on the bench
  • 25boards running now
  • 1.00xreal time, on every board
ESP32-C3: WS2812B rainbowlive0.000 s 0.00x
Click to open it in the editor
The rainbow example: sixteen WS2812Bs on GPIO 5, bit-banged to the datasheet’s pulse widths.

WS2812B LEDs, sold by Adafruit as NeoPixels, put a red, green and blue LED and a tiny controller in every pixel, and chain them all on one data wire. The circuit above is sixteen of them on GPIO 5 of an ESP32-C3, with a hue wheel sliding along the strip. It is running now, on a simulated ESP32-C3 executing the compiled firmware, and the strip is decoding the actual pulses on its data pin.

Open it in the editor to change the number of LEDs, the speed or the brightness, and run it again.

What you need

  • An ESP32-C3 board (the DevKitM-1 here)
  • A WS2812B strip, ring or matrix
  • A breadboard and jumper wires

Wiring

Part and pin
Goes to
Note
Strip DIN
GPIO 5
The data line into the first LED
Strip VDD
5 V
On a long real strip, a separate 5 V supply
Strip VSS
GND
Shared with the board

One wire, and no clock

There is no clock line. Each bit is a high pulse whose length is the bit: about 0.4 microseconds for a nought and 0.8 for a one. Each LED takes the first 24 bits it receives, green first, then red, then blue, and passes the rest along to the next. When the line has been quiet for 50 microseconds, every LED takes up its new color at once.

The simulated strip decodes those pulses off the pin’s own edges, with the datasheet’s timing windows. A pulse that is too short or too long is rejected, the way a real strip stops following a stream it cannot read. That is why a driver that works here has its timing right.

The Adafruit_NeoPixel code

The Adafruit NeoPixel tutorial sketch compiles as it stands on the ESP32-C3 and ESP32-C6. The header is Mokxi’s own, on its WS2812 driver, with setPixelColor(), Color(), ColorHSV(), fill(), clear(), setBrightness(), gamma32() and rainbow(). The built-in rainbow example uses the lower-level driver directly, with its own integer color wheel of 1536 steps round the edges of the RGB cube.

A first NeoPixel sketch
#include <Adafruit_NeoPixel.h>

Adafruit_NeoPixel pixels(8, 4, NEO_GRB + NEO_KHZ800);

void setup() {
  pixels.begin();
}

void loop() {
  pixels.clear();
  pixels.setPixelColor(0, pixels.Color(0, 150, 0));
  pixels.show();
  delay(500);
}

Why this tutorial uses an ESP32

On a real Arduino Uno, the Adafruit library drives WS2812s with hand-tuned assembly that counts every one of the 16 MHz clock’s cycles. In Mokxi, the WS2812 driver is a counted loop that needs the ESP32-C3’s 160 MHz to hit the timing, so the NeoPixel header is available on the ESP32-C3 and ESP32-C6 and stops with a message on other boards. The ESP32 is the more common choice for LED projects today anyway, with more memory for large panels.

Try it in the editor

Open the circuit in the editor and change LEVEL from 40 to 255. The strip gets much brighter. On a real bench each pixel can draw up to sixty milliamps at full white, and the supply has to be sized for that, which is why the example runs at a sixth of full scale.

Then select the strip and change its layout property to ring. The same sixteen LEDs are drawn as a circle, with LED 0 at the top and the rest clockwise, which is how the rings you can buy are numbered. The sketch does not change at all, because electrically a ring is a strip.

Finally, change SPEED to 1 for a slow drift of color, or change SPREAD so that the whole strip shows a single color at a time.

Common mistakes

Full brightness on every LED. Each pixel can draw about 60 milliamps at full white, so a 60 LED strip wants over 3 amps. The example runs at about a sixth of full scale. The simulator does not model supply current, so plan the power for a real build yourself.

The wrong color order. WS2812B strips expect green first. A sketch that sends red first, with NEO_RGB, gets red and green swapped, here and on the bench.

No common ground between the board and a separately powered strip. The data line is measured against the strip’s ground.

A 3.3 volt data line into a 5 volt strip. Many real strips accept it, but not all. A level shifter, or a first pixel run a little lower, fixes it. The simulated strip reads 3.3 volt data without complaint.

Forgetting show(). Setting a pixel changes the buffer in memory; nothing reaches the strip until show() sends it.

Questions

Can I use a NeoPixel ring or matrix instead of a strip?

Yes. Electrically they are all a strip. The simulated part has a layout property for strip, ring and matrix, including serpentine matrices, and the numbering follows the real products.

How many NeoPixels can one pin drive?

As many as you have memory and time for: three bytes each, and about 30 microseconds per LED to send. The simulated strip holds up to 1024 LEDs per part.

Does FastLED work?

FastLED is not one of the headers the browser compiler carries. Adafruit_NeoPixel.h is, on the ESP32-C3 and ESP32-C6.

Build this for real

Open the editor, change a value and watch the number move with it. Nothing to install, and no account needed.