Web Banner

Project - Electronic Dice

I wanted to learn more about the capabilities of the OLED (SSD1306) and decided this would be an interesting project. This is where I first encountered problems with the Adafruit OLED display driver changing the I2C bus speed. The Adafruit SSD1306 library had two hidden parameters in the class constructor.

In INO file below (Line 11) you can see that there are two parameters with the value of 100000. The first is the I2C bus speed to use when sending any display commands. The second is the I2C bus speed to set after the display command has been sent. These are not shown in the example file, but are very important.

By default the display library set's the I2C bus speed to 400000 for it's communication overriding the Wire speed. It then lowers the clock speed back to 100000. The unexpected increase in I2C bus speed caused a problem with the pull-up resistor that I was using.

I2C Rule of Thumb

- A faster bus speed requires a lower resistor.

Arduino .INO File

In order to use the class, it's very simple. Create a reference to the Dice class, then call begin.

begin (&Dispay Object , Solid Dice)

- Pass in the SSD1306 display object and then set true or false for a solid dice image

setNumber(Number Of Dice)

- Adjusts the number of dice to roll from 1 to 5.

setDiceType(Max Number On Dice) (Optional)

- Set's the maximum number on the dice (6 for D6 1-6, 4 for D4 1-4).

roll()

- Starts dice rolling.

Note: This class is blocking. The code will not proceed until the dice roll completes.

Arduino .INO File

1 : #include <Arduino.h> 2 : #include <Adafruit_GFX.h> 3 : #include <Adafruit_SSD1306.h> 4 : #include "dice.h" 5 : 6 : #define SCREEN_WIDTH 128 // OLED display width, in pixels 7 : #define SCREEN_HEIGHT 64 // OLED display height, in pixels 8 : #define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin) 9 : #define SCREEN_ADDRESS 0x3C // I2C address of the SSD1306 10 : 11 : Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET, 1000000, 1000000); 12 : 13 : Dice myDice = Dice(); // Initialize my dice object 14 : 15 : const int PIN_SWITCH = 8; // The number of the pushbutton pin 16 : 17 : void setup() { 18 : 19 : pinMode(PIN_SWITCH, INPUT); // Initialize the switch pin as an input: 20 : 21 : if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS, OLED_RESET)) { // Initialize the SSD1306 display 22 : Serial.println(F("SSD1306 allocation failed")); 23 : } 24 : 25 : display.clearDisplay(); // Clear the display memory 26 : display.display(); // Display the memory values on the screen 27 : 28 : myDice.begin(&display, true); // 1 : Pass in the reference to the display 29 : // 2 : Display dice as solid color or as an outline 30 : 31 : myDice.setNumber(4); // Sets the number of dice to roll 32 : myDice.setDiceType(6); // Sets the dice type D4 or D6 33 : } 34 : 35 : void loop() { 36 : if (digitalRead(PIN_SWITCH) == true){ 37 : myDice.roll(); 38 : } 39 : } 40 : 41 : 42 :

Arduino .H File

1 : //---------------------------------------------------- 2 : // Developed By: MakingSense_otw 3 : //---------------------------------------------------- 4 : // Description: 5 : // Rolls one or more dice and displays the animation 6 : // using the Adafruit_SSD1306 library. 7 : // 8 : // Date: Feb 18th 2024 9 : // Version: 1.0 10 : //---------------------------------------------------- 11 : 12 : #ifndef _DICE_H 13 : #define _DICE_H 14 : 15 : #include 16 : 17 : #define X_LARGE_DICE 44 //Size in pixels for the X-Large Dice 18 : #define LARGE_DICE 40 //Size in pixels for the Large Dice 19 : #define MEDIUM_DICE 29 //Size in pixels for the Medium Dice 20 : #define SMALL_DICE 24 //Size in pixels for the Small Dice 21 : 22 : class Dice { 23 : 24 : public: 25 : 26 : Dice(void); 27 : 28 : bool begin (Adafruit_SSD1306 *display, bool solid); 29 : 30 : void setNumber (int numberOfDice); 31 : void setDiceType (int diceType); 32 : 33 : void roll(); 34 : 35 : private: 36 : int _numberOfDice = 1; // The number of dice to roll 37 : int _minDiceValue = 1; // Sets the smallest random number. 38 : int _maxDiceValue = 6; // Sets the highest random number. 39 : bool _solid = true; // If true the dice has a solid white color with black dots 40 : int _numberOfRollsBeforeStopping = 10;// The number of random dice rolls before stopping 41 : 42 : int _yPositionDice = 20; // Sets the top location in pixels for the dice images 43 : 44 : Adafruit_SSD1306 *_display; // Reference to the Adafruit display object 45 : 46 : bool randomized = false; // A flag to indicate if the random seed value has been set 47 : int getValue(); 48 : void drawTextNumber (int x, int y, int size, int value, bool solid); 49 : void drawDiceImage (int x, int y, int size, int value, bool solid); 50 : void drawDiceShape (int x, int y, int size, int radius, bool solid); 51 : void drawDiceDots (int x, int y, int radius, bool solid ); 52 : void clearGraphics (void); 53 : }; 54 : 55 : #endif

