Built-in project · ESP32-C3-DevKitM-1

Rainbow strip on an ESP32-C3

An ESP32-C3 runs a hue wheel along sixteen WS2812Bs, bit-banged to the datasheet pulse widths. The whole build, an ESP32-C3 and 1 more part, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.

Teaching? Assign it to your class in one click.

Intermediate About 20 minutes to build and run. Runs in your browser. Free, and no account needed.

The parts retail for about $16. On Mokxi they're free.

Step by step, with the wiring and the common mistakes: ESP32 NeoPixel (WS2812B) Rainbow Tutorial

The circuit runs here once the page has loaded.

What you'll learn

  • How a one-wire LED strip is driven with timed pulses
  • A color wheel built from integers, with no floating point
  • Offsetting each LED's hue to make a moving rainbow
  • Limiting brightness to save power and eyes
  • Difficulty: Intermediate
  • Time: About 20 minutes
  • Board: ESP32-C3-DevKitM-1
  • Parts: 3

How it works

The strip has sixteen WS2812B LEDs on one data wire, DIN, connected to GPIO 5. There is no clock. Each bit is a high pulse, 0.4 microseconds for a 0 and 0.8 for a 1, with 24 bits per LED. About fifty microseconds of quiet at the end tells every LED to show what it was sent.

The colors come from a wheel of 1536 steps around the RGB cube. Each LED is a fixed distance further along the wheel than its neighbor, which makes a rainbow. Every frame, the whole pattern slides a little further, so it appears to move.

Brightness is held to a sixth of full scale so the strip is not blinding.

Parts list

3 parts, plus the jumper wires. They retail for about $16; on Mokxi they're free.

Wiring table

Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.

Part and pinConnects to
Addressable RGB LEDs pin VSSESP32-C3-DevKitM-1 pin GND
Addressable RGB LEDs pin DINESP32-C3-DevKitM-1 pin 5
Addressable RGB LEDs pin VDDESP32-C3-DevKitM-1 pin 5V

The code

The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.

sketch.ino · ESP32-C3-DevKitM-1
// Rainbow: a hue wheel running along a WS2812B strip.
//
// Wiring, strip to board:
//   DIN -> GPIO 5      VDD -> 5 V      VSS -> GND
//
// One wire carries the whole strip. There is no clock: a bit is a high pulse
// whose length is the bit, 0.4 us for a 0 and 0.8 us for a 1, twenty-four bits
// an LED, green first. Fifty microseconds of quiet at the end is the latch, and
// that is when every LED takes up what it was sent.
//
// The driver in mokxi_ws2812.h makes those pulses with a counted loop rather
// than a timer, because SYSTIMER ticks at 16 MHz, 62.5 ns a tick, and reading
// it takes longer than the pulse being measured. At 160 MHz with one
// instruction a cycle, a two-instruction loop is a 12.5 ns clock, which is
// plenty. See the header for the numbers that come out on the wire.
//
// There is no floating point in this runtime, so the colour wheel is integers:
// 1536 steps round the six edges of the RGB cube, one channel at full, one at
// nothing and one sliding between them.

#include "mokxi_ws2812.h"

const uint8_t DIN_PIN = 5;
const uint16_t LEDS = 16;

// Three bytes an LED, green first: the order the wire wants them in.
uint8_t pixels[LEDS * 3];
Ws2812 strip(DIN_PIN, pixels, LEDS);

// Where the wheel has got to, and how far apart two neighbours are on it.
uint16_t phase = 0;
const uint16_t SPREAD = 1536 / LEDS;
const uint16_t SPEED = 12;

// Full scale on all sixteen would be a floodlight. This is a sixth of it.
const uint8_t LEVEL = 40;

const int FRAME_MS = 20;

void setup() {
  Serial.begin(115200);
  Serial.println("Mokxi ESP32-C3: a rainbow on sixteen WS2812Bs");
  strip.begin();
  strip.clear();
  strip.show();
}

void loop() {
  for (uint16_t i = 0; i < LEDS; i++) {
    uint8_t r;
    uint8_t g;
    uint8_t b;
    ws2812_wheel((uint16_t)(phase + i * SPREAD), &r, &g, &b);
    strip.setPixel(i, ws2812_scale(r, LEVEL), ws2812_scale(g, LEVEL),
                   ws2812_scale(b, LEVEL));
  }
  strip.show();

  phase = (uint16_t)((phase + SPEED) % 1536u);
  delay(FRAME_MS);
}

Code walkthrough

The key functions of the sketch, and what each one does.

setup sketch.ino

Opens serial at 115200 baud, then calls strip.begin(), clears the pixel buffer and calls strip.show() so the strip starts dark.

void setup() {
  Serial.begin(115200);
  Serial.println("Mokxi ESP32-C3: a rainbow on sixteen WS2812Bs");
  strip.begin();
  strip.clear();
  strip.show();
}

loop sketch.ino

For each of the 16 LEDs, it finds a color on the wheel at phase plus that LED's offset, scales it down with LEVEL, and stores it. Then strip.show() sends the whole frame. phase moves on by SPEED and the loop waits FRAME_MS.

void loop() {
  for (uint16_t i = 0; i < LEDS; i++) {
    uint8_t r;
    uint8_t g;
    uint8_t b;
    ws2812_wheel((uint16_t)(phase + i * SPREAD), &r, &g, &b);
    strip.setPixel(i, ws2812_scale(r, LEVEL), ws2812_scale(g, LEVEL),
                   ws2812_scale(b, LEVEL));
  }
  strip.show();

  phase = (uint16_t)((phase + SPEED) % 1536u);
  delay(FRAME_MS);
}

Troubleshooting

The usual mistakes with this circuit, and the fix for each.

  • The strip stays dark This project
    Check DIN goes to GPIO 5, VDD to 5 V and VSS to ground.
  • The rainbow is too dim or too bright This project
    Change LEVEL. It is 40 out of 255 here.
  • The rainbow moves too fast or too slow This project
    Change SPEED or FRAME_MS.
  • The first LED flickers, shows wrong colors, or the whole strip glitches. Addressable RGB LEDs
    Join the grounds of the strip and the controller. Add a 300 to 500 ohm resistor in series with the data line. Put a 1000 uF capacitor across the strip's 5 V and GND.
  • The strip works at low brightness but dims, turns yellow, or resets when many LEDs go white. Addressable RGB LEDs
    Each LED can draw about 60 mA at full white. Use a 5 V supply sized for the count, feed power in at several points, and lower the brightness in code.
  • Upload fails or the board is not found by the computer. ESP32-C3-DevKitM-1
    Pick the ESP32-C3 board in your tools and the right port. If it still fails, hold BOOT, tap RESET, then release BOOT to enter download mode and try again.
  • GPIO pins act strangely or the board resets. ESP32-C3-DevKitM-1
    GPIO is 3.3 V only, so never feed it 5 V. GPIO 18 and 19 are USB, and GPIO 2, 8 and 9 are boot strapping pins. Avoid loading them at reset.

Change it and keep it

Open it in the editor, change the circuit or the code, and keep your version in a free account.