Maker.io main logo

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.

project_1

The finished product.

BOM

Product/Amount/Where to buy

Assembling the Project

Thanks to the Arduino doing most of the heavy lifting, the schematic diagram for this project is fairly simple to understand:

schemeit_2

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:

breadboard_3

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:

Copy Code
#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:

Copy Code
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:

Copy Code
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:

Copy Code
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:

Copy Code
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.

display_4

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.

制造商零件编号 A000066
ARDUINO UNO R3 ATMEGA328P BOARD
Arduino
¥190.97
Details
制造商零件编号 3128
0.54" 4-DIGIT 14-SEGMENT BLUE
Adafruit Industries LLC
¥117.54
Details
制造商零件编号 CF14JT2K20
RES 2.2K OHM 5% 1/4W AXIAL
Stackpole Electronics Inc
¥0.81
Details
制造商零件编号 MJTP1230
SWITCH TACTILE SPST-NO 0.05A 12V
APEM Inc.
¥1.79
Details
制造商零件编号 1957
JUMPER WIRE M TO M 6" 28AWG
Adafruit Industries LLC
¥15.87
Details
制造商零件编号 MIKROE-1097
BREADBOARD TERMINAL STRIP
MikroElektronika
¥75.75
Details
Add all DigiKey Parts to Cart
TechForum

Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.

Visit TechForum