ESP32 Bench
Boards: ESP32, S2, S3, C3, C6
Flash and monitor: Web Serial
← All projects

NeoPixel fire and lava lamp

A flickering fire on a strip of NeoPixels, and a slow lava-lamp glow. The BOOT button switches between them.

Starter15 min115200 baud
Board
Fig. 1. Pins this project uses on the ESP32-DevKitC V4, 38 pin. USB connector at the bottom.

Wiring

FromTo boardNote
Strip 5V or VDC5VFine for a short strip at this brightness. Longer strips need their own 5 V supply
Strip GNDGNDJoin the grounds if the strip has its own supply
Strip DIN, through 330 ΩGPIO 16The resistor goes next to the strip
BOOT buttonGPIO 0Already on the board. Press it to switch effects

Tested on this board

Not run on this board yet. Treat the wiring and code as a first draft.

Firmware, Arduino sketch for ESP32 Dev Module

// Fire and lava lamp on a NeoPixel strip. BOOT switches the effect.
#include <Adafruit_NeoPixel.h>

const int PIN = 16;
const int COUNT = 30;       // LEDs on your strip
const int BUTTON = 0;   // the BOOT button
const int COOLING = 55;     // fire: higher makes shorter flames
const int SPARKING = 120;   // fire: higher makes a busier fire

Adafruit_NeoPixel strip(COUNT, PIN, NEO_GRB + NEO_KHZ800);
uint8_t heat[COUNT];
bool lava = false;
int lastButton = HIGH;
unsigned long frames = 0, reportAt = 0;

// Black, red, orange, yellow, white as heat rises.
uint32_t heatColor(uint8_t t) {
  uint8_t scaled = (t * 191) / 255;  // 0 to 191, three bands of 64
  uint8_t ramp = (scaled & 63) << 2;
  if (scaled > 127) return strip.Color(255, 255, ramp);
  if (scaled > 63) return strip.Color(255, ramp, 0);
  return strip.Color(ramp, 0, 0);
}

void fire() {
  // The classic Fire2012 idea: each cell cools, heat drifts up, new sparks at the bottom.
  for (int i = 0; i < COUNT; i++) {
    int cool = random(0, ((COOLING * 10) / COUNT) + 2);
    heat[i] = heat[i] > cool ? heat[i] - cool : 0;
  }
  for (int k = COUNT - 1; k >= 2; k--) heat[k] = (heat[k - 1] + 2 * heat[k - 2]) / 3;
  if (random(255) < SPARKING) {
    int y = random(min(7, COUNT));
    heat[y] = min(255, heat[y] + (int)random(160, 255));
  }
  for (int j = 0; j < COUNT; j++) strip.setPixelColor(j, heatColor(heat[j]));
}

void lavaLamp() {
  // Two slow waves of warm colour drifting past each other.
  float t = millis() / 1000.0f;
  for (int i = 0; i < COUNT; i++) {
    float a = (sinf(i * 0.35f + t * 0.6f) + 1) / 2;
    float b = (sinf(i * 0.17f - t * 0.4f + 2) + 1) / 2;
    uint16_t hue = 60000 + (uint16_t)(a * 9000);  // magenta through red to orange
    uint8_t value = 60 + (uint8_t)(b * 195);
    strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(hue, 255, value)));
  }
}

void setup() {
  Serial.begin(115200);
  pinMode(BUTTON, INPUT_PULLUP);
  strip.begin();
  strip.setBrightness(60);  // about a quarter: easy on USB power
  strip.show();
  randomSeed(esp_random());
  Serial.printf("%d LEDs on GPIO %d. Press BOOT to switch effects.\n", COUNT, PIN);
  Serial.println("mode: fire");
}

void loop() {
  int button = digitalRead(BUTTON);
  if (lastButton == HIGH && button == LOW) {
    lava = !lava;
    Serial.println(lava ? "mode: lava lamp" : "mode: fire");
    delay(30);
  }
  lastButton = button;

  if (lava) lavaLamp();
  else fire();
  strip.show();
  frames++;

  if (millis() - reportAt >= 5000) {
    Serial.printf("fps=%lu\n", frames * 1000 / (millis() - reportAt));
    frames = 0;
    reportAt = millis();
  }
  delay(lava ? 30 : 16);
}

Parts

QuantityPartWhere to buy
1×ESP32 dev boardBuy at Amazon
1×WS2812B strip, 30 LEDs, or a NeoPixel StickBuy at Adafruit
1×330 Ω resistor
1×Optional 74AHCT125 level shifterBuy at Adafruit
3×Jumper wires

Amazon buy links may include an Associates tag, and this site may earn a commission from qualifying Amazon purchases at no extra cost to you. Adafruit links are direct.

Libraries

Adafruit NeoPixel

Load it

  1. In the Arduino IDE, install the esp32 board package and pick the board named above the sketch.
  2. Install the libraries listed above from the Library Manager.
  3. Paste the sketch, choose the board's port and upload. Hold BOOT if the upload stalls at Connecting.
  4. Close the IDE's serial monitor, then open the bench here to watch the output.

Field notes

  • Each LED can draw about 60 mA at full white. Thirty LEDs at this brightness are fine on USB; at full brightness use a separate 5 V supply and a 1000 µF capacitor across it.
  • WS2812B LEDs on 5 V expect data near 5 V. Most accept 3.3 V; if the first LED flickers or stays dark, put a 74AHCT125 level shifter in the data line.
  • Hang the strip vertically for the fire: the flames rise from LED 0. A paper tube or a frosted jar over it makes it look far better.
  • Tune COOLING and SPARKING to change the fire. The fps lines on the bench show how fast the strip is updating.

Not tested yet

  • Not tested on hardware yet. Compiles for every board listed with Arduino-ESP32 core 3.3.12.

Built it? Send a build report with your board and what the serial port printed. Working reports become the "Tested on this board" entries above.