Improved 'Simon Says' Code

5,470

12

19

Introduction: Improved 'Simon Says' Code

About: Professional Software, Hardware, Systems Engineer for more than 40 years. Amateur Radio HAM (KI7NEW)

An updated 'Simple Simon' project.

Specifically, easier to work with software implementation.

Step 1: Getting Started

I started with the instructable at 'Simple-Simon-Says-Game'

Refer to it for general hardware implementation.

As I already had done a project of my own which had 4 buttons, 4 LEDs and a speaker, I used that hardware (seen above). I touched up the photos a little to change some aspects to be more appropriate for illustration in this project.

It uses an Nano 3.0 and I used different pin assignments for the peripherals.

By the way, you may also be interested in a hardware simplified version I did Instructables.com/id/Fast-Easy-Simon

Step 2: Adapting to New Hardware

The Simon_Says sketch did not work for me as the code relied on fix pin assignments. Further the code was hard to follow and had some bugs.

So I created this updated version.

  • Fixed the miss use of type 'boolean' for pin numbers
  • Modified the code so that it will work for any LED & button pin assignments.
  • The logic flow was overly complicated and in spots too repetitive, causing it to be hard to understand and debug. So I simplified it for the most part.
  • Along with other improvements like the fact that I changed the 'Tone' class object's name from 'speakerpin' to 'speaker' and created a byte variable 'speakerpin' for its pin # assignment.
  • Oct 2015: allowed for fast button input sequences
  • Aug 2021:Made start patern real world random

A download link for my updated sketch is included here. You should find it easy to get going with your own hardware by simply changing pin assignments near the beginning of the code. Download and have fun with it.

I have also created a related Tinkercad Simulation.

Step 3: By Popular Demand

As there has been queries about how to use the software with a servo to operate a latch when a successful set of Simon pattern matching is done. I am including here versions of 'Simon_Says' as well as the similar 'Simon_Sings' which I adapted to be suitable for further modification with appropriate servo code. I have placed '@TODO' comments in the code of each showing where to put one's servo code.There is also an include file they rely on 'toneNotes.h'

As of Aug 2021 'Simon_Says_Open' (v2) no longer uses the 'Tone' object library, which conflicted with the Servo library.

The exact coding will be dependent on the hardware implementation and the servo library one uses. I do not have answers for servo particular issues. For that, I recommend reviewing the likes of the following:
video: How to control servos
instructables.com/id/Arduino-Servo-Motors/
instructables.com/id/Access-control-with-Arduino-Keypad-4x4-Servo/
For those needing further servo related help, I recommend a post to the servo related instructable with the most similarities to their servo implementation.

1 Person Made This Project!

Recommendations

  • Fix It Speed Challenge

    Fix It Speed Challenge
  • Jewelry Challenge

    Jewelry Challenge
  • One Board Contest

    One Board Contest

19 Comments

0
dannew
dannew

4 months ago

Ron, this is great! I finally got a Simon Says design to work, thanks to your instructions. I didn't have a capacitor (they come with the Arduino Starter Kit but not the Elegoo) nor an inductor (not in either kit), but I got my speaker to work by connecting positive to Pin 8 and the other to GND. In your setup, do you not need a ground?

I'd love to use your Simon Says Open variation, but I'm stumped at the start, as there seems to be a conflict with the Tone and Servo libraries, returning this error:
-------
libraries/Servo/avr/Servo.cpp.o (symbol from plugin): In function `ServoCount':
(.text+0x0): multiple definition of `__vector_11'
libraries/Tone/Tone.cpp.o (symbol from plugin):(.text+0x0): first defined here
collect2: error: ld returned 1 exit status
exit status 1
-------


or, if I just load the Servo library and add no additional code, this:

---------
libraries/Tone/Tone.cpp.o (symbol from plugin): In function timer0_toggle_count': (.text+0x0): multiple definition of __vector_11'
libraries/Servo/avr/Servo.cpp.o (symbol from plugin):(.text+0x0): first defined here
collect2: error: ld returned 1 exit status
exit status 1
---------


Is there a beginner-friendly way to fix that?

0
RonM9
RonM9

Reply 4 months ago

Ok, Dan, I have updated this Instructable with 'Simon_Says_Open_v2' which has been modified to longer use the 'Tone' object library, but instead use the builtin 'tone()' function. As such it does not conflict with the 'Servo' library.
Let me know when you get your project working. ;~}

0
dannew
dannew

Reply 4 months ago

This is great, Ron! Thanks to your updated code, I was able to get this up and running. It's been a great first Arduino project for me, and I'm excited to try some of your others. GIF of my Simon Says box attached here.
Thanks again,
Dan

