Introduction: Improved 'Simon Says' Code
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.
Attachments
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.
19 Comments
1 year 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?
Reply 1 year 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. ;~}
Reply 1 year 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
Reply 1 year 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. :~}
Reply 1 year ago
Sure thing! Thanks again, Ron. This was a great introduction to the Arduino world.
Reply 1 year 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.
Reply 1 year 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
Reply 1 year 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.
5 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
Reply 5 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.
7 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
Reply 7 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;
}
Reply 7 years ago
Referrence above to 'FastEasy_Simon_Says.ino'
Should be: 'FastEasy_Simon_Sings.ino'
Reply 7 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;
}
}
}
Reply 7 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.
Reply 7 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++;
}
}
Reply 7 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.
7 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 =)
Reply 7 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.