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

Plasma panel on an ESP32-C3

Three sine waves drift across a 16x16 WS2812B matrix, 256 LEDs on one wire from an ESP32-C3. 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.

AdvancedRuns in your browser. Free, and no account needed.

Plasma panel · ESP32-C3-DevKitM-1live0.000 s 0.00x
Click to open it in the editor
The circuit itself, running here on the simulator. Press what can be pressed; click anything else to open it in the editor.

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
// Plasma: a 16x16 WS2812B panel, 256 LEDs on one wire.
//
// Wiring, panel to board:
//   DIN -> GPIO 5      VDD -> 5 V      VSS -> GND
//
// The panel is serpentine: the first row runs left to right, the second right
// to left, and so on, which is how almost all of them are built and why the
// part has a `serpentine` property. `setPanel` does that arithmetic, so the
// sketch thinks in x and y.
//
// A plasma is three sine waves added together: one across, one down, and one
// along the diagonal, each drifting at its own rate. The sum picks a hue. There
// is no floating point in this runtime and none is wanted: a 256-entry table
// of one cycle, indexed by an 8-bit angle that wraps by itself, is both faster
// and exactly periodic.
//
// # What a frame costs, and why it is ten a second
//
// 256 LEDs is 6144 bits, and each one is a pulse the driver times with a
// counted loop, so a frame takes about 7.7 ms of the board's own time. The
// plasma arithmetic is a few thousand instructions and disappears next to it.
// Interrupts are held off for the duration: a stretched pulse is not this
// protocol any more, and the panel would drop the whole frame rather than show
// half of it.
//
// The frame rate is set by the simulator rather than by the chip. Those 6144
// bits are 12 288 edges on the wire, and the Mokxi kernel costs about two
// microseconds to carry one of them through the WS2812B's decoder, so a frame
// the board would finish in 7.7 ms takes the simulator some 30 ms of real time.
// A real ESP32-C3 would run this panel at fifty frames a second without
// noticing. Ten is what the browser can carry while the clock on the toolbar
// still reads close to 1.00x, and even then it dips: a 16x16 panel is the most
// expensive thing in the parts bin to simulate, edge for edge.

#include "mokxi_ws2812.h"

const uint8_t DIN_PIN = 5;
const uint8_t SIDE = 16;
const uint16_t LEDS = (uint16_t)SIDE * SIDE;

uint8_t pixels[LEDS * 3];
Ws2812 panel(DIN_PIN, pixels, LEDS);

/** One cycle of a sine, 0..255 in and 0..255 out. The angle wraps by itself. */
static const uint8_t SIN8[256] = {
    128, 131, 134, 137, 140, 143, 146, 149, 152, 155, 158, 162,
    165, 167, 170, 173, 176, 179, 182, 185, 188, 190, 193, 196,
    198, 201, 203, 206, 208, 211, 213, 215, 218, 220, 222, 224,
    226, 228, 230, 232, 234, 235, 237, 238, 240, 241, 243, 244,
    245, 246, 248, 249, 250, 250, 251, 252, 253, 253, 254, 254,
    254, 255, 255, 255, 255, 255, 255, 255, 254, 254, 254, 253,
    253, 252, 251, 250, 250, 249, 248, 246, 245, 244, 243, 241,
    240, 238, 237, 235, 234, 232, 230, 228, 226, 224, 222, 220,
    218, 215, 213, 211, 208, 206, 203, 201, 198, 196, 193, 190,
    188, 185, 182, 179, 176, 173, 170, 167, 165, 162, 158, 155,
    152, 149, 146, 143, 140, 137, 134, 131, 128, 124, 121, 118,
    115, 112, 109, 106, 103, 100,  97,  93,  90,  88,  85,  82,
     79,  76,  73,  70,  67,  65,  62,  59,  57,  54,  52,  49,
     47,  44,  42,  40,  37,  35,  33,  31,  29,  27,  25,  23,
     21,  20,  18,  17,  15,  14,  12,  11,  10,   9,   7,   6,
      5,   5,   4,   3,   2,   2,   1,   1,   1,   0,   0,   0,
      0,   0,   0,   0,   1,   1,   1,   2,   2,   3,   4,   5,
      5,   6,   7,   9,  10,  11,  12,  14,  15,  17,  18,  20,
     21,  23,  25,  27,  29,  31,  33,  35,  37,  40,  42,  44,
     47,  49,  52,  54,  57,  59,  62,  65,  67,  70,  73,  76,
     79,  82,  85,  88,  90,  93,  97, 100, 103, 106, 109, 112,
    115, 118, 121, 124,
};

static inline uint8_t sin8(uint8_t angle) { return SIN8[angle]; }

uint16_t t = 0;

// Bright enough to read on the canvas, dim enough that a real panel would not
// need its own supply.
const uint8_t LEVEL = 96;

const int FRAME_MS = 100;

void setup() {
  Serial.begin(115200);
  Serial.println("Mokxi ESP32-C3: plasma on a 16x16 WS2812B panel");
  panel.begin();
  panel.clear();
  panel.show();
}

void loop() {
  for (uint8_t y = 0; y < SIDE; y++) {
    for (uint8_t x = 0; x < SIDE; x++) {
      // Three waves: across, down, and along the diagonal, each drifting.
      uint16_t v = (uint16_t)sin8((uint8_t)(x * 14u + t));
      v = (uint16_t)(v + sin8((uint8_t)(y * 12u - t * 2u)));
      v = (uint16_t)(v + sin8((uint8_t)((x + y) * 9u + t / 2u)));
      // 0..765 into the 1536 steps of the wheel, twice round the cube.
      uint16_t hue = (uint16_t)((v * 2u) % 1536u);

      uint8_t r;
      uint8_t g;
      uint8_t b;
      ws2812_wheel(hue, &r, &g, &b);
      panel.setPanel(x, y, SIDE, true, ws2812_scale(r, LEVEL), ws2812_scale(g, LEVEL),
                     ws2812_scale(b, LEVEL));
    }
  }
  panel.show();

  t = (uint16_t)(t + 3u);
  delay(FRAME_MS);
}

Parts list

3 parts, plus the jumper wires. Every one is in the editor's parts bin.

How it is wired

3 connections, pin by pin, read from the circuit itself. Each line is one set of pins joined together, by a jumper wire or a breadboard strip.

  • Ground: ESP32-C3-DevKitM-1 pin GND; Addressable RGB LEDs pin VSS
  • ESP32-C3-DevKitM-1 pin 5; Addressable RGB LEDs pin DIN
  • ESP32-C3-DevKitM-1 pin 5V; Addressable RGB LEDs pin VDD

Change it and keep it

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