This project builds a small electronic six-sided die: press a button and get a random roll, shown instantly as a familiar dice-face pattern of lit LEDs. It's a compact project, but it packs in two genuinely useful beginner concepts at once — generating randomness that actually behaves randomly, and driving multiple LEDs in patterns rather than one at a time.
What the Controller Does
The ESP32-C3 waits for a button press, and when one is detected, generates a new random number from 1 to 6 and lights the LED combination that matches that number's dice-face pattern. All of the decision-making — noticing the press, generating the number, and choosing which LEDs to turn on — happens in the controller's code.
What the LEDs Do
Seven LEDs arranged in the classic dice-face layout can display any number from 1 to 6 by lighting different combinations, the same way a real die shows different arrangements of pips for each face. Each LED needs its own current-limiting resistor, taken from the resistor kit, wired in series between the controller pin and the LED — without it, the LED would try to draw more current than the pin can safely supply, which shortens the LED's life or damages the pin over time.
What the Push Button Does
The push button triggers a new "roll." Pressing it tells the controller to generate a new random number and light the matching LED pattern, replacing whatever was previously lit.
Debouncing the Push Button
Mechanical buttons don't switch cleanly — the metal contacts physically bounce for a few milliseconds when pressed, which a fast controller can register as several rapid presses instead of one. Left unhandled, this means a single press can trigger multiple rolls in a row, and the LEDs may flicker through several patterns before settling. The standard fix, called debouncing, is to ignore any additional press signals for a short window (typically 10-50 milliseconds) after the first one is detected, either in code or with a small capacitor in the circuit. This is a small detail, but it's one of the first real-world timing problems most people run into with physical buttons.
Software and Library Concepts
A genuinely useful, real beginner technique here is seeding the random number generator by reading an unconnected (floating) analog pin at startup. A floating pin picks up tiny amounts of ambient electrical noise, so reading it gives a different starting value nearly every time the board powers on. Skipping this step is a very common and real beginner bug: without a randomized seed, most random number generators produce the exact same sequence every time the board resets, so the "random" roll looks identical after every power-up. Beyond the seeding, the rest of the logic is a lookup table mapping each number 1-6 to the specific set of LED pins that should be lit for that face.
Testing and Troubleshooting
LEDs never light in a valid dice pattern — check the mapping table in code between each number and which LED pins should be on.
The "random" roll shows the same number every time the board resets — the random seed isn't actually being randomized; revisit how the seed is generated, ideally from a floating analog pin read at startup.
Some LEDs never light at all even when the pattern should include them — check that specific LED's polarity and resistor wiring individually; a reversed LED simply won't light.
A single button press triggers several rapid rolls — this is switch bounce; add or lengthen a debounce delay after the first press is detected.
Optional Future Upgrade: Add a Roll Sound Effect
A small piezo buzzer, driven from a spare controller pin at the same moment the LEDs update, can play a short beep or click on every roll. It's a small addition that makes the die feel far more like a finished object, and it introduces the idea of triggering more than one output device from a single event without adding much complexity to the existing code.
Wiring
LED "TL" (top-left pip) anode → 220–330Ω resistor → ESP32-C3 GPIO0 — one leg of the 6-LED dice array; cathode to GND
Pin numbers above (GPIO0–GPIO6) are general-purpose and safe on the ESP32-C3 SuperMini, but double-check against your specific board's silkscreen before wiring — adjust the `#define`s if your labeling differs. A true 6-sided die needs 7 pip positions (with a true center), but with only 6 LEDs the code treats the middle-left LED as the shared "center" pip for 1/3/5 and only lights the middle-right LED for 6 — a common and honest simplification for 6-LED electronic dice builds. Build and test one LED at a time on the breadboard before wiring all six, since a reversed LED is the most common bring-up failure.
Wiring Diagram
Starter Code
// Electronic Dice — ESP32-C3 SuperMini
// 6 LEDs arranged in a simplified dice pip pattern, rolled with a button.
// --- Pin assignments — adjust to match your board's silkscreen if different ---
#define LED_TL 0 // top-left pip
#define LED_TR 1 // top-right pip
#define LED_ML 2 // middle-left pip (also used as the "center" pip for 1/3/5)
#define LED_MR 3 // middle-right pip (only lit for 6)
#define LED_BL 4 // bottom-left pip
#define LED_BR 5 // bottom-right pip
#define BUTTON_PIN 6
const uint8_t ledPins[6] = { LED_TL, LED_TR, LED_ML, LED_MR, LED_BL, LED_BR };
// Which of the 6 LEDs (in ledPins order) are lit for each face 1-6
const bool dicePatterns[6][6] = {
{ false, false, true, false, false, false }, // 1: center only
{ true, false, false, false, false, true }, // 2: diagonal
{ true, false, true, false, false, true }, // 3: diagonal + center
{ true, true, false, false, true, true }, // 4: four corners
{ true, true, true, false, true, true }, // 5: four corners + center
{ true, true, true, true, true, true } // 6: all six
};
void showFace(uint8_t face) {
// face is 1-6
for (uint8_t i = 0; i < 6; i++) {
digitalWrite(ledPins[i], dicePatterns[face - 1][i] ? HIGH : LOW);
}
}
void allOff() {
for (uint8_t i = 0; i < 6; i++) digitalWrite(ledPins[i], LOW);
}
void rollAnimation() {
// Quick flicker through random faces before settling, like a physical roll
for (int i = 0; i < 15; i++) {
showFace(random(1, 7));
delay(40 + i * 4); // gradually slow down
}
}
void setup() {
Serial.begin(115200);
for (uint8_t i = 0; i < 6; i++) {
pinMode(ledPins[i], OUTPUT);
}
pinMode(BUTTON_PIN, INPUT_PULLUP);
randomSeed(esp_random()); // ESP32 hardware RNG, seeds Arduino's random()
allOff();
}
void loop() {
static bool lastPressed = false;
bool pressed = (digitalRead(BUTTON_PIN) == LOW);
if (pressed && !lastPressed) {
rollAnimation();
uint8_t result = random(1, 7);
showFace(result);
Serial.print("Rolled: ");
Serial.println(result);
delay(200); // simple debounce after handling the press
}
lastPressed = pressed;
}