Arcade button lighting on an ESP32-C3
Four arcade buttons on an ESP32-C3, each lighting its own pair of WS2812B LEDs the moment it is pressed and fading out when it is let go. The whole build, an ESP32-C3 and 5 more parts, 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 Runs in your browser. Free, and no account needed.
The code
The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.
// Button lights: each arcade button lights its own pair of WS2812B LEDs.
//
// Wiring:
// buttons 1 on GPIO 0 2 on GPIO 1 3 on GPIO 10 4 on GPIO 4
// (other leg of each to GND, internal pull-up on, pressed = LOW)
// strip DIN -> GPIO 5 VDD -> 5 V VSS -> GND
//
// Eight LEDs on one wire, two under each button. A press lights that
// button's pair at full color at once; letting go fades it out over a few
// frames, which is the "reactive" lighting many custom controllers ship with.
//
// Why an ESP32-C3 and not a Pro Micro or a Pico: a WS2812 bit is a pulse a
// few hundred nanoseconds long. Real 32U4 boards make it with hand-written
// assembly (FastLED, the Adafruit library) and the RP2040 with its PIO
// blocks. Mokxi runs WS2812 strips from the ESP32-C3 and ESP32-C6 only, so
// this circuit uses the C3. The lighting logic below is the same on any
// board; only the driver underneath changes.
//
// The button reading is the same pull-up pattern as the fight stick
// examples, and the lights never slow it down: a frame is drawn at most
// every FRAME_MS, and the buttons are read on every pass of loop().
#include <Adafruit_NeoPixel.h>
const uint8_t DIN_PIN = 5;
const uint8_t LEDS = 8;
const uint8_t BUTTONS = 4;
const uint8_t BUTTON_PINS[BUTTONS] = {0, 1, 10, 4};
// One color per button, as 0..255 red, green, blue.
const uint8_t COLORS[BUTTONS][3] = {
{255, 40, 40}, // 1: red
{40, 120, 255}, // 2: blue
{40, 255, 80}, // 3: green
{255, 200, 30}, // 4: yellow
};
// How much of the light is left after each frame once a button is let go,
// out of 256. 200 is a quick fade of a few tenths of a second.
const uint16_t FADE = 200;
const unsigned long FRAME_MS = 16;
Adafruit_NeoPixel strip(LEDS, DIN_PIN, NEO_GRB + NEO_KHZ800);
// Each button's brightness, 0..255.
uint8_t level[BUTTONS];
bool wasHeld[BUTTONS];
unsigned long lastFrame = 0;
void setup() {
Serial.begin(115200);
for (uint8_t i = 0; i < BUTTONS; i++) {
pinMode(BUTTON_PINS[i], INPUT_PULLUP);
}
strip.begin();
strip.clear();
strip.show();
Serial.println("Button lights: press the arcade buttons.");
}
void loop() {
for (uint8_t i = 0; i < BUTTONS; i++) {
bool held = digitalRead(BUTTON_PINS[i]) == LOW;
if (held) {
level[i] = 255; // on at once, on the same pass the press was seen
}
if (held != wasHeld[i]) {
wasHeld[i] = held;
Serial.print("button ");
Serial.print(i + 1);
Serial.println(held ? " down" : " up");
}
}
unsigned long now = millis();
if (now - lastFrame < FRAME_MS) {
return;
}
lastFrame = now;
for (uint8_t i = 0; i < BUTTONS; i++) {
uint8_t r = (uint8_t)((COLORS[i][0] * (uint16_t)level[i]) >> 8);
uint8_t g = (uint8_t)((COLORS[i][1] * (uint16_t)level[i]) >> 8);
uint8_t b = (uint8_t)((COLORS[i][2] * (uint16_t)level[i]) >> 8);
strip.setPixelColor(i * 2, r, g, b);
strip.setPixelColor(i * 2 + 1, r, g, b);
if (!wasHeld[i]) {
level[i] = (uint8_t)((level[i] * FADE) >> 8);
}
}
strip.show();
}
Parts list
7 parts, plus the jumper wires. Every one is in the editor's parts bin.
How it is wired
7 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; Arcade button (1) pin 1; Arcade button (2) pin 1; Arcade button (3) pin 1; Arcade button (4) pin 1
- ESP32-C3-DevKitM-1 pin 0; Arcade button (1) pin 2
- ESP32-C3-DevKitM-1 pin 1; Arcade button (2) pin 2
- ESP32-C3-DevKitM-1 pin 10; Arcade button (3) pin 2
- ESP32-C3-DevKitM-1 pin 4; Arcade button (4) pin 2
- 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.