All Dungeons and Dragons Dice in One Device
2021-12-27 | By Maker.io Staff
License: None
A previous article explored how to build a simple electronic die with only a handful of integrated circuits. That die was perfect for most board games that use a standard six-sided die. Some games, such as Dungeons and Dragons, for example, require the players to use a variety of different dice. This project combines the various dice needed for playing a game of DnD in a convenient Arduino-powered project that picks a random number when users need it.
The finished product.
BOM
Product/Amount/Where to buy
- Arduino UNO - 1
- 14-Segment Display - 1
- 2.2K Resistor - 2
- Tactile Push Button - 2
- 6” M/M Jumper Wires - 1
- Solderless Breadboard - 1
Assembling the Project
Thanks to the Arduino doing most of the heavy lifting, the schematic diagram for this project is fairly simple to understand:
The schematic diagram for this project. Scheme-It link
As you can see, the Arduino serves as the brain of this project. It recognizes user inputs, calculates a random number, and displays the result on the 14-segment display. First, connect one side of each button to +5V. Tie each button’s neighboring pin to GND via a 2.2K pull-down resistor. Then, attach each button to a digital pin on the Arduino. Lastly, connect the I2C data and clock line of the display to the Arduino, and don’t forget to tie the VCC and GND lines of the screen to the Arduino.
When using a breadboard, the result should look similar to this:
I assembled the project on a solderless breadboard according to the schematic above.
A Look at this Project’s Software
As mentioned earlier, the Arduino does most of the work in this project. Therefore, the software part of this project is a bit more complex than the schematic. I’ll go over the essential aspects of this project’s firmware in more detail, and you can download the complete Arduino sketch at the end of this article.
Before getting started, make sure that you install the Adafruit LED Backpack and Adafruit GFX libraries using the Arduino IDE’s built-in library manager. The firmware sketch first imports both libraries and then defines a few variables:
#include <Wire.h>
#include <Adafruit_GFX.h>
#include "Adafruit_LEDBackpack.h"
#define BTN_DEBOUNCE 100
#define ROLL_ANIM_LENGTH 50
#define DISPLAY_ADDRESS 0x70
#define SWITCH_BTN 2
#define ROLL_BTN 3
#define NUM_DICE 6
Adafruit_AlphaNum4 disp = Adafruit_AlphaNum4();
unsigned limits[NUM_DICE] = {4, 6, 8, 10, 12, 20};
unsigned selectedDie = 0;
unsigned long lastButtonPress = 0UL;
bool rolling = false;
bool diceInfoDisplaying = false;
bool switchButtonDown = false;
bool rollButtonDown = false;
The define statements tell the Arduino which pins you used to connect the buttons, and it also lets you tweak the behavior of the finished product. The limits array defines the dice you want to combine in this device. I used six dice, and I entered the highest number you can get when rolling each die. The remaining variables are mainly there for de-bounce purposes.
Next, the setup()-method:
void setup()
{
disp.begin(DISPLAY_ADDRESS);
disp.setBrightness(1);
displaySelectedDie();
displayRollResult(limits[selectedDie]);
pinMode(SWITCH_BTN, OUTPUT);
pinMode(ROLL_BTN, OUTPUT);
}
This method initializes the display and the pushbuttons. Then, it lets the user know which die is selected by default. Apart from the chosen die, the Arduino also informs the user of the maximum number you can roll with the selected die. The displaySelectedDie() and displayRollResult() functions are two relatively short helper functions that send a supplied number to the 14-segment display:
void displayRollResult(int roll)
{
if(roll > 99)
roll = 99;
if(roll < 0)
roll = 0;
int firstDigit = (roll / 10);
int secondDigit = (roll - firstDigit * 10);
displaySelectedDie();
disp.writeDigitAscii(2, '0' + firstDigit);
disp.writeDigitAscii(3, '0' + secondDigit);
disp.writeDisplay();
}
Note that you can only send a single character to the display at once. Therefore, I instructed the Arduino to split the supplied number into two digits. Then, the Arduino transmits each digit to the display controller. Note that you have to use ASCII characters with this function, so the program converts the digits to characters before sending them to the display. For that purpose, you can add a number to the character ‘0’ and then send the resulting char to the display.
Lastly, the loop()-method:
void loop()
{
unsigned long currentMillis = millis();
if(!rolling)
{
if(currentMillis - lastButtonPress > BTN_DEBOUNCE)
{
if(digitalRead(ROLL_BTN))
{
if(!rollButtonDown)
{
rolling = true;
rollButtonDown = true;
}
}
else
{
rollButtonDown = false;
}
if(digitalRead(SWITCH_BTN))
{
if(!switchButtonDown)
{
selectedDie = (selectedDie + 1) % NUM_DICE;
switchButtonDown = true;
displaySelectedDie();
displayRollResult(limits[selectedDie]);
}
}
else
{
switchButtonDown = false;
}
lastButtonPress = currentMillis;
}
}
else
{
/* Roll the selected die (code omitted; see below) */
}
}
This first part of the loop()-method handles user inputs. The Arduino ignores button presses while it’s in the process of rolling a die. Otherwise, the program checks whether enough time has elapsed since it last detected user input. If that’s the case, the Arduino checks each button individually.
When the user presses the roll button, the Arduino sets the rolling and rollButtonDown variables to true. The first variable initiates a roll the next time the loop()-function gets called. The second variable tells the Arduino to ignore subsequent button presses until the user releases the roll button. Then, the software uses the same technique to check the other button for presses. With the second button, users can select the die they want to roll. If a user changes the active die, the Arduino displays the die’s number and the maximum value a user can roll.
The following code implements the roll logic:
void loop()
{
unsigned long currentMillis = millis();
if(!rolling)
{
/* Already discussed; see above */
}
else
{
unsigned rollChanges = 0;
while(rollChanges < ROLL_ANIM_LENGTH)
{
int roll = random(1, limits[selectedDie] + 1);
unsigned long m = millis();
displaySelectedDie();
displayRollResult(roll);
while(millis() - m < rollChanges * 2.5) {} // Wait for a while. The wait delay increases with the number of rolls
rollChanges++;
}
rolling = false;
}
}
Once the Arduino changes the roll variable to true, the loop function repeatedly picks a random number and displays it shortly. The method does this 50 times, and the display period increases with every new number. It’s unnecessary to pick 50 numbers. However, this way, the display plays a short animation each time a user rolls a die, which makes playing board games with family and friends more exciting. Once the loop finishes, the Arduino displays the last random number it picked. The result stays on the display until a user presses another button.
The Arduino displays the final result of the die roll once it finishes the animation. Note that it also shows the selected die. In this example, the Arduino rolled die number six, and the result is 13.
Download the Source Code
You can download the complete firmware code here.
Summary
This article discussed how to build a simple device that combines multiple dice in a single, convenient package. The schematic is simple, as the Arduino does most of the work. Besides an Arduino UNO, the circuit contains two tactile pushbuttons, two 2.2K resistors, and a 14-segment display module. The software does most of the heavy lifting. It first initializes the switches and the external display. Then, it listens for user inputs. Users can use two buttons to interact with the system. The first button selects the die, and the second one instructs the Arduino to think of a random number. Once rolled, the Arduino plays a short animation that simulates a real-life die roll, and it then continuously displays the result until the user makes another input.
Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.
Visit TechForum