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.
- A faster bus speed requires a lower resistor.
In order to use the class, it's very simple. Create a reference to the Dice class, then call begin.
- Pass in the SSD1306 display object and then set true or false for a solid dice image
- Adjusts the number of dice to roll from 1 to 5.
- Set's the maximum number on the dice (6 for D6 1-6, 4 for D4 1-4).
- Starts dice rolling.
Note: This class is blocking. The code will not proceed until the dice roll completes.
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 :
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
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 :
Library Name | Author | Version |
---|---|---|
Adafruit GFX Library | Adafruit | 1.11.9 |
Adafruit SSD1306 | Adafruit | 2.5.9 |