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.
#include <Arduino.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "dice.h"
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C // I2C address of the SSD1306
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET, 1000000, 1000000);
Dice myDice = Dice(); // Initialize my dice object
const int PIN_SWITCH = 8; // The number of the pushbutton pin
void setup() {
pinMode(PIN_SWITCH, INPUT); // Initialize the switch pin as an input:
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS, OLED_RESET)) { // Initialize the SSD1306 display
Serial.println(F("SSD1306 allocation failed"));
}
display.clearDisplay(); // Clear the display memory
display.display(); // Display the memory values on the screen
myDice.begin(&display, true); // 1 : Pass in the reference to the display
// 2 : Display dice as solid color or as an outline
myDice.setNumber(4); // Sets the number of dice to roll
myDice.setDiceType(6); // Sets the dice type D4 or D6
}
void loop() {
if (digitalRead(PIN_SWITCH) == true){
myDice.roll();
}
}
//----------------------------------------------------
// Developed By: MakingSense_otw
//----------------------------------------------------
// Description:
// Rolls one or more dice and displays the animation
// using the Adafruit_SSD1306 library.
//
// Date: Feb 18th 2024
// Version: 1.0
//----------------------------------------------------
#ifndef _DICE_H
#define _DICE_H
#include
#define X_LARGE_DICE 44 //Size in pixels for the X-Large Dice
#define LARGE_DICE 40 //Size in pixels for the Large Dice
#define MEDIUM_DICE 29 //Size in pixels for the Medium Dice
#define SMALL_DICE 24 //Size in pixels for the Small Dice
class Dice {
public:
Dice(void);
bool begin (Adafruit_SSD1306 *display, bool solid);
void setNumber (int numberOfDice);
void setDiceType (int diceType);
void roll();
private:
int _numberOfDice = 1; // The number of dice to roll
int _minDiceValue = 1; // Sets the smallest random number.
int _maxDiceValue = 6; // Sets the highest random number.
bool _solid = true; // If true the dice has a solid white color with black dots
int _numberOfRollsBeforeStopping = 10;// The number of random dice rolls before stopping
int _yPositionDice = 20; // Sets the top location in pixels for the dice images
Adafruit_SSD1306 *_display; // Reference to the Adafruit display object
bool randomized = false; // A flag to indicate if the random seed value has been set
int getValue();
void drawTextNumber (int x, int y, int size, int value, bool solid);
void drawDiceImage (int x, int y, int size, int value, bool solid);
void drawDiceShape (int x, int y, int size, int radius, bool solid);
void drawDiceDots (int x, int y, int radius, bool solid );
void clearGraphics (void);
};
#endif
//----------------------------------------------------
// Developed By: MakingSense_otw
//----------------------------------------------------
// Description:
// Rolls one or more dice and displays the animation
// using the Adafruit_SSD1306 library.
//
// Date: Feb 18th 2024
// Version: 1.0
//----------------------------------------------------
#include "dice.h"
#include <Adafruit_SSD1306.h>
#ifdef ALLOW_DICE_TEXT
#include <Fonts/FreeSansBold9pt7b.h>
#include <Fonts/FreeSansBold12pt7b.h>
#include <Fonts/FreeSansBold18pt7b.h>
#endif
Dice::Dice(){
}
bool Dice::begin(Adafruit_SSD1306 *display, bool solid){
// Store the references & values for the parameters
_display = display
_solid = solid;
clearGraphics();
_display->setTextSize(1);
_display->display();
return true;
}
void Dice::setNumber(int numberOfDice){
_numberOfDice = numberOfDice
}
void Dice::setDiceType(int diceType){
#ifdef ALLOW_DICE_TEXT
if (diceType > 0 && diceType <= 99){
_minDiceValue = 1;
_maxDiceValue = diceType;
}else if (diceType == 100){
_minDiceValue = 0;
_maxDiceValue = 99;
}
else
{
_minDiceValue = 1;
_maxDiceValue = 6;
}
#else
if (diceType > 0 && diceType <= 6){
_minDiceValue = 1;
_maxDiceValue = diceType;
}
#endif
}
int Dice::getValue(){
return random(_minDiceValue,_maxDiceValue+1);
}
void Dice::roll(){
int offSet = 0;
int remainingSpace = 0;
if (randomized==false){
randomSeed(millis());
randomized = true;
}
int rollDelay = 220;
for (int index = 0; index < _numberOfRollsBeforeStopping; index++)
{
clearGraphics();
switch(_numberOfDice){
case 1:
drawDiceImage(40, _yPositionDice, X_LARGE_DICE, getValue(), _solid);
break
case 2:
drawDiceImage(14, _yPositionDice, X_LARGE_DICE, getValue(), _solid);
drawDiceImage(74, _yPositionDice, X_LARGE_DICE, getValue(), _solid);
break
case 3:
remainingSpace = 128 - (LARGE_DICE*3);
offSet = remainingSpace/2;
drawDiceImage(0, _yPositionDice, LARGE_DICE, getValue(), _solid);
drawDiceImage(LARGE_DICE+offSet, _yPositionDice, LARGE_DICE, getValue(), _solid);
drawDiceImage((LARGE_DICE+offSet)*2, _yPositionDice, LARGE_DICE, getValue(), _solid);
break
case 4:
remainingSpace = 128 - (MEDIUM_DICE*4);
offSet = remainingSpace/3;
drawDiceImage(0, _yPositionDice, MEDIUM_DICE, getValue(), _solid);
drawDiceImage(MEDIUM_DICE+offSet, _yPositionDice+15, MEDIUM_DICE, getValue(), _solid);
drawDiceImage((MEDIUM_DICE+offSet)*2, _yPositionDice, MEDIUM_DICE, getValue(), _solid);
drawDiceImage((MEDIUM_DICE+offSet)*3, _yPositionDice+15, MEDIUM_DICE, getValue(), _solid);
break
case 5:
remainingSpace = 128 - (SMALL_DICE*5);
offSet = remainingSpace/4;
drawDiceImage(0, _yPositionDice, SMALL_DICE, getValue(), _solid);
drawDiceImage(SMALL_DICE+offSet, _yPositionDice+20, SMALL_DICE, getValue(), _solid);
drawDiceImage((SMALL_DICE+offSet)*2, _yPositionDice, SMALL_DICE, getValue(), _solid);
drawDiceImage((SMALL_DICE+offSet)*3, _yPositionDice+20, SMALL_DICE, getValue(), _solid);
drawDiceImage((SMALL_DICE+offSet)*4, _yPositionDice, SMALL_DICE, getValue(), _solid);
break
}
_display->display();
delay(rollDelay);
rollDelay = rollDelay - 20;
}
}
void Dice::clearGraphics (void){
_display->fillRect(0,16,127,48, SSD1306_BLACK);
}
// Draws the image of the dice
void Dice::drawDiceImage(int x, int y, int size, int value, bool solid){
//There is a margin around the edge
//Then the middle is split in to three points
int margin = size/4;
int diceRadius = size /6;
int dotRadius = size/10;
int diceSegment = (size - (margin * 2))/2;
// Set spacing of the dots on the dice
int offset0 = margin;
int offset1 = margin + diceSegment;
int offset2 = margin + (diceSegment *2);
// Set position of each on the dots on the dice
int leftTopX = x+offset0;
int leftTopY = y+offset0;
int leftMiddleX = x+offset0;
int leftMiddleY = y+offset1;
int leftBottomX = x+offset0;
int leftBottomY = y+offset2;
int middleMiddleX = x+offset1;
int middleMiddleY = y+offset1;
int rightTopX = x+offset2;
int rightTopY = y+offset0;
int rightMiddleX = x+offset2;
int rightMiddleY = y+offset1;
int rightBottomX = x+offset2;;
int rightBottomY = y+offset2;;
drawDiceShape(x, y, size, diceRadius, solid);
if (_maxDiceValue <= 6){
if(value == 2 ||value == 3 ||value == 4 ||value == 5 ||value == 6){
drawDiceDots(leftTopX, leftTopY, dotRadius, solid); // Left Top
drawDiceDots(rightBottomX, rightBottomY, dotRadius, solid); // Right Bottom
}
if(value == 6){
drawDiceDots(leftMiddleX, leftMiddleY, dotRadius, solid); // Left Middle
drawDiceDots(rightMiddleX, rightMiddleY, dotRadius, solid); // Right Middle
}
if(value == 4 ||value == 5 ||value == 6){
drawDiceDots(leftBottomX, leftBottomY, dotRadius, solid); // Left Bottom
drawDiceDots(rightTopX, rightTopY, dotRadius, solid); // Right Top
}
if(value == 1 ||value == 3 ||value == 5){
drawDiceDots(middleMiddleX, middleMiddleY, dotRadius, solid); // Middle
}
}
#ifdef ALLOW_DICE_TEXT
else
{
drawTextNumber(x, y, size, value, solid);
}
#endif
}
#ifdef ALLOW_DICE_TEXT
void Dice::drawTextNumber (int x, int y, int size, int value, bool solid){
char charArray[6];
itoa(value,charArray,10);
if (solid){
_display->setTextColor(SSD1306_BLACK);
}else{
_display->setTextColor(SSD1306_WHITE);
}
switch (size){
case SMALL_DICE:
_display->setFont(&FreeSansBold9pt7b);
if (value < 10){
_display->setCursor(x+7,y+18);
_display->write(charArray);
}else if (value>=10 && value <=19){
_display->setCursor(x+1,y+18);
_display->write(charArray[0]);
_display->setCursor(x+11,y+18);
_display->write(charArray[1]);
}else{
_display->setCursor(x+2,y+18);
_display->write(charArray[0]);
_display->setCursor(x+12,y+18);
_display->write(charArray[1]);
}
break
case MEDIUM_DICE:
_display->setFont(&FreeSansBold12pt7b);
if (value < 10){
_display->setCursor(x+8,y+21);
_display->write(charArray);
}else if (value >=10 && value <=19){
_display->setCursor(x+1,y+21);
_display->write(charArray[0]);
_display->setCursor(x+12,y+21);
_display->write(charArray[1]);
}
else{
_display->setCursor(x+1,y+21);
_display->write(charArray);
}
break
case LARGE_DICE:
_display->setFont(&FreeSansBold18pt7b);
if (value < 10){
_display->setCursor(x+11,y+31);
_display->write(charArray);
}else if (value>= 10 && value<=19){
_display->setCursor(x+2,y+31);
_display->write(charArray[0]);
_display->setCursor(x+18,y+31);
_display->write(charArray[1]);
}else{
_display->setCursor(x+2,y+31);
_display->write(charArray[0]);
_display->setCursor(x+19,y+31);
_display->write(charArray[1]);
}
break
case X_LARGE_DICE:
_display->setFont(&FreeSansBold18pt7b);
if (value < 10){
_display->setCursor(x+12,y+33);
_display->write(charArray);
}else if (value>= 10 && value<=19){
_display->setCursor(x+3,y+33);
_display->write(charArray[0]);
_display->setCursor(x+18,y+33);
_display->write(charArray[1]);
}else{
_display->setCursor(x+3,y+33);
_display->write(charArray[0]);
_display->setCursor(x+21,y+33);
_display->write(charArray[1]);
}
break
}
}
#endif
void Dice::drawDiceShape (int x, int y, int size, int radius, bool solid){
if (solid == false){
_display->drawRoundRect(x, y, size, size, radius, SSD1306_WHITE);
}else {
_display->fillRoundRect(x, y, size, size, radius, SSD1306_WHITE);
}
}
void Dice::drawDiceDots (int x, int y, int radius, bool solid ){
if (solid == false){
_display->drawCircle(x,y,radius,SSD1306_WHITE);
} else {
_display->fillCircle(x,y,radius,SSD1306_BLACK);
}
}
Library Name | Author | Version |
---|---|---|
Adafruit GFX Library | Adafruit | 1.11.9 |
Adafruit SSD1306 | Adafruit | 2.5.9 |