Arduino Dice Roller with Statistics using OLED Display (Wokwi Simulation)

 

Build a smart Digital Dice Roller with real-time statistics tracking using the Arduino Uno in the Wokwi. This interactive STEM project simulates a 6-sided dice and records how many times each number (1–6) appears, helping students understand random number generation, probability, and data tracking using Arduino.

The dice results are displayed on a crisp SSD1306 OLED Display (or optional 16x2 LCD), along with roll history, distribution bars, and statistical insights like average, most common, and least common numbers.

Perfect for beginners, robotics learners, and embedded systems students, this project combines electronics + mathematics + programming in one engaging simulation.


Key Features

✔ Digital 6-sided dice simulation
✔ Real-time statistics tracking
✔ Animated rolling effect
✔ Last 10 roll history display
✔ Probability distribution bars
✔ Average calculation
✔ Most & least common number detection
✔ Push-button roll control
✔ Buzzer sound effects
✔ Fully testable in Wokwi


 How It Works

When the Roll button is pressed:

  1. The Arduino uses the random() function to generate a number between 1 and 6.

  2. The number is displayed with a graphical dice animation.

  3. The roll count is stored in an array.

  4. Statistics are updated automatically.

  5. The buzzer plays a sound based on the rolled value.

Pressing the Stats button switches to a statistics dashboard showing:

  • Total rolls

  • Average value

  • Distribution bars

  • Most frequent number

  • Least frequent number


 Learning Outcomes

  • Understanding random number generation in Arduino

  • Using arrays for data storage

  • Working with push buttons (INPUT_PULLUP logic)

  • Displaying dynamic graphics on OLED

  • Probability and statistics visualization

  • Embedded C++ programming concepts

This project makes probability theory practical and visual, helping students observe real-world randomness patterns over time.


 Applications

  • STEM classroom demonstration

  • Arduino beginner workshop project

  • Probability experiment simulator

  • Embedded systems training

  • DIY handheld electronic dice

  • Robotics exhibition project

Code:

/*
 * DICE ROLLER WITH STATISTICS
 * Features:
 * - Roll button for 6-sided dice
 * - OLED display shows current roll
 * - Tracks last 10 rolls
 * - Real-time probability tracking
 * - Statistics: Average, most/least common
 * - Animated dice roll effect
 */

#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// Pin definitions
const int BTN_ROLL = 2;
const int BTN_STATS = 3;
const int BUZZER = 8;

// Game state
int currentRoll = 0;
int totalRolls = 0;
int rollHistory[10] = {0};  // Last 10 rolls
int rollCounts[6] = {0};    // Count of each number (1-6)
bool isRolling = false;
bool showStats = false;

// Button debouncing
bool rollPressed = false;
bool statsPressed = false;
unsigned long lastRollTime = 0;
unsigned long rollAnimStart = 0;

void setup() {
  Serial.begin(9600);
  Serial.println("Dice Roller Starting...");
 
  // Initialize buttons
  pinMode(BTN_ROLL, INPUT_PULLUP);
  pinMode(BTN_STATS, INPUT_PULLUP);
  pinMode(BUZZER, OUTPUT);
 
  // Initialize display
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println("Display failed!");
    for(;;);
  }
 
  display.clearDisplay();
  display.setTextColor(SSD1306_WHITE);
 
  // Show welcome screen
  showWelcome();
  delay(2000);
 
  // Seed random number generator
  randomSeed(analogRead(A0));
}

void loop() {
  unsigned long now = millis();
 
  // Check roll button
  if (digitalRead(BTN_ROLL) == LOW && !rollPressed && !isRolling) {
    rollPressed = true;
    startRoll();
  } else if (digitalRead(BTN_ROLL) == HIGH) {
    rollPressed = false;
  }
 
  // Check stats button
  if (digitalRead(BTN_STATS) == LOW && !statsPressed && !isRolling) {
    statsPressed = true;
    showStats = !showStats;
    tone(BUZZER, 800, 50);
  } else if (digitalRead(BTN_STATS) == HIGH) {
    statsPressed = false;
  }
 
  // Handle rolling animation
  if (isRolling) {
    if (now - rollAnimStart < 1000) {
      // Animate rolling
      animateRoll();
    } else {
      // Finish roll
      finishRoll();
    }
  } else {
    // Display current view
    display.clearDisplay();
    if (showStats) {
      drawStatsScreen();
    } else {
      drawMainScreen();
    }
    display.display();
  }
 
  delay(50);
}

void showWelcome() {
  display.clearDisplay();
  display.setTextSize(2);
  display.setCursor(25, 10);
  display.println("DICE");
  display.setCursor(15, 30);
  display.println("ROLLER");
  display.setTextSize(1);
  display.setCursor(10, 52);
  display.println("Press to Roll!");
  display.display();
}

void startRoll() {
  isRolling = true;
  rollAnimStart = millis();
  playRollSound();
}

