Introduction: Pocket-Sized Simon Game With Arduino

Remember the game of Simon? This classic electronic game released by Hasbro in 1978 is still available in many Walmarts, just to give you an idea of how popular this game has been.

Follow my instructions, and you shrink this game into a pocket-sized fun box, complete with light and sound effects. To make this game, you will need some supplies and tools found in homes of typical makers:

Supplies

  • Arduino Uno or compatible clone
  • Breadboard
  • Soldering Tools
    • Iron
    • Solder
    • Solder Wick for when you make mistakes
  • Electronic Components
    • Veroboard
    • Push Buttons
    • Resistors of Various Ratings
    • Colorful LEDs
    • Piezo Buzzer
    • 9V Battery
    • 9V Battery Connector
    • Breakaway header pins
  • 3D Printer (Optional)

Step 1: Program Your Arduino

This is what makes the game of Simon tick. I used a Finite States Machine approach to specify its functions. The machine follows the process of Standby > Make a New Command > Get Input > Advance or Fail. If the user fails, a failure tone plays, and the program resets to Standby state. It is also interesting to note that Arduino Uno does not have a true RNG, so the first game will always be the same sequence. It does become different after a few games.

Download and include the Bounce2 library which handles inputs, and flash it to your Arduino.

#include <Bounce2.h>

//Game Parameters to Change Difficulty
int showLength = 500;


//Pin Assignments
int ledPin1 = 11;
int ledPin2 = 12;
int ledPin3 = 13;

int ledState1 = LOW;
int ledState2 = LOW;
int ledState3 = LOW;


int buttonPin1 = 7;
int buttonPin2 = 9;
int buttonPin3 = 8;

int piezoPin = 10;



//Debouncers Instances
Bounce debouncer1 = Bounce();
Bounce debouncer2 = Bounce();
Bounce debouncer3 = Bounce();


//Controller Variables
int cmds [128];
int cmdIndex = 0;
int diffLvl = 0;
int state = 0; //0 is standby, 1 is generating and displaying, 2 is taking input, 3 is game over



void makeNewCmd(){
cmds[diffLvl] = random(1,4);
}

void flashAll(){
digitalWrite(ledPin1, HIGH);
digitalWrite(ledPin2, HIGH);
digitalWrite(ledPin3, HIGH);
delay(showLength);
digitalWrite(ledPin1, LOW);
digitalWrite(ledPin2, LOW);
digitalWrite(ledPin3, LOW);
delay(showLength);

}


void showNum(int number){
switch(number){
case 1:
digitalWrite(ledPin1, HIGH);
tone(piezoPin, 523);
break;

case 2:
digitalWrite(ledPin2, HIGH);
tone(piezoPin, 587);
break;


case 3:
digitalWrite(ledPin3, HIGH);
tone(piezoPin, 659);
break;

}

delay(showLength);
noTone(piezoPin);
digitalWrite(ledPin1, LOW);
digitalWrite(ledPin2, LOW);
digitalWrite(ledPin3, LOW);
delay(showLength/2);
}

//Light and sound code
void showCmds(int cmdArr[128]){
for (int i = 0; i<=diffLvl; i++){
digitalWrite(ledPin1, LOW);
digitalWrite(ledPin2, LOW);
digitalWrite(ledPin3, LOW);

showNum(cmdArr[i]);
}

flashAll();
}


void startAnim(){
digitalWrite(ledPin1, HIGH);
tone(piezoPin, 523);
delay(showLength/2);

digitalWrite(ledPin2, HIGH);
tone(piezoPin, 659);
delay(showLength/2);

digitalWrite(ledPin3, HIGH);
tone(piezoPin, 784);
delay(showLength/2);

digitalWrite(ledPin1, LOW);
digitalWrite(ledPin2, LOW);
digitalWrite(ledPin3, LOW);

tone(piezoPin, 1047);
delay(showLength);
noTone(piezoPin);
delay(showLength);

}