Arduino .CPP File

1 : //---------------------------------------------------- 2 : // Developed By: MakingSense_otw 3 : //---------------------------------------------------- 4 : // Description: 5 : // Rolls one or more dice and displays the animation 6 : // using the Adafruit_SSD1306 library. 7 : // 8 : // Date: Feb 18th 2024 9 : // Version: 1.0 10 : //---------------------------------------------------- 11 : 12 : #include "dice.h" 13 : #include <Adafruit_SSD1306.h> 14 : 15 : #ifdef ALLOW_DICE_TEXT 16 : #include <Fonts/FreeSansBold9pt7b.h> 17 : #include <Fonts/FreeSansBold12pt7b.h> 18 : #include <Fonts/FreeSansBold18pt7b.h> 19 : #endif 20 : 21 : Dice::Dice(){ 22 : } 23 : 24 : bool Dice::begin(Adafruit_SSD1306 *display, bool solid){ 25 : // Store the references & values for the parameters 26 : _display = display 27 : _solid = solid; 28 : 29 : clearGraphics(); 30 : _display->setTextSize(1); 31 : _display->display(); 32 : 33 : return true; 34 : } 35 : 36 : void Dice::setNumber(int numberOfDice){ 37 : _numberOfDice = numberOfDice 38 : } 39 : 40 : void Dice::setDiceType(int diceType){ 41 : #ifdef ALLOW_DICE_TEXT 42 : if (diceType > 0 && diceType <= 99){ 43 : _minDiceValue = 1; 44 : _maxDiceValue = diceType; 45 : }else if (diceType == 100){ 46 : _minDiceValue = 0; 47 : _maxDiceValue = 99; 48 : } 49 : else 50 : { 51 : _minDiceValue = 1; 52 : _maxDiceValue = 6; 53 : } 54 : #else 55 : if (diceType > 0 && diceType <= 6){ 56 : _minDiceValue = 1; 57 : _maxDiceValue = diceType; 58 : } 59 : #endif 60 : } 61 : 62 : int Dice::getValue(){ 63 : return random(_minDiceValue,_maxDiceValue+1); 64 : } 65 : 66 : void Dice::roll(){ 67 : int offSet = 0; 68 : int remainingSpace = 0; 69 : 70 : if (randomized==false){ 71 : randomSeed(millis()); 72 : randomized = true; 73 : } 74 : int rollDelay = 220; 75 : 76 : for (int index = 0; index < _numberOfRollsBeforeStopping; index++) 77 : { 78 : 79 : clearGraphics(); 80 : switch(_numberOfDice){ 81 : case 1: 82 : drawDiceImage(40, _yPositionDice, X_LARGE_DICE, getValue(), _solid); 83 : break 84 : 85 : case 2: 86 : drawDiceImage(14, _yPositionDice, X_LARGE_DICE, getValue(), _solid); 87 : drawDiceImage(74, _yPositionDice, X_LARGE_DICE, getValue(), _solid); 88 : break 89 : 90 : case 3: 91 : remainingSpace = 128 - (LARGE_DICE*3); 92 : offSet = remainingSpace/2; 93 : 94 : drawDiceImage(0, _yPositionDice, LARGE_DICE, getValue(), _solid); 95 : drawDiceImage(LARGE_DICE+offSet, _yPositionDice, LARGE_DICE, getValue(), _solid); 96 : drawDiceImage((LARGE_DICE+offSet)*2, _yPositionDice, LARGE_DICE, getValue(), _solid); 97 : break 98 : 99 : case 4: 100 : remainingSpace = 128 - (MEDIUM_DICE*4); 101 : offSet = remainingSpace/3; 102 : 103 : drawDiceImage(0, _yPositionDice, MEDIUM_DICE, getValue(), _solid); 104 : drawDiceImage(MEDIUM_DICE+offSet, _yPositionDice+15, MEDIUM_DICE, getValue(), _solid); 105 : drawDiceImage((MEDIUM_DICE+offSet)*2, _yPositionDice, MEDIUM_DICE, getValue(), _solid); 106 : drawDiceImage((MEDIUM_DICE+offSet)*3, _yPositionDice+15, MEDIUM_DICE, getValue(), _solid); 107 : break 108 : 109 : case 5: 110 : remainingSpace = 128 - (SMALL_DICE*5); 111 : offSet = remainingSpace/4; 112 : 113 : drawDiceImage(0, _yPositionDice, SMALL_DICE, getValue(), _solid); 114 : drawDiceImage(SMALL_DICE+offSet, _yPositionDice+20, SMALL_DICE, getValue(), _solid); 115 : drawDiceImage((SMALL_DICE+offSet)*2, _yPositionDice, SMALL_DICE, getValue(), _solid); 116 : drawDiceImage((SMALL_DICE+offSet)*3, _yPositionDice+20, SMALL_DICE, getValue(), _solid); 117 : drawDiceImage((SMALL_DICE+offSet)*4, _yPositionDice, SMALL_DICE, getValue(), _solid); 118 : break 119 : } 120 : _display->display(); 121 : 122 : delay(rollDelay); 123 : rollDelay = rollDelay - 20; 124 : } 125 : 126 : } 127 : 128 : void Dice::clearGraphics (void){ 129 : _display->fillRect(0,16,127,48, SSD1306_BLACK); 130 : } 131 : 132 : // Draws the image of the dice 133 : void Dice::drawDiceImage(int x, int y, int size, int value, bool solid){ 134 : //There is a margin around the edge 135 : //Then the middle is split in to three points 136 : 137 : int margin = size/4; 138 : int diceRadius = size /6; 139 : int dotRadius = size/10; 140 : 141 : int diceSegment = (size - (margin * 2))/2; 142 : 143 : // Set spacing of the dots on the dice 144 : int offset0 = margin; 145 : int offset1 = margin + diceSegment; 146 : int offset2 = margin + (diceSegment *2); 147 : 148 : // Set position of each on the dots on the dice 149 : int leftTopX = x+offset0; 150 : int leftTopY = y+offset0; 151 : 152 : int leftMiddleX = x+offset0; 153 : int leftMiddleY = y+offset1; 154 : 155 : int leftBottomX = x+offset0; 156 : int leftBottomY = y+offset2; 157 : 158 : int middleMiddleX = x+offset1; 159 : int middleMiddleY = y+offset1; 160 : 161 : int rightTopX = x+offset2; 162 : int rightTopY = y+offset0; 163 : 164 : int rightMiddleX = x+offset2; 165 : int rightMiddleY = y+offset1; 166 : 167 : int rightBottomX = x+offset2;; 168 : int rightBottomY = y+offset2;; 169 : 170 : drawDiceShape(x, y, size, diceRadius, solid); 171 : 172 : if (_maxDiceValue <= 6){ 173 : if(value == 2 ||value == 3 ||value == 4 ||value == 5 ||value == 6){ 174 : drawDiceDots(leftTopX, leftTopY, dotRadius, solid); // Left Top 175 : drawDiceDots(rightBottomX, rightBottomY, dotRadius, solid); // Right Bottom 176 : } 177 : 178 : if(value == 6){ 179 : drawDiceDots(leftMiddleX, leftMiddleY, dotRadius, solid); // Left Middle 180 : drawDiceDots(rightMiddleX, rightMiddleY, dotRadius, solid); // Right Middle 181 : } 182 : 183 : if(value == 4 ||value == 5 ||value == 6){ 184 : drawDiceDots(leftBottomX, leftBottomY, dotRadius, solid); // Left Bottom 185 : drawDiceDots(rightTopX, rightTopY, dotRadius, solid); // Right Top 186 : } 187 : 188 : if(value == 1 ||value == 3 ||value == 5){ 189 : drawDiceDots(middleMiddleX, middleMiddleY, dotRadius, solid); // Middle 190 : } 191 : } 192 : #ifdef ALLOW_DICE_TEXT 193 : else 194 : { 195 : drawTextNumber(x, y, size, value, solid); 196 : } 197 : #endif 198 : } 199 : 200 : #ifdef ALLOW_DICE_TEXT 201 : void Dice::drawTextNumber (int x, int y, int size, int value, bool solid){ 202 : 203 : 204 : char charArray[6]; 205 : itoa(value,charArray,10); 206 : 207 : if (solid){ 208 : _display->setTextColor(SSD1306_BLACK); 209 : }else{ 210 : _display->setTextColor(SSD1306_WHITE); 211 : } 212 : 213 : switch (size){ 214 : case SMALL_DICE: 215 : _display->setFont(&FreeSansBold9pt7b); 216 : if (value < 10){ 217 : _display->setCursor(x+7,y+18); 218 : _display->write(charArray); 219 : }else if (value>=10 && value <=19){ 220 : _display->setCursor(x+1,y+18); 221 : _display->write(charArray[0]); 222 : _display->setCursor(x+11,y+18); 223 : _display->write(charArray[1]); 224 : }else{ 225 : _display->setCursor(x+2,y+18); 226 : _display->write(charArray[0]); 227 : _display->setCursor(x+12,y+18); 228 : _display->write(charArray[1]); 229 : } 230 : 231 : break 232 : 233 : case MEDIUM_DICE: 234 : _display->setFont(&FreeSansBold12pt7b); 235 : if (value < 10){ 236 : _display->setCursor(x+8,y+21); 237 : _display->write(charArray); 238 : }else if (value >=10 && value <=19){ 239 : _display->setCursor(x+1,y+21); 240 : _display->write(charArray[0]); 241 : _display->setCursor(x+12,y+21); 242 : _display->write(charArray[1]); 243 : } 244 : else{ 245 : _display->setCursor(x+1,y+21); 246 : _display->write(charArray); 247 : } 248 : 249 : break 250 : 251 : case LARGE_DICE: 252 : _display->setFont(&FreeSansBold18pt7b); 253 : if (value < 10){ 254 : _display->setCursor(x+11,y+31); 255 : _display->write(charArray); 256 : }else if (value>= 10 && value<=19){ 257 : _display->setCursor(x+2,y+31); 258 : _display->write(charArray[0]); 259 : _display->setCursor(x+18,y+31); 260 : _display->write(charArray[1]); 261 : }else{ 262 : _display->setCursor(x+2,y+31); 263 : _display->write(charArray[0]); 264 : _display->setCursor(x+19,y+31); 265 : _display->write(charArray[1]); 266 : } 267 : 268 : break 269 : 270 : case X_LARGE_DICE: 271 : _display->setFont(&FreeSansBold18pt7b); 272 : if (value < 10){ 273 : _display->setCursor(x+12,y+33); 274 : _display->write(charArray); 275 : }else if (value>= 10 && value<=19){ 276 : _display->setCursor(x+3,y+33); 277 : _display->write(charArray[0]); 278 : _display->setCursor(x+18,y+33); 279 : _display->write(charArray[1]); 280 : }else{ 281 : _display->setCursor(x+3,y+33); 282 : _display->write(charArray[0]); 283 : _display->setCursor(x+21,y+33); 284 : _display->write(charArray[1]); 285 : } 286 : break 287 : } 288 : } 289 : #endif 290 : 291 : void Dice::drawDiceShape (int x, int y, int size, int radius, bool solid){ 292 : if (solid == false){ 293 : _display->drawRoundRect(x, y, size, size, radius, SSD1306_WHITE); 294 : }else { 295 : _display->fillRoundRect(x, y, size, size, radius, SSD1306_WHITE); 296 : } 297 : } 298 : 299 : void Dice::drawDiceDots (int x, int y, int radius, bool solid ){ 300 : if (solid == false){ 301 : _display->drawCircle(x,y,radius,SSD1306_WHITE); 302 : } else { 303 : _display->fillCircle(x,y,radius,SSD1306_BLACK); 304 : } 305 : } 306 :

Download

Source Code

A zip file containing the complete Arduino dice project

Zip file containing source code

Arduino Libraries

Library Name Author Version
Adafruit GFX Library Adafruit 1.11.9
Adafruit SSD1306 Adafruit 2.5.9