dangerous art box.gif
0
RonM9
RonM9

Reply 4 months ago

All Smiles here.
Dan ,would you please post, that great GIF, as part of an "I made it" post, so others will readily see it. :~}

0
dannew
dannew

Reply 4 months ago

Sure thing! Thanks again, Ron. This was a great introduction to the Arduino world.

0
RonM9
RonM9

Reply 4 months ago

Yes, I normally connect one side of a piezo sounder (or speaker) to ground. An added inductor is of very limited use. An inline capacitor (0.1-100uf), for isolation, is a good idea but not required. Just don't leave the audio out pin High.

0
RonM9
RonM9

Reply 4 months ago

It appears that the Servo and Tone object libraries both use a same interrupt in a conflicting way, as they are written.
You could rewrite one of them. Nah.
You could use my supplied 'Simon_Sings_Open' which does not use any object libraries (but a simple .h include) and compiles along with Servo.h included.
Simon_Sings operates a bit different than does Simon_Says. If you are OK with that then great.
If not you'll need to modify Simon_Sings to your liking. Or modify Simon_Says to produces audio notes like Simon_Sings does (with pitches.h & the now builtin tone() function, not 'Tone' methods). This is probably the easiest, if Simon_Sings won't do as is.
Keep me updated. All my best to you & your efforts, Ron

0
RonM9
RonM9

Reply 4 months ago

Not sure. Unfortunately those error messages are not much help.
First of all, when ever you get error messages, look all the way back at the first error being reported, and focus on resolving that alone.
Can you scroll back and see a different earliest error message?
I'll see if I can sort out a clue here.

0
MerkMan99
MerkMan99

4 years ago

I did this project but I am getting an error on:

Tone speaker;

saying that 'Tone' does not name a type

Thank You

0
RonM9
RonM9

Reply 4 years ago

Hi, I am happy to hear that you have done this project.
This is apparently not an uncommon problem. For help with this, please see my post of 2016-01-18 below.
Looking forward to hearing of your further success; Best Ron
PS: You may find that you're interested in checking out my other projects.

0
patrickevelien
patrickevelien

6 years ago

hello

i have it working..thanks for that, i want to use this game as a lock..

so when the player has it 6 times good i want to open a servo motor but i dont know how to do it.. can you please help me? thanks

0
RonM9
RonM9

Reply 6 years ago

OK. You would need to add code like that below after the line "Serial.print(turn);"

For the coded needed to operate your servo find a project which uses that same hardware-electronics to use as an example.

You may want to use the version 'FastEasy_Simon_Says.ino' of a Simon type game found in https://www.instructables.com/id/Fast-Easy-Simon/ , as among other differences it waits for an input before proceeding and starts off with a pattern of 4 instead of 1.

if (turn==6) {
// !! Here place the code needed to operate the servo latch !!
bool btnPushed = false;
while (! btnPushed) { // wait for a key-press before starting all over
for (int y = 0; y < 4; y++)
{ if (digitalRead(button[y]) == LOW) //Checking for button push
btnPushed = true; }
}
turn = -1; //Reset turn value so the game starts over
exit;
}

0
RonM9
RonM9

Reply 6 years ago

Referrence above to 'FastEasy_Simon_Says.ino'

Should be: 'FastEasy_Simon_Sings.ino'

0
patrickevelien
patrickevelien

Reply 6 years ago

i did try for a full day to put a servo in...

im a beginner with arduino is it possible that you write it into the arduino code?

this is the code.. thanks 1000 times.

i want the servo on pin 7..

#include <Tone.h>

/*Simon Says game. Now with sound effects.

Originaly made by Robert Spann

Code trimmed and sound effects added by digimike

Ron Miller (aka RonM9) Jun 2015: Removed fixed pin
assignment dependences and simplified code

RRM Oct 2015: allowed for faster button inputs

Buttons are to be set on there designated pins without pull
down resistors

and connected to ground rather then +5.

*/

#include "Tone.h"

Tone speaker;

byte speakerPin = 12; // speaker was on pin 13

int starttune[] = {NOTE_C4, NOTE_F4, NOTE_C4, NOTE_F4,
NOTE_C4, NOTE_F4, NOTE_C4, NOTE_F4, NOTE_G4, NOTE_F4, NOTE_E4, NOTE_F4,
NOTE_G4};

int duration2[] = {100, 200, 100, 200, 100, 400, 100, 100,
100, 100, 200, 100, 500};

int cheer[] = {NOTE_C4, NOTE_C4, NOTE_G4, NOTE_C5, NOTE_G4,
NOTE_C5};