void animateRoll() {
  display.clearDisplay();
 
  // Show "ROLLING..." text
  display.setTextSize(2);
  display.setCursor(10, 10);
  display.println("ROLLING");
 
  // Show random numbers flashing
  int tempRoll = random(1, 7);
  drawLargeDice(64, 40, tempRoll);
 
  display.display();
}

void finishRoll() {
  isRolling = false;
 
  // Generate final roll
  currentRoll = random(1, 7);
  totalRolls++;
 
  // Update history (shift array and add new roll)
  for (int i = 9; i > 0; i--) {
    rollHistory[i] = rollHistory[i-1];
  }
  rollHistory[0] = currentRoll;
 
  // Update counts
  rollCounts[currentRoll - 1]++;
 
  // Play result sound
  playResultSound(currentRoll);
 
  // Log to serial
  Serial.print("Roll #");
  Serial.print(totalRolls);
  Serial.print(": ");
  Serial.println(currentRoll);
 
  // Flash the result
  for (int i = 0; i < 2; i++) {
    display.clearDisplay();
    drawLargeDice(64, 32, currentRoll);
    display.display();
    delay(150);
    display.clearDisplay();
    display.display();
    delay(100);
  }
}

void drawMainScreen() {
  // Title
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.print("DICE ROLLER");
 
  // Roll count
  display.setCursor(90, 0);
  display.print("#");
  display.print(totalRolls);
 
  // Draw line
  display.drawLine(0, 10, 128, 10, SSD1306_WHITE);
 
  if (currentRoll > 0) {
    // Show current roll (large)
    drawLargeDice(64, 35, currentRoll);
   
    // Show last 5 rolls (small)
    display.setTextSize(1);
    display.setCursor(0, 56);
    display.print("Last:");
    for (int i = 0; i < min(5, totalRolls); i++) {
      display.print(" ");
      display.print(rollHistory[i]);
    }
  } else {
    // First time - show instructions
    display.setTextSize(1);
    display.setCursor(15, 25);
    display.println("Press ROLL");
    display.setCursor(10, 35);
    display.println("button to start");
  }
 
  // Button labels
  display.drawLine(0, 52, 128, 52, SSD1306_WHITE);
  display.setTextSize(1);
  display.setCursor(0, 56);
  display.print("Roll");
  display.setCursor(92, 56);
  display.print("Stats");
}

void drawStatsScreen() {
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.print("STATISTICS");
  display.drawLine(0, 10, 128, 10, SSD1306_WHITE);
 
  if (totalRolls == 0) {
    display.setCursor(10, 25);
    display.println("No rolls yet!");
    return;
  }
 
  // Total rolls
  display.setCursor(0, 14);
  display.print("Total Rolls: ");
  display.print(totalRolls);
 
  // Average
  float avg = calculateAverage();
  display.setCursor(0, 24);
  display.print("Average: ");
  display.print(avg, 2);
 
  // Distribution (probability bars)
  display.setCursor(0, 34);
  display.println("Distribution:");
 
  for (int i = 0; i < 6; i++) {
    int x = 0;
    int y = 44 + (i / 3) * 9;
    int col = i % 3;
    x = col * 42;
   
    // Number
    display.setCursor(x, y);
    display.print(i + 1);
    display.print(":");
   
    // Mini bar
    int barWidth = map(rollCounts[i], 0, max(totalRolls/6 + 3, 1), 0, 22);
    display.drawRect(x + 12, y, 24, 7, SSD1306_WHITE);
    display.fillRect(x + 13, y + 1, barWidth, 5, SSD1306_WHITE);
  }
 
  // Most/Least common
  int mostCommon = getMostCommon();
  int leastCommon = getLeastCommon();
 
  // Button labels
  display.drawLine(0, 52, 128, 52, SSD1306_WHITE);
  display.setCursor(0, 56);
  display.print("Roll");
  display.setCursor(92, 56);
  display.print("Back");
}

