SparkFun Inventor's Kit Experiment Guide - v4.1
Circuit 4C: DIY Who Am I? Game
"DIY Who Am I?" is based on the popular Hedbanz game or HeadsUp! app. It's a fun party game in which a player holds an LCD screen to his/her forehead so that the player canât see the word(s) that appear on the screen. Other players have to give hints, act out charades or make noises that will make the player with the LCD guess the word(s).
Parts Needed
Grab the following quantities of each part listed to build this circuit:
New Components
4xAA Battery Holder
Included in your kit is a 4-cell AA battery holder. The 5-inch cable is terminated with a standard barrel jack connector. The connector mates with the barrel jack on the RedBoard, allowing you to easily make your project battery powered.
New Concepts
Button Debounce
When working with momentary buttons, it is usually necessary to add button debouncing to your code. This is because the code that is meant to execute when the button is pressed may execute faster than you can press and release the button (microcontrollers are fast!). The simplest way to debounce a button is to add a small delay to the end of your code. This sketch adds a 500 millisecond delay at the end of loop()
to account for this. This simple addition will prevent a word from getting skipped when you press the button for the game.
For a more complex example of button debouncing, in the Arduino IDE click File > Examples > 02.Digital > Debounce.
Strings
Strings are used to print words and even sentences to an LCD or the Serial Monitor. Strings are actually just an array of characters with a null character at the end to let the program know where the end of the string is.
Arrays of Strings
In circuit 2A you used an array of characters to represent musical notes. In this program, youâll want to make an array of strings. Strings use multiple characters to make words, so youâll need to use a little trick to put them in an array. The trick is to use a pointer. When you declare your array, youâll use an asterisk after the char data type, as follows:
const char* arrayOfStrings = {âFeynmanâ âSaganâ, âTysonâ, âNyeâ};
Pointers
Pointers are an advanced programming topic. They can be difficult to understand the first time you're introduced to them. For now, think of pointers as a variable that "points" to the value contained in a certain address in memory. In this sketch, the char* variable points to arrayOfStrings address and returns the character values to create a list of strings.
Hardware Hookup
Polarized Components | Pay special attention to the componentâs markings indicating how to place it on the breadboard. Polarized components can only be connected to a circuit in one direction. |
Batteries are polarized. They have a positive end and a negative end. The battery holder has images indicating which end goes in which orientation for each cell.
Ensure all the batteries are inserted correctly before plugging the battery holder into the RedBoard.
Battery Holder Attachment
To attach the battery holder to the breadboard baseplate, first cut two strips of Dual Lock that are roughly 1 inch x 1 inch each, or 2.5cm x 2.5cm. Remove the adhesive backing and attach one piece to the back of the battery holder.
Adhere the second piece to the bottom of the breadboard baseplate (directly in the middle is recommended, as this will come into play in Project 5).
Last, press the battery holder to the baseplate so that the two pieces of Dual Lock snap together. Insert the batteries into the holder if you have not done so already. Remember that batteries are polarized and can only go in one way.
Remove the battery pack while building your circuit.
Ready to start hooking everything up? Check out the circuit diagram and hookup table below to see how everything is connected.
Circuit Diagram
Hookup Table
Component | RedBoard | Breadboard | Breadboard | Breadboard |
---|---|---|---|---|
Jumper Wire | 5V | 5V Rail ( + ) | ||
Jumper Wire | GND | GND Rail ( - ) | ||
LCD | A15-A30 (Pin 1 on A15) | |||
Jumper Wire | E30 | GND Rail ( - ) | ||
Jumper Wire | E29 | 5V Rail ( + ) | ||
Jumper Wire | Digital Pin 8 | E28 | ||
Jumper Wire | Digital Pin 9 | E27 | ||
Jumper Wire | Digital Pin 10 | E26 | ||
Jumper Wire | Digital Pin 11 | E25 | ||
Jumper Wire | Digital Pin 12 | E20 | ||
Jumper Wire | E19 | GND Rail ( - ) | ||
Jumper Wire | Digital Pin 13 | E18 | ||
Jumper Wire | E16 | 5V Rail ( + ) | ||
Jumper Wire | E15 | GND Rail ( - ) | ||
Potentiometer | A8 | A9 | A10 | |
Jumper Wire | E9 | E17 | ||
Jumper Wire | E8 | GND Rail ( - ) | ||
Jumper Wire | E10 | 5V Rail ( + ) | ||
Buzzer | G6 (Buzzer +) |
G8 (Buzzer -) |
||
Jumper Wire | Digital Pin 6 | J6 | ||
Jumper Wire | J8 | GND Rail ( - ) |
||
Push Button | D1/D3 | G1/G3 | ||
Jumper Wire | Digital Pin 2 | J1 | ||
Jumper Wire | J3 | GND Rail ( - ) |
Open the Sketch
To open the code, go to: File > Examples > SIK_Guide_Code-master > SIK_Circuit_4C-DIYWhoAmI
You can also copy and paste the following code into the Arduino IDE. Hit upload, and see what happens!
/* SparkFun Inventorâs Kit Circuit 4C - Heads Up Game This is a DIY version of the popular Heads Up party game. To play, one person resets the RedBoard and holds the LCD facing away from them so that they cannot see it (usually on their forehead). The display will show a short countdown then display random words. The other player(s) who can see the screen must yell out clues until time runs out or the player guesses what word is on the screen. If they guess correctly, they can press the button on the breadboard and another word will be displayed. This sketch was written by SparkFun Electronics, with lots of help from the Arduino community. This code is completely free for any use. View circuit diagram and instructions at: https://learn.sparkfun.com/tutorials/sparkfun-inventors-kit-experiment-guide---v41 Download drawings and code at: https://github.com/sparkfun/SIK-Guide-Code */ LiquidCrystal lcd(13, 12, 11, 10, 9, 8); // tell the RedBoard what pins are connected to the display int buttonPin = 2; //pin that the button is connected to int buzzerPin = 6; //pin for driving the buzzer int buttonPressTime = 0; //variable to show how much time the player has left to guess the word (and press the button) long timeLimit = 15000; //time limit for the player to guess each word long startTime = 0; //used to measure time that has passed for each word int roundNumber = 0; //keeps track of the roundNumber so that it can be displayed at the end of the game const int arraySize = 25; const char* words[arraySize] = {"moose", "beaver", "bear", "goose", "dog", "cat", "squirrel", "bird", "elephant", "horse", "bull", "giraffe", "seal", "bat", "skunk", "turtle", "whale", "rhino", "lion", "monkey", "frog", "alligator", "kangaroo", "hippo", "rabbit" }; // the start value in the sequence array must have a value that could never be an index of an array // or at least a value outside the range of 0 to the size of the words array - 1; in this case, it can't be between 0 to 24 int sequence[] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; //start with an array full of -1's void setup() { pinMode(buttonPin, INPUT_PULLUP); //set the button pin as an input lcd.begin(16, 2); //tell the LCD library the size of the screen generateRandomOrder(); //generate an array of random numbers from 0 to 24 that will determine which order the words are shown in showStartSequence(); //print the start sequence text } void loop() { for (int i = 0; i < arraySize; i++) { //for each of the 25 words in the sequence lcd.clear(); //clear off the array roundNumber = i + 1; //the array starts at 0, but the roundNumber will start counting from 1 lcd.print(roundNumber); //print the roundNumber (this is the current round number) lcd.print(": "); //spacer between the number and the word lcd.print(words[sequence[i]]); //print a random word from the word array startTime = millis(); //record the time that this round started while (digitalRead(buttonPin) == HIGH) { //do this until the button is pressed... int roundedTime = round((timeLimit - (millis() - startTime)) / 1000); //calculate the time left in the round (dividing by 1000 converts the number to seconds lcd.setCursor(14, 1); //set the cursor in the lower right corner of the screen lcd.print(" "); lcd.setCursor(14, 1); //set the cursor in the lower right corner of the screen lcd.print(roundedTime); //print the time left in the time limit delay(15); if (millis() - startTime > timeLimit) { //if the time limit is up before the button is pressed gameOver(); //end the game } if (digitalRead(buttonPin) == LOW) { tone(buzzerPin, 272, 10); //emit a short beep when the button is pressed } } //exit this loop when the button is pressed delay(500); //delay for a moment before going onto the next round, so that the button press doesn't get registered twice } //if you finish all 25 words winner(); //show the you win message } //--------------FUNCTIONS------------------------------ //DISPLAYS A COUNTDOWN TO START THE GAME void showStartSequence() { lcd.clear(); //clear the screen lcd.setCursor(0, 0); //move the cursor to the top left corner lcd.print("Category:"); //print "Category:" lcd.setCursor(0, 1); //move the cursor to the bottom left corner lcd.print("Animals"); //print "Animals:" delay(2000); //Wait 2 seconds lcd.clear(); //clear the screen lcd.print("Get ready!"); //print "Get ready!" delay(1000); //wait 1 second lcd.clear(); //clear the screen lcd.print("3"); //print "3" delay(1000); //wait 1 second lcd.clear(); //clear the screen lcd.print("2"); //print "3" delay(1000); //wait 1 second lcd.clear(); //clear the screen lcd.print("1"); //print "3" delay(1000); //wait 1 second } //GENERATES A RANDOM ORDER FOR THE WORDS TO BE DISPLAYED void generateRandomOrder() { randomSeed(analogRead(0)); //reset the random seed (Arduino needs this to generate truly random numbers for (int i = 0; i < arraySize; i++) { //do this until all 25 positions are filled int currentNumber = 0; //variable to hold the current number boolean match = false; //does the currentNumber match any of the previous numbers? //generate random numbers until you've generated one that doesn't match any of the other numbers in the array do { currentNumber = random(0, arraySize); //generate a random number from 0 to 24 match = false; //we haven't checked for matches yet, so start by assuming that it doesn't match for (int i = 0; i < arraySize; i++) { //for all 25 numbers in the array if (currentNumber == sequence[i]) { //does the currentNumber match any of the numbers? match = true; //if so, set the match variable to true } } } while (match == true); //if the match variable is true, generate another random number and try again sequence[i] = currentNumber; //if the match variable is false (the new number is unique) then add it to the sequence } } //GAME OVER void gameOver() { lcd.clear(); //clear the screen lcd.setCursor(0, 0); //move the cursor the top left corner lcd.print("Game Over"); //print "Game Over" lcd.setCursor(0, 1); //move to the bottom row lcd.print("Score: "); //print a label for the score lcd.print(roundNumber-1); //print the score (the score is equal to the previous level/round number) //play the losing fog horn tone(buzzerPin, 130, 250); //E6 delay(275); tone(buzzerPin, 73, 250); //G6 delay(275); tone(buzzerPin, 65, 150); //E7 delay(175); tone(buzzerPin, 98, 500); //C7 delay(500); while (true) {} //get stuck in this loop forever } //WINNER void winner() { lcd.clear(); //clear the screen lcd.setCursor(7, 0); //move the cursor to the top center of the screen lcd.print("YOU"); //print "You" lcd.setCursor(7, 1); //move the cursor to the bottom center of the screen lcd.print("WIN!"); //print "WIN!" //play the 1Up noise tone(buzzerPin, 1318, 150); //E6 delay(175); tone(buzzerPin, 1567, 150); //G6 delay(175); tone(buzzerPin, 2637, 150); //E7 delay(175); tone(buzzerPin, 2093, 150); //C7 delay(175); tone(buzzerPin, 2349, 150); //D7 delay(175); tone(buzzerPin, 3135, 500); //G7 delay(500); while (true) {} //get stuck in this loop forever }
What You Should See
The game will begin with a prompt telling you the category of words. Then it will run through a short countdown. When the first round starts, the word to be guessed will be displayed in the top left, and a countdown will be displayed in the bottom right of the LCD screen. Each time the button is pressed (before the timer expires) a new word will be displayed. If you win or lose, a short song will play and text will be displayed.
Program Overview
- Generate a random order for the words to be displayed.
- Show the starting countdown on the LCD.
- Start a loop that will run 25 times (there are 25 words total). For each round: a. Print the round number and the word to be guessed. b. Display a countdown timer in the lower right-hand corner of the screen that counts down the time limit for each round. c. If the time limit runs out, play the losing song, print "Game Over" and show the player's final score. d. If the player presses the button before the time limit is up, advance to the next word.
- If the player gets through all 25 words, play the winning song and print âYOU WIN!â
Code to Note
Code | Description |
---|---|
Array of Strings:const char* array_name [array_length] = | Makes an array of strings. The strings are stored as constants, so they canât be changed once the program starts. |
Rounding function:round(value_to_round); | This math function rounds a number up or down to the nearest whole number. |
Random Function:random(min, max); | The random function takes a set of numbers and generates a pseudo-random number from that set. |
Button Debounce:delay(500); | This 500 millisecond delay at the end of the loop adds button debounce so that erroneous button presses are not detected by the RedBoard. |
User Functions | Description |
generateRandomOrder(); | Makes an array that is a random ordering of the numbers from 1-25. This is used to display words for the game in a random order. |
showStartSequence(); | Shows the category of words on the LCD, then displays a countdown before the game starts. |
gameOver(); | Plays a sound and shows the text âGame Overâ along with the playerâs final score. |
winner(); | Shows the text âYOU WIN!â and plays a winning sound. |
Coding Challenges
Challenge | Description |
---|---|
Change the time limit | Changing the time limit variable will change the difficulty of the game. |
Change the words in the word list | Try changing the categories and words. The number of words in your words array must match the value of the variable âarraySizeâ. |
Change the winning and losing songs | By changing the tones in the winner() and gameover() functions you can change which song plays at the end of the game. |
Troubleshooting
Problem | Solution |
---|---|
The screen is blank or flickering | Adjust the contrast by twisting the potentiometer. If itâs incorrectly adjusted, you wonât be able to read the text. Also, check the potentiometer to make sure it's connected correctly. |
No sound is coming from the buzzer | Check the wiring from the buzzer. Make sure you are using the correct pin as defined in your code. You may add a potentiometer volume knob if you desire. |
The button doesn't work or words are getting skipped before they are guessed | If the button isn't working, check your wiring. If words are being skipped when the button is pressed, increase the debounce delay found at the end of the loop. It should be 500 milliseconds by default. Increasing this number by tiny increments will help with this problem. |