int btnNote[] = {NOTE_G3, NOTE_A3, NOTE_B3, NOTE_C4};

int duration[] = {100, 100, 100, 300, 100, 300};

//boolean button[] = {2, 3, 4, 5}; //The four button input
pins

//boolean ledpin[] = {8, 9, 10, 11}; // LED pins

int button[] = {8, 9, 10, 11}; //The four button input pins
oranje draad

int ledpin[] = {2, 3, 4, 5};
// LED pins geel draad

int turn = 0; // turn
counter

int buttonstate = 0;
// button state checker

int randomArray[100]; //Intentionally long to store up to
100 inputs (doubtful anyone will get this far)

int inputArray[100];

// ===================================================

void setup()

{

int note;

Serial.begin(9600);


speaker.begin(speakerPin);

for (int x = 0; x
< 4; x++) // LED pins are outputs

{

pinMode(ledpin[x],
OUTPUT);

pinMode(button[x],
INPUT); // button pins are inputs


digitalWrite(button[x], HIGH); //
enable internal pullup; buttons start in high position; logic reversed

}


randomSeed(analogRead(0)); //Added to generate "more randomness"
with the randomArray for the output function

for (int thisNote =
0; thisNote < 13; thisNote ++) {

// play the next
note:

note =
starttune[thisNote];


speaker.play(note);

if (note ==
NOTE_C4) digitalWrite(ledpin[0],
HIGH);

else if (note ==
NOTE_F4) digitalWrite(ledpin[1], HIGH);

else if (note ==
NOTE_G4) digitalWrite(ledpin[2], HIGH);

else if (note ==
NOTE_E4) digitalWrite(ledpin[3], HIGH);

// hold the note:


delay(duration2[thisNote]);

// stop for the
next note:

speaker.stop();

allLEDs(LOW);

delay(25);

}

delay(1000);

}

// ========================================================

void loop() // This
'loop' is automatically ran repeatedly

{

for (int level = 0;
level <= 99; level++)

{

allLEDs(HIGH);

for (int thisNote
= 0; thisNote < 6; thisNote ++) {

// play the next
note:


speaker.play(cheer[thisNote]);

// hold the
note:


delay(duration[thisNote]);

// stop for the
next note:

speaker.stop();

delay(25);

}

allLEDs(LOW);

delay(1000);


Serial.print("\n Turn: ");
// Some serial output to follow along


Serial.print(turn);

// fill up the
array to be matched by the player

randomArray[turn]
= random(1, 5); //Assigning a random number (1-4) to the randomArray[turn
count]


Serial.print(" Rand#:
");


Serial.println(randomArray[turn]);

for (int x = 0; x
<= turn; x++)

{


Serial.print(randomArray[x]);

byte btn =
randomArray[x]; // logical button/led #
(1-4)


digitalWrite(ledpin[btn - 1], HIGH);


speaker.play(btnNote[btn - 1], 100);

delay(400);


digitalWrite(ledpin[btn - 1], LOW);

delay(100);

}

input();

}

}

// ===================================================

// local
functions