void drawLargeDice(int x, int y, int number) {
  int size = 20;
  int dotSize = 3;
 
  // Draw dice square
  display.fillRoundRect(x - size, y - size, size * 2, size * 2, 4, SSD1306_WHITE);
  display.fillRoundRect(x - size + 2, y - size + 2, size * 2 - 4, size * 2 - 4, 2, SSD1306_BLACK);
 
  // Draw dots based on number
  switch(number) {
    case 1:
      display.fillCircle(x, y, dotSize, SSD1306_WHITE);
      break;
    case 2:
      display.fillCircle(x - 8, y - 8, dotSize, SSD1306_WHITE);
      display.fillCircle(x + 8, y + 8, dotSize, SSD1306_WHITE);
      break;
    case 3:
      display.fillCircle(x - 8, y - 8, dotSize, SSD1306_WHITE);
      display.fillCircle(x, y, dotSize, SSD1306_WHITE);
      display.fillCircle(x + 8, y + 8, dotSize, SSD1306_WHITE);
      break;
    case 4:
      display.fillCircle(x - 8, y - 8, dotSize, SSD1306_WHITE);
      display.fillCircle(x + 8, y - 8, dotSize, SSD1306_WHITE);
      display.fillCircle(x - 8, y + 8, dotSize, SSD1306_WHITE);
      display.fillCircle(x + 8, y + 8, dotSize, SSD1306_WHITE);
      break;
    case 5:
      display.fillCircle(x - 8, y - 8, dotSize, SSD1306_WHITE);
      display.fillCircle(x + 8, y - 8, dotSize, SSD1306_WHITE);
      display.fillCircle(x, y, dotSize, SSD1306_WHITE);
      display.fillCircle(x - 8, y + 8, dotSize, SSD1306_WHITE);
      display.fillCircle(x + 8, y + 8, dotSize, SSD1306_WHITE);
      break;
    case 6:
      display.fillCircle(x - 8, y - 10, dotSize, SSD1306_WHITE);
      display.fillCircle(x + 8, y - 10, dotSize, SSD1306_WHITE);
      display.fillCircle(x - 8, y, dotSize, SSD1306_WHITE);
      display.fillCircle(x + 8, y, dotSize, SSD1306_WHITE);
      display.fillCircle(x - 8, y + 10, dotSize, SSD1306_WHITE);
      display.fillCircle(x + 8, y + 10, dotSize, SSD1306_WHITE);
      break;
  }
}

float calculateAverage() {
  if (totalRolls == 0) return 0;
 
  int sum = 0;
  for (int i = 0; i < 6; i++) {
    sum += rollCounts[i] * (i + 1);
  }
  return (float)sum / totalRolls;
}

int getMostCommon() {
  int maxCount = 0;
  int maxIndex = 0;
  for (int i = 0; i < 6; i++) {
    if (rollCounts[i] > maxCount) {
      maxCount = rollCounts[i];
      maxIndex = i;
    }
  }
  return maxIndex + 1;
}

int getLeastCommon() {
  int minCount = totalRolls + 1;
  int minIndex = 0;
  for (int i = 0; i < 6; i++) {
    if (rollCounts[i] < minCount && rollCounts[i] > 0) {
      minCount = rollCounts[i];
      minIndex = i;
    }
  }
  return minIndex + 1;
}

void playRollSound() {
  // Rolling sound effect
  for (int i = 0; i < 5; i++) {
    tone(BUZZER, 200 + i * 100, 50);
    delay(60);
  }
}

void playResultSound(int roll) {
  // Higher roll = higher pitch
  int freq = 400 + (roll * 100);
  tone(BUZZER, freq, 200);
  delay(220);
  tone(BUZZER, freq + 100, 150);
}

 Key Features

  • Digital dice simulation

  • Real-time statistics tracking

  • OLED or LCD display output

  • Button-controlled roll action

  • Beginner-friendly Arduino logic

  • Fully testable in Wokwi simulator

Diagram.json:
{
  "version": 1,
  "author": "Wokwi",
  "editor": "wokwi",
  "parts": [
    { "type": "wokwi-arduino-uno", "id": "uno", "top": 0, "left": 0, "attrs": {} },
    { "type": "wokwi-ssd1306", "id": "oled", "top": -100, "left": 100, "attrs": {} },
    { "type": "wokwi-pushbutton", "id": "btn_roll", "top": 150, "left": -20, "attrs": { "color": "green", "label": "ROLL" } },
    { "type": "wokwi-pushbutton", "id": "btn_stats", "top": 150, "left": 60, "attrs": { "color": "blue", "label": "STATS" } },
    { "type": "wokwi-buzzer", "id": "buzzer", "top": 100, "left": -100, "attrs": {} }
  ],
  "connections": [
    [ "oled:GND", "uno:GND.1", "black", [ "v0" ] ],
    [ "oled:VCC", "uno:5V", "red", [ "v0" ] ],
    [ "oled:SDA", "uno:A4", "blue", [ "v0" ] ],
    [ "oled:SCL", "uno:A5", "green", [ "v0" ] ],
    [ "btn_roll:1.l", "uno:2", "green", [ "v0" ] ],
    [ "btn_roll:2.l", "uno:GND.2", "black", [ "v0" ] ],
    [ "btn_stats:1.l", "uno:3", "blue", [ "v0" ] ],
    [ "btn_stats:2.l", "uno:GND.2", "black", [ "v0" ] ],
    [ "buzzer:1", "uno:8", "red", [ "v0" ] ],
    [ "buzzer:2", "uno:GND.3", "black", [ "v0" ] ]
  ]
}


 Learning Outcomes

  • Understanding random number generation in Arduino

  • Using arrays and counters

  • Working with push button inputs

  • Displaying dynamic data on OLED/LCD

  • Learning probability concepts using electronics

Comments