GAME BOX
In this tutorial, we will guide you through creating a versatile digital display project using the TM1637 4-digit 7-segment display, an Arduino, and basic electronic components. This project features multiple modes, including a 6-sided die, a 20-sided die, a heads-or-tails indicator, and a stopwatch. The mode is selectable via a potentiometer, and the function is triggered using a button.
Background Knowledge:
What you'll need:
1 - 9 Volt Battery
1 - 9 Volt Battery Harness
1 - Arduino
1 - PBNO
1 - 100 K-Ohm Potentiometer
1 - TM1637 Display
1 - 6"x12" 1/8 MDF
1 - Laser Cutter/Engraver
1 - Soldering Pen
Building the Project
Step One
Understanding how to read the schematic for your HT16K33 display project is essential. It serves as a blueprint, detailing the connections and components needed, such as resistors, capacitors, buttons, and the display itself. By interpreting the schematic correctly, you can ensure accurate wiring and component placement, preventing common errors and ensuring your project functions as intended.
Step Two
Building a breadboard prototype is a critical step in bringing your HT16K33 display project to life. It allows you to test and verify your circuit design before finalizing it. Using a breadboard helps you easily connect and adjust components, ensuring that everything works correctly without the need for soldering. This hands-on process also aids in troubleshooting and refining your project.
Mini-Lesson: Understanding Multiplexing for the TM1637 Display
Multiplexing is a technique used to manage multiple signals over a shared medium or device, such as a TM1637 display, which is a 4-digit 7-segment display module. This method allows for efficient control of multiple segments without the need for individual wiring for each LED segment.
How Multiplexing Works in TM1637
The TM1637 display consists of four 7-segment digits, and each segment of each digit can be controlled independently. However, instead of having separate control lines for each segment, the TM1637 uses multiplexing to control all segments with fewer pins.
Shared Control Lines:
The TM1637 display has two main control lines: CLK (clock) and DIO (data input/output). These lines are used to send data to the display module, which then handles the multiplexing internally.
Time Division Multiplexing (TDM):
The display works by rapidly cycling through each of the four digits, turning each digit on and off in quick succession. This cycling happens so fast that the human eye perceives all digits as being on simultaneously. Due to the persistence of vision, where the eye retains an image for a fraction of a second after the object is gone, the rapid on-off cycling of each digit creates the illusion of a stable, continuous display.
Segment Control:
For each digit, the TM1637 controller sends a set of data that determines which segments of that digit should be lit. The controller sends data to each digit one at a time, enabling the correct segments for each digit in turn.
Data Transmission:
Data is transmitted serially to the TM1637 display. Each digit is addressed in turn, and the data for that digit’s segments is sent. The CLK line is used to synchronize the transmission, and the DIO line carries the actual segment data.
Example of Multiplexing in TM1637
Initialization:
The TM1637 module is initialized by setting the brightness and starting communication using the CLK and DIO pins.
TM1637Display display(CLK, DIO);
display.setBrightness(0x0f); // Max brightness
Sending Data:
The microcontroller sends data to the display. For example, to display the number 1234, the microcontroller will send data for each digit.
uint8_t digits[] = {0x06, 0x5b, 0x4f, 0x66}; // Representing 1, 2, 3, 4
display.setSegments(digits);
Updating Display:
The display controller updates each digit in sequence. While the first digit is being updated, the others are temporarily turned off. This process repeats for each digit. The setSegments function takes an array of segment data and updates the display accordingly.
Benefits of Multiplexing
Reduced Pin Count: By multiplexing, fewer pins are needed on the microcontroller to control multiple segments, as all segments share the same control lines.
Simplified Wiring: The use of multiplexing simplifies the wiring needed to connect the display to the microcontroller.
Cost Efficiency: Fewer components are required, reducing the overall cost of the project.
In summary, multiplexing in the TM1637 display enables efficient control of multiple 7-segment digits with minimal wiring and pin usage, providing a seamless and effective way to display numeric data.
Step Three
Laser cut and assemble your finger joint box. Make the dimensions large enough to fit an Arduino, 9v battery, and the other components. Most simple threaded components like the Potentiometer and button require a hole just over a 1/4" to fit through. Measure your HT16K33 display to place a slot in the middle. Be sure to check the tolerances of your laser cut to make sure these holes are not too tight or loose. If you want, add symbols for your dial to point to to know the mode types ahead of time.
Step Four
Assembling your Game Box project with an Arduino in real life is an exciting step that brings your design from concept to reality. Carefully follow the schematic to connect components to the Arduino, ensuring each wire and part is correctly placed. This hands-on assembly helps verify that your circuit functions as intended and allows for real-time testing and debugging. It's a rewarding process that turns your theoretical knowledge into a working prototype.
Step Five
Writing a short code using Tinkercad's block coding or Arduino's IDE is an essential step to bring your project to life. In Tinkercad, you can use the intuitive block coding interface to drag and drop commands, making it easy to create a program that controls your display. Alternatively, using Arduino's IDE, you can write a concise and efficient code in C++, allowing for more advanced functionality and customization. This coding step ensures your components interact correctly and your project operates as intended. A completed version of the code is shown below.
Step Six
Assembling the box and placing the components inside is a crucial step to finalize your Game Box project. Carefully position each component within the enclosure according to your layout plan, ensuring the Arduino, display, buttons, and other parts fit snugly. Use hot glue to securely attach components in place, preventing movement and potential disconnections. This step not only protects your circuit but also gives your project a polished, professional appearance.
Variants
Staying flexible with variants of similar parts in your project is important for several reasons:
Cost: Different parts can vary significantly in price. Being open to using similar, more affordable components can help you stay within budget without compromising functionality.
Availability: Component availability can fluctuate due to supply chain issues or stock levels. Flexibility allows you to substitute parts when the exact component is unavailable, ensuring your project can proceed without delays.
Compatibility: Some parts may offer better compatibility with your existing components or design. Flexibility in choosing similar parts can lead to easier integration and more efficient performance.
Upgradability and Future Proofing: Staying open to using different parts can make it easier to upgrade or modify your project in the future. It ensures that you are not locked into a single component that may become obsolete or harder to source.
Innovation and Learning: Exploring different parts and their functionalities can lead to better solutions and new ideas. It enhances your problem-solving skills and broadens your knowledge of available technologies.
Overall, flexibility in component selection helps ensure the success and longevity of your projects by adapting to cost, availability, and compatibility considerations
When switching from the HT16K33 backpack to another clock display module, the build and coding processes will slightly differ. The new display may have different pin configurations, requiring adjustments in wiring and connections. Additionally, the code will need to be updated to accommodate any new libraries or functions specific to the new display module. This might involve changes in initialization, segment control, and display updates. Understanding the datasheet and documentation for the new display is crucial to ensure proper integration and functionality within your project.
TM1637 Game Box Code
#include <TM1637Display.h>
// Define the connections pins for the TM1637
#define CLK 3
#define DIO 2
// Define the button pin
const int buttonPin = 4; // Main button
// Define the potentiometer pin
const int potPin = A0;
// Create an instance of the TM1637 display
TM1637Display display(CLK, DIO);
// Modes
enum Mode {
MODE_6_SIDED_DIE,
MODE_20_SIDED_DIE,
MODE_HEADS_TAILS,
MODE_TIMER
};
Mode currentMode = MODE_6_SIDED_DIE;
bool stopwatchRunning = false;
unsigned long stopwatchStartTime = 0;
unsigned long countdownDuration = 180000; // Initial countdown duration: 3 minutes in milliseconds
unsigned long elapsedTime = 0;
void setup() {
// Initialize the display
display.setBrightness(0x0f); // Max brightness
// Initialize the button pin as input with pull-up resistor
pinMode(buttonPin, INPUT_PULLUP);
// Seed the random number generator
randomSeed(analogRead(0));
}
void loop() {
// Read the potentiometer value and map it to the mode range
int potValue = analogRead(potPin);
int modeIndex = map(potValue, 0, 1023, 0, 3);
currentMode = static_cast<Mode>(modeIndex);
// Display the mode (0, 1, 2, 3)
display.showNumberDec(modeIndex);
// Read the state of the button
int buttonState = digitalRead(buttonPin);
// Check if the button is pressed
if (buttonState == LOW) {
switch (currentMode) {
case MODE_6_SIDED_DIE:
rollDice(6);
break;
case MODE_20_SIDED_DIE:
rollDice(20);
break;
case MODE_HEADS_TAILS:
displayHeadsTails();
break;
case MODE_TIMER:
startTimer(countdownDuration);
break;
}
// Debounce delay
delay(200);
// Wait until the button is released to avoid multiple actions
while (digitalRead(buttonPin) == LOW) {
delay(10);
}
}
}
void rollDice(int sides) {
int roll = random(1, sides + 1);
display.showNumberDec(roll);
// Wait until either the button is pressed again or the potentiometer is adjusted
while (true) {
int potValue = analogRead(potPin);
int modeIndex = map(potValue, 0, 1023, 0, 3);
if (static_cast<Mode>(modeIndex) != currentMode || digitalRead(buttonPin) == LOW) {
return;
}
}
}
void displayHeadsTails() {
bool result = random(0, 2);
if (result) {
displayHead(); // Display "HEAD"
} else {
displayTail(); // Display "TAIL"
}
}
void displayHead() {
uint8_t head[4] = {
SEG_G | SEG_B | SEG_C | SEG_E | SEG_F, // H
SEG_A | SEG_F | SEG_G | SEG_E | SEG_D, // E
SEG_A | SEG_B | SEG_F | SEG_G | SEG_E | SEG_C, // A
SEG_A | SEG_B | SEG_F | SEG_E | SEG_C | SEG_D, // D
};
display.setSegments(head);
}
void displayTail() {
uint8_t tail[4] = {
SEG_G | SEG_F | SEG_E | SEG_D, // t
SEG_A | SEG_B | SEG_F | SEG_G | SEG_E | SEG_C , // A
SEG_B | SEG_C , // I
SEG_F | SEG_E | SEG_D , // L
};
display.setSegments(tail);
}
void startTimer(unsigned long countdownDuration) {
// Calculate countdown duration based on potentiometer value
stopwatchRunning = true;
stopwatchStartTime = millis();
elapsedTime = 0;
while (stopwatchRunning) {
unsigned long currentTime = millis() - stopwatchStartTime;
// Calculate remaining time
unsigned long remainingTime = countdownDuration - currentTime;
// Calculate minutes and seconds
unsigned int minutes = (remainingTime / 60000) % 60;
unsigned int seconds = (remainingTime / 1000) % 60;
// Display the time in MM:SS format
uint8_t timeDisplay[4];
timeDisplay[0] = display.encodeDigit(minutes / 10); // Tens of minutes
timeDisplay[1] = display.encodeDigit(minutes % 10); // Units of minutes
timeDisplay[2] = display.encodeDigit(seconds / 10); // Tens of seconds
timeDisplay[3] = display.encodeDigit(seconds % 10); // Units of seconds
// Set colon ':' between minutes and seconds
timeDisplay[1] |= 0x80; // Add colon to the second digit (units of minutes)
display.setSegments(timeDisplay);
delay(200); // Update display every 200 milliseconds
}
}