void input() { //Function for allowing user input and
checking input against the generated array

bool btnPushed;

for (int x = 0; x
<= turn; x++)

{ // for each one in
the current sequence wait for a button to be pushed

// if it is the
correct one we keep looping through the sequence

btnPushed = false;

while (!
btnPushed) {

for (int y = 0;
y < 4; y++)

{

buttonstate =
digitalRead(button[y]);

byte btn = y +
1; // logical button # (1-4)

if
(buttonstate == LOW) //Checking for
button push

{

btnPushed =
true;


digitalWrite(ledpin[y], HIGH);


speaker.play(btnNote[btn - 1], 100);


delay(100); // insures minimum
LED illumination


inputArray[x] = btn;


Serial.print(" btn pushed:
");


Serial.print(btn);

// was a poor way to allow for button release: delay(250);


wait_BtnRelease();


digitalWrite(ledpin[y], LOW);

if
(inputArray[x] != randomArray[x]) { //Checks value input by user and checks it
against


fail();
//the value in the same spot on the generated array

//The fail
function is called if it does not match

exit;

}

}

}

} // end while

}

delay(500);

turn++; //Increments
the turn count, also the last action before starting the output function over
again

}

// ------------------------------------------------------

void fail() { //Function used if the player fails to match
the sequence

for (int y = 0; y
<= 2; y++)

{ //Flashes lights
for failure

allLEDs(HIGH);


speaker.play(NOTE_G3, 300);

delay(200);

allLEDs(LOW);


speaker.play(NOTE_C3, 300);

delay(200);

}

delay(500);

turn = -1; //Resets
turn value so the game starts over without need for a reset button

}

// ------------------------------------------------------

void allLEDs(byte state) {


digitalWrite(ledpin[0], state);

digitalWrite(ledpin[1],
state);


digitalWrite(ledpin[2], state);


digitalWrite(ledpin[3], state);

}

void wait_BtnRelease() {

bool
btnStillDown=true;

while
(btnStillDown) {

btnStillDown =
false;

for (int y = 0;
y < 4; y++)

{

buttonstate =
digitalRead(button[y]);

if
(buttonstate == LOW) //Checking for
button push

btnStillDown
= true;

}

}

}

0
RonM9
RonM9

Reply 5 years ago

Patrick, did you ever get your project working?

Since our last communication I have added a section to this instructable, titled 'By popular demand'. You may find this a little more helpful &/or interesting.

0
OndřejT4
OndřejT4

Reply 5 years ago

Hi...i am trying to add same servo function like patrickevelien. When I put code to servo open/close right behind void setup(), servo operate correctly and after that game starts, But when I put code behind if (turn==6) { nothing happend. When I put here for example allLEDs(HIGH); , this works. Can you give me some advice please?

/*Simon Says game. Now with sound effects.

Originaly made by Robert Spann

Code trimmed and sound effects added by digimike

Ron Miller (aka RonM9) Jun 2015: Removed fixed pin assignment dependences and simplified code

RRM Oct 2015: allowed for faster button inputs

Buttons are to be set on there designated pins without pull down resistors

and connected to ground rather then +5.

*/

#include <Tone.h>

Tone speaker;

#include <ServoTimer2.h>

ServoTimer2 a;

byte speakerPin = 12; // speaker was on pin 13

int starttune[] = {NOTE_C4, NOTE_F4, NOTE_C4, NOTE_F4, NOTE_C4, NOTE_F4, NOTE_C4, NOTE_F4, NOTE_G4, NOTE_F4, NOTE_E4, NOTE_F4, NOTE_G4};

int duration2[] = {100, 200, 100, 200, 100, 400, 100, 100, 100, 100, 200, 100, 500};

int cheer[] = {NOTE_C4, NOTE_C4, NOTE_G4, NOTE_C5, NOTE_G4, NOTE_C5};

int btnNote[] = {NOTE_G3, NOTE_A3, NOTE_B3, NOTE_C4};

int duration[] = {100, 100, 100, 300, 100, 300};

//boolean button[] = {2, 3, 4, 5}; //The four button input pins

//boolean ledpin[] = {8, 9, 10, 11}; // LED pins

int button[] = {5, 6, 7, 8}; //The four button input pins

int ledpin[] = {2, 3, 17, 16}; // LED pins

int turn = 0; // turn counter

int buttonstate = 0; // button state checker

int randomArray[100]; //Intentionally long to store up to 100 inputs (doubtful anyone will get this far)

int inputArray[100];

// ===================================================

void setup()

{

a.attach(9);

a.write(2000);

delay(2000);

a.write(1000);

delay(2000);

int note;

Serial.begin(9600);

speaker.begin(speakerPin);

for (int x = 0; x < 4; x++) // LED pins are outputs

{

pinMode(ledpin[x], OUTPUT);

pinMode(button[x], INPUT); // button pins are inputs

digitalWrite(button[x], HIGH); // enable internal pullup; buttons start in high position; logic reversed

}

randomSeed(analogRead(0)); //Added to generate "more randomness" with the randomArray for the output function

for (int thisNote = 0; thisNote < 13; thisNote ++) {

// play the next note:

note = starttune[thisNote];

speaker.play(note);

if (note == NOTE_C4) digitalWrite(ledpin[0], HIGH);

else if (note == NOTE_F4) digitalWrite(ledpin[1], HIGH);

else if (note == NOTE_G4) digitalWrite(ledpin[2], HIGH);

else if (note == NOTE_E4) digitalWrite(ledpin[3], HIGH);

// hold the note:

delay(duration2[thisNote]);

// stop for the next note:

speaker.stop();

allLEDs(LOW);

delay(25);

}

delay(1000);

}

// ========================================================

void loop() // This 'loop' is automatically ran repeatedly

{

for (int level = 0; level <= 99; level++)

{

allLEDs(HIGH);

for (int thisNote = 0; thisNote < 6; thisNote ++) {

// play the next note:

speaker.play(cheer[thisNote]);

// hold the note:

delay(duration[thisNote]);

// stop for the next note:

speaker.stop();

delay(25);

}

allLEDs(LOW);

delay(1000);

Serial.print("\n Turn: "); // Some serial output to follow along

Serial.print(turn);

// fill up the array to be matched by the player

if (turn==2) {

// HERE ADD SERVO OPEN/CLOSE

bool btnPushed = false;

while (! btnPushed) { // wait for a key-press before starting all over

for (int y = 0; y < 4; y++)

{ if (digitalRead(button[y]) == LOW) //Checking for button push

btnPushed = true; }

}

turn = -1; //Reset turn value so the game starts over

exit;

}

randomArray[turn] = random(1, 5); //Assigning a random number (1-4) to the randomArray[turn count]

Serial.print(" Rand#: ");

Serial.println(randomArray[turn]);

for (int x = 0; x <= turn; x++)

{

Serial.print(randomArray[x]);

byte btn = randomArray[x]; // logical button/led # (1-4)

digitalWrite(ledpin[btn - 1], HIGH);

speaker.play(btnNote[btn - 1], 100);

delay(400);

digitalWrite(ledpin[btn - 1], LOW);

delay(100);

}

input();

}

}

// ===================================================

// local functions

void input() { //Function for allowing user input and checking input against the generated array

bool btnPushed;

for (int x = 0; x <= turn; x++)

{ // for each one in the current sequence wait for a button to be pushed

// if it is the correct one we keep looping through the sequence

btnPushed = false;

while (! btnPushed) {

for (int y = 0; y < 4; y++)

{

buttonstate = digitalRead(button[y]);

byte btn = y + 1; // logical button # (1-4)

if (buttonstate == LOW) //Checking for button push

{

btnPushed = true;

digitalWrite(ledpin[y], HIGH);

speaker.play(btnNote[btn - 1], 100);

delay(100); // insures minimum LED illumination

inputArray[x] = btn;

Serial.print(" btn pushed: ");

Serial.print(btn);

// was a poor way to allow for button release: delay(250);

wait_BtnRelease(); // much better this way

digitalWrite(ledpin[y], LOW);

if (inputArray[x] != randomArray[x]) { //Checks value input by user and checks it against

fail(); //the value in the same spot on the generated array

//The fail function is called if it does not match

exit;

}

}

}

} // end while

}

delay(500);

turn++; //Increments the turn count, also the last action before starting the output function over again

}

// ------------------------------------------------------

void fail() { //Function used if the player fails to match the sequence

for (int y = 0; y <= 2; y++)

{ //Flashes lights for failure

allLEDs(HIGH);

speaker.play(NOTE_G3, 300);

delay(200);

allLEDs(LOW);

speaker.play(NOTE_C3, 300);

delay(200);

}

delay(500);

turn = -1; //Resets turn value so the game starts over without need for a reset button

}

// ------------------------------------------------------

void allLEDs(byte state) {

digitalWrite(ledpin[0], state);

digitalWrite(ledpin[1], state);

digitalWrite(ledpin[2], state);

digitalWrite(ledpin[3], state);

}

void wait_BtnRelease() {

bool btnStillDown;

int debounce=0; // need depends on button used. mine caused some double trips

while (debounce<2) {

delay(5);

btnStillDown = false;

for (int y = 0; y < 4; y++)

{

buttonstate = digitalRead(button[y]);

if (buttonstate == LOW) //Checking for button push

btnStillDown = true;

}

if (btnStillDown) debounce=0;

else debounce++;

}

}

0
RonM9
RonM9

Reply 5 years ago

Thanks for your interest in this instructable.

It looks like you have not placed the needed servo control code where this line is located: // HERE ADD SERVO ...

I also noticed you are using a different servo library than what I see others using; and your initialization code in setup() looks odd to me.

I have added a version of the .ino code with the idea of being modified for use to operate a latch, with better comments regarding placement of servo support code. Please see the added section to this instructable, titled 'By popular demand'. I hope this gets you going in the right direction.

Looking forward to hearing about your success.

0
MiguelD13
MiguelD13

6 years ago

C:\Users\user\Downloads\Compressed\Simon_Says\Tone\Tone\Simon_Says\Simon_Says.ino:9:18: fatal error: Tone.h: No such file or directory

#include <Tone.h>

^

compilation terminated.

exit status 1

Error compiling.

This report would have more information with

"Show verbose output during compilation"

enabled in File > Preferences.


Can you help me with this? TIA =)

0
RonM9
RonM9

Reply 6 years ago

It is not finding 'Tone.h'. So the compiler is looking in the wrong places, or the file is in the wrong place or it is missing. The Arduino IDE comes with an Examples/Tone/ToneTest sketch which uses Tone.h from Sketchbook/libraries/Tone. You probably need to reinstall the latest IDE to get this sketch as well as the ToneTest sketch to build.