void endAnim(){
for (int i = 1000; i>600; i--){
tone(piezoPin, i);
delay(8);
}
noTone(piezoPin);
for (int i = 0; i<500; i++){
digitalWrite(piezoPin, LOW) ;
delayMicroseconds(random(1000,3000)) ;
digitalWrite(piezoPin, HIGH) ;
delayMicroseconds(random(1000,3000)) ;
}




}





bool getSequence(){
bool buttonState1 = false;
bool buttonState2 = false;
bool buttonState3 = false;

int inputVal = 0;


for (int i = 0; i<=diffLvl; i++){

while(true){
debouncer1.update();
debouncer2.update();
debouncer3.update();
buttonState1 = debouncer1.fell();
buttonState2 = debouncer2.fell();
buttonState3 = debouncer3.fell();

if (buttonState1 or buttonState2 or buttonState3){
break;
}
}

if (buttonState1){
inputVal = 1;
}
else if (buttonState2){
inputVal = 2;
}
else if (buttonState3){
inputVal = 3;

}

showNum(inputVal);

if (inputVal == cmds[i]){//Correct input, advnance to next command
}
else{ // Wrong input, play failure tone, return False to indicate failure.
return false;
}
}

return true;

}




void setup() {
// put your setup code here, to run once:

debouncer1.attach(buttonPin1,INPUT_PULLUP);
debouncer1.interval(25);
debouncer2.attach(buttonPin2,INPUT_PULLUP);
debouncer2.interval(25);
debouncer3.attach(buttonPin3,INPUT_PULLUP);
debouncer3.interval(25);


pinMode(ledPin1,OUTPUT);
pinMode(ledPin2,OUTPUT);
pinMode(ledPin3,OUTPUT);
pinMode(piezoPin,OUTPUT);


}

void loop() {
// put your main code here, to run repeatedly:
debouncer1.update();
debouncer2.update();
debouncer3.update();

switch(state){
case 0: // Standby
diffLvl = 0;


if(debouncer1.fell() or debouncer2.fell() or debouncer3.fell()){

startAnim();
state = 1;
}
break;

case 1: //Make new command and display

makeNewCmd();
showCmds(cmds);

state = 2;
break;


case 2://Get player input
if(!getSequence()){//An Incorrect entry is made
state = 3;
break;
}
diffLvl ++;
flashAll();
state = 1;
break;

case 3://Game Over
endAnim();
state = 0;

}

}

Step 2: Making the Circuit Board

The circuitry of Simon is very simple, especially with the help of Arduino's integrated hardware. As shown in the diagram, the pins 11, 12, 13 are configured as INPUT_PULLUP, and is pulled LOW when buttons 1, 2, 3 are depressed. The pins 7,8,9 are digital outputs, powering color LEDs through a 220 ohm resistor to limit their currents, Pin 19 is configured as digital output to drive a Piezo buzzer to provide sound effects.

I first connected the circuit using a breadboard. This is useful for debugging the project, but it is not necessary to follow. If you feel confident, you can go straight to making the veroboard circuit. Plenty of sources can teach you about the art of using these versatile boards, but I'll just tell you the very basics. The strips on the veroboard form one big electrical node, and can be broken up into smaller, separate nodes by breaking the strip using a drill bit (3mm). When you're satisfied with the circuit, solder components onto it as with regular through-hole PCB's. You might have realized that I used the veroboard upside down, where the buttons and LED's were soldered as if they were SMD components. This is because the pins that connect to the Arduino need to be inserted from the other side, leaving me with only the back side to work with. This is not ideal, but can be done with some care taken.


When finished with soldering, test fit the circuit board onto the Arduino, and run the code provided. The Simon game should now function on USB power! In the next step, I will design a Pocket-Sized enclosure for this project, and add wiring to run the Arduino on a 9V battery.

Step 3: Designing and Printing the Shell

With the electronics finished, now let's give Simon a body. I used Solidworks to make a low-detail model for my circuit board, mating it to an Arduino model and 9V battery to establish dimensions of the shell. The shell has integrated flexing leaves so that you can press the buttons. The gaps in the leaves also let the LED lights shine through.

Step 4: Assemble!