Electronic dice
Press a button to roll two dice on an OLED, with a tumbling animation. The serial port keeps score, so you can check how fair the dice are.
Wiring
| From | To board | Note |
|---|---|---|
| OLED VCC | 3V3 | |
| OLED GND | GND | |
| OLED SDA | GPIO 21 | |
| OLED SCL | GPIO 22 | |
| Button | GPIO 4 | Other side to GND. The internal pull-up does the rest |
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
// Electronic dice on an SSD1306. Press the button to roll.
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
const int BUTTON = 4;
const int DICE = 2; // 1 or 2
Adafruit_SSD1306 display(128, 64, &Wire, -1);
int counts[13]; // how often each total came up
int rolls = 0;
void drawDie(int x, int y, int value) {
const int S = 44, R = 4;
display.drawRoundRect(x, y, S, S, 6, SSD1306_WHITE);
int a = x + 11, b = x + S / 2, c = x + S - 11; // pip columns
int d = y + 11, e = y + S / 2, f = y + S - 11; // pip rows
if (value % 2 == 1) display.fillCircle(b, e, R, SSD1306_WHITE);
if (value >= 2) {
display.fillCircle(a, d, R, SSD1306_WHITE);
display.fillCircle(c, f, R, SSD1306_WHITE);
}
if (value >= 4) {
display.fillCircle(c, d, R, SSD1306_WHITE);
display.fillCircle(a, f, R, SSD1306_WHITE);
}
if (value == 6) {
display.fillCircle(a, e, R, SSD1306_WHITE);
display.fillCircle(c, e, R, SSD1306_WHITE);
}
}
void showDice(int one, int two) {
display.clearDisplay();
if (DICE == 1) {
drawDie(42, 10, one);
} else {
drawDie(12, 10, one);
drawDie(72, 10, two);
}
display.display();
}
void setup() {
Serial.begin(115200);
pinMode(BUTTON, INPUT_PULLUP);
Wire.begin(21, 22);
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println("SSD1306 not found at 0x3C");
for (;;) delay(1000);
}
showDice(6, 6);
Serial.println("Dice ready. Press the button to roll.");
}
void loop() {
if (digitalRead(BUTTON) == HIGH) return;
// Tumble: quick random faces that slow down, like a die settling.
int one = 1, two = 1;
for (int wait = 30; wait < 220; wait += 20) {
one = 1 + esp_random() % 6;
two = 1 + esp_random() % 6;
showDice(one, two);
delay(wait);
}
int total = DICE == 1 ? one : one + two;
counts[total]++;
rolls++;
if (DICE == 1) Serial.printf("die1=%d\n", one);
else Serial.printf("die1=%d die2=%d sum=%d\n", one, two, total);
if (rolls % 12 == 0) {
Serial.printf("after %d rolls:", rolls);
for (int t = DICE; t <= DICE * 6; t++) Serial.printf(" %d:%d", t, counts[t]);
Serial.println();
}
while (digitalRead(BUTTON) == LOW) delay(10);
delay(50);
}Wiring
| From | To board | Note |
|---|---|---|
| OLED VCC | 3V3 | |
| OLED GND | GND | |
| OLED SDA | GPIO 8 | |
| OLED SCL | GPIO 9 | |
| Button | GPIO 4 | Other side to GND. The internal pull-up does the rest |
Tested on this board
Not run on this board yet. Treat the wiring and code as a first draft.
Firmware, Arduino sketch for ESP32S3 Dev Module
// Electronic dice on an SSD1306. Press the button to roll.
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
const int BUTTON = 4;
const int DICE = 2; // 1 or 2
Adafruit_SSD1306 display(128, 64, &Wire, -1);
int counts[13]; // how often each total came up
int rolls = 0;
void drawDie(int x, int y, int value) {
const int S = 44, R = 4;
display.drawRoundRect(x, y, S, S, 6, SSD1306_WHITE);
int a = x + 11, b = x + S / 2, c = x + S - 11; // pip columns
int d = y + 11, e = y + S / 2, f = y + S - 11; // pip rows
if (value % 2 == 1) display.fillCircle(b, e, R, SSD1306_WHITE);
if (value >= 2) {
display.fillCircle(a, d, R, SSD1306_WHITE);
display.fillCircle(c, f, R, SSD1306_WHITE);
}
if (value >= 4) {
display.fillCircle(c, d, R, SSD1306_WHITE);
display.fillCircle(a, f, R, SSD1306_WHITE);
}
if (value == 6) {
display.fillCircle(a, e, R, SSD1306_WHITE);
display.fillCircle(c, e, R, SSD1306_WHITE);
}
}
void showDice(int one, int two) {
display.clearDisplay();
if (DICE == 1) {
drawDie(42, 10, one);
} else {
drawDie(12, 10, one);
drawDie(72, 10, two);
}
display.display();
}
void setup() {
Serial.begin(115200);
pinMode(BUTTON, INPUT_PULLUP);
Wire.begin(8, 9);
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println("SSD1306 not found at 0x3C");
for (;;) delay(1000);
}
showDice(6, 6);
Serial.println("Dice ready. Press the button to roll.");
}
void loop() {
if (digitalRead(BUTTON) == HIGH) return;
// Tumble: quick random faces that slow down, like a die settling.
int one = 1, two = 1;
for (int wait = 30; wait < 220; wait += 20) {
one = 1 + esp_random() % 6;
two = 1 + esp_random() % 6;
showDice(one, two);
delay(wait);
}
int total = DICE == 1 ? one : one + two;
counts[total]++;
rolls++;
if (DICE == 1) Serial.printf("die1=%d\n", one);
else Serial.printf("die1=%d die2=%d sum=%d\n", one, two, total);
if (rolls % 12 == 0) {
Serial.printf("after %d rolls:", rolls);
for (int t = DICE; t <= DICE * 6; t++) Serial.printf(" %d:%d", t, counts[t]);
Serial.println();
}
while (digitalRead(BUTTON) == LOW) delay(10);
delay(50);
}Wiring
| From | To board | Note |
|---|---|---|
| OLED VCC | 3V3 | |
| OLED GND | GND | |
| OLED SDA | GPIO 6 | |
| OLED SCL | GPIO 7 | |
| Button | GPIO 4 | Other side to GND. The internal pull-up does the rest |
Tested on this board
Not run on this board yet. Treat the wiring and code as a first draft.
Firmware, Arduino sketch for ESP32C3 Dev Module
// Electronic dice on an SSD1306. Press the button to roll.
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
const int BUTTON = 4;
const int DICE = 2; // 1 or 2
Adafruit_SSD1306 display(128, 64, &Wire, -1);
int counts[13]; // how often each total came up
int rolls = 0;
void drawDie(int x, int y, int value) {
const int S = 44, R = 4;
display.drawRoundRect(x, y, S, S, 6, SSD1306_WHITE);
int a = x + 11, b = x + S / 2, c = x + S - 11; // pip columns
int d = y + 11, e = y + S / 2, f = y + S - 11; // pip rows
if (value % 2 == 1) display.fillCircle(b, e, R, SSD1306_WHITE);
if (value >= 2) {
display.fillCircle(a, d, R, SSD1306_WHITE);
display.fillCircle(c, f, R, SSD1306_WHITE);
}
if (value >= 4) {
display.fillCircle(c, d, R, SSD1306_WHITE);
display.fillCircle(a, f, R, SSD1306_WHITE);
}
if (value == 6) {
display.fillCircle(a, e, R, SSD1306_WHITE);
display.fillCircle(c, e, R, SSD1306_WHITE);
}
}
void showDice(int one, int two) {
display.clearDisplay();
if (DICE == 1) {
drawDie(42, 10, one);
} else {
drawDie(12, 10, one);
drawDie(72, 10, two);
}
display.display();
}
void setup() {
Serial.begin(115200);
pinMode(BUTTON, INPUT_PULLUP);
Wire.begin(6, 7);
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println("SSD1306 not found at 0x3C");
for (;;) delay(1000);
}
showDice(6, 6);
Serial.println("Dice ready. Press the button to roll.");
}
void loop() {
if (digitalRead(BUTTON) == HIGH) return;
// Tumble: quick random faces that slow down, like a die settling.
int one = 1, two = 1;
for (int wait = 30; wait < 220; wait += 20) {
one = 1 + esp_random() % 6;
two = 1 + esp_random() % 6;
showDice(one, two);
delay(wait);
}
int total = DICE == 1 ? one : one + two;
counts[total]++;
rolls++;
if (DICE == 1) Serial.printf("die1=%d\n", one);
else Serial.printf("die1=%d die2=%d sum=%d\n", one, two, total);
if (rolls % 12 == 0) {
Serial.printf("after %d rolls:", rolls);
for (int t = DICE; t <= DICE * 6; t++) Serial.printf(" %d:%d", t, counts[t]);
Serial.println();
}
while (digitalRead(BUTTON) == LOW) delay(10);
delay(50);
}Parts
| Quantity | Part | Where to buy |
|---|---|---|
| 1× | ESP32 dev board | Buy at Amazon |
| 1× | 0.96 inch SSD1306 OLED, I2C, address 0x3C | |
| 1× | Push button | Buy at Adafruit |
| 6× | 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 SSD1306, Adafruit GFX Library
Load it
- In the Arduino IDE, install the esp32 board package and pick the board named above the sketch.
- Install the libraries listed above from the Library Manager.
- Paste the sketch, choose the board's port and upload. Hold BOOT if the upload stalls at Connecting.
- Close the IDE's serial monitor, then open the bench here to watch the output.
Field notes
- esp_random() uses the chip's hardware random number generator, which is truly random while the radio is on and plenty good for dice when it is off.
- With two dice, 7 should come up most often: about one roll in six. The summary every 12 rolls lets you watch that happen.
- Set DICE to 1 for a single die in the middle of the screen.
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.