/*
20150307 ekristoff Adapted from SparkFun Inventor's Kit Example sketch 16
https://learn.sparkfun.com/tutorials/sik-experiment-guide-for-arduino---v32/experiment-16-simon-says
Simon tones from Wikipedia
- A (red) - 440Hz
- a (green) - 880Hz
- D (blue) - 587.33Hz
- G (yellow) - 784Hz
*/
#include <Servo.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
/*************************************************
* Public Constants
*************************************************/
// NOTE FREQUENCIES
#define C3 130.81
#define Db3 138.59
#define D3 146.83
#define Eb3 155.56
#define E3 164.81
#define F3 174.61
#define Gb3 185.00
#define G3 196.00
#define Ab3 207.65
#define LA3 220.00
#define Bb3 233.08
#define B3 246.94
#define C4 261.63
#define Db4 277.18
#define D4 293.66
#define Eb4 311.13
#define E4 329.63
#define F4 349.23
#define Gb4 369.99
#define G4 392.00
#define Ab4 415.30
#define LA4 440.00
#define Bb4 466.16
#define B4 493.88
#define C5 523.25
#define Db5 554.37
#define D5 587.33
#define Eb5 622.25
#define E5 659.26
#define F5 698.46
#define Gb5 739.99
#define G5 783.99
#define Ab5 830.61
#define LA5 880.00
#define Bb5 932.33
#define B5 987.77
#define C6 1047
#define CS6 1109
#define D6 1175
#define DS6 1245
#define E6 1319
#define F6 1397
#define FS6 1480
#define G6 1568
#define GS6 1661
#define A6 1760
#define AS6 1865
#define B6 1976
#define C7 2093
#define CS7 2217
#define D7 2349
#define DS7 2489
#define E7 2637
#define F7 2794
#define FS7 2960
#define G7 3136
#define GS7 3322
#define A7 3520
#define AS7 3729
#define B7 3951
#define C8 4186
#define CS8 4435
#define D8 4699
#define DS8 4978
// DURATION OF THE NOTES
#define BPM 240 //you can change this value changing all the others
#define Q 60000/BPM //quarter 1/4
#define H 2*Q //half 2/4
#define T 3*Q //three quarter 3/4
#define E Q/2 //eighth 1/8
#define S Q/4 // sixteenth 1/16
#define W 4*Q // whole 4/4
#define Z 1.5*Q //3/4
#define A 3*E //3/8
// CHECKS FOR BUTTON AND LIGHT POSITIONS
#define CHOICE_OFF 0 //Used to control LEDs
#define CHOICE_NONE 0 //Used to check buttons
#define CHOICE_RED (1 << 0)
#define CHOICE_GREEN (1 << 1)
#define CHOICE_BLUE (1 << 2)
#define CHOICE_YELLOW (1 << 3)
// DEFINE PIN LOCATIONS
#define LED_RED 8
#define LED_GREEN 10
#define LED_BLUE 12
#define LED_YELLOW 6
#define BUTTON_RED 7
#define BUTTON_GREEN 9
#define BUTTON_BLUE 11
#define BUTTON_YELLOW 5
#define BUZZER 4
#define SERVOPIN 13
// GAME PARAMETERS
#define ENTRY_TIME_LIMIT 3000 //Amount of time to press a button before game times out. 3000 ms = 3 sec
int ROUNDS_TO_WIN = 5; //Number of rounds to succeasfully remember before you win.
// GAME STATE
byte gameBoard[32]; //Contains the combination of buttons as we advance
byte gameRound = 0; //Counts the number of succesful rounds the player has made it through
Servo servo;
int pos = 0;
void setup() // Run once when power is connected
{
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0, 0);
servo.attach(SERVOPIN);
servo.write(0); // Degree position
pinMode(BUTTON_RED, INPUT_PULLUP);
pinMode(BUTTON_GREEN, INPUT_PULLUP);
pinMode(BUTTON_BLUE, INPUT_PULLUP);
pinMode(BUTTON_YELLOW, INPUT_PULLUP);
pinMode(LED_RED, OUTPUT);
pinMode(LED_GREEN, OUTPUT);
pinMode(LED_BLUE, OUTPUT);
pinMode(LED_YELLOW, OUTPUT);
}
void loop()
{
attractMode();
// Indicate the start of game play
setLEDs(CHOICE_RED | CHOICE_GREEN | CHOICE_BLUE | CHOICE_YELLOW); // Turn all LEDs on
delay(1000);
setLEDs(CHOICE_OFF); // Turn off LEDs
delay(250);
// Play memory game and handle result
if (play_memory() == true)
play_winner(); // Player won, play winner tones
else
play_loser(); // Player lost, play loser tones
}
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//THE FOLLOWING FUNCTIONS CONTROL GAME PLAY
// Play the memory game
// Returns 0 if player loses, or 1 if player wins
boolean play_memory(void)
{
randomSeed(millis()); // Seed the random generator with random amount of millis()
gameRound = 0; // Reset the game to the beginning
while (gameRound < ROUNDS_TO_WIN) {
// Display the round number
display.clearDisplay();
display.setCursor(0, 0);
display.println("Round " + String(gameRound + 1) + " of " + String(ROUNDS_TO_WIN));
display.display();
add_to_moves(); // Add a button to the current moves, then play them back
playMoves(); // Play back the current game board
// Then require the player to repeat the sequence.
for (byte currentMove = 0 ; currentMove < gameRound ; currentMove++) {
byte choice = wait_for_button(); // See what button the user presses
if (choice == 0) return false; // If wait timed out, player loses
if (choice != gameBoard[currentMove]) return false; // If the choice is incorrect, player loses
}
delay(1000); // Player was correct, delay before playing moves
}
return true; // Player made it through all the rounds to win!
}
// Plays the current contents of the game moves
void playMoves(void)
{
for (byte currentMove = 0 ; currentMove < gameRound ; currentMove++)
{
toner(gameBoard[currentMove]);
// Wait some amount of time between button playback
// Shorten this to make game harder
delay(150); // 150 works well. 75 gets fast.
}
}
// Adds a new random button to the game sequence, by sampling the timer
void add_to_moves(void)
{
byte newButton = random(0, 4); //min (included), max (exluded)
// We have to convert this number, 0 to 3, to CHOICEs
if(newButton == 0) newButton = CHOICE_RED;
else if(newButton == 1) newButton = CHOICE_GREEN;
else if(newButton == 2) newButton = CHOICE_BLUE;
else if(newButton == 3) newButton = CHOICE_YELLOW;
gameBoard[gameRound++] = newButton; // Add this new button to the game array
}
//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//THE FOLLOWING FUNCTIONS CONTROL THE HARDWARE
// Lights a given LEDs
// Pass in a byte that is made up from CHOICE_RED, CHOICE_YELLOW, etc
void setLEDs(byte leds)
{
if ((leds & CHOICE_RED) != 0)
digitalWrite(LED_RED, HIGH);
else
digitalWrite(LED_RED, LOW);
if ((leds & CHOICE_GREEN) != 0)
digitalWrite(LED_GREEN, HIGH);
else
digitalWrite(LED_GREEN, LOW);
if ((leds & CHOICE_BLUE) != 0)
digitalWrite(LED_BLUE, HIGH);
else
digitalWrite(LED_BLUE, LOW);
if ((leds & CHOICE_YELLOW) != 0)
digitalWrite(LED_YELLOW, HIGH);
else
digitalWrite(LED_YELLOW, LOW);
}
// Wait for a button to be pressed.
// Returns one of LED colors (LED_RED, etc.) if successful, 0 if timed out
byte wait_for_button(void)
{
long startTime = millis(); // Remember the time we started the this loop
while ( (millis() - startTime) < ENTRY_TIME_LIMIT) // Loop until too much time has passed
{
byte button = checkButton();
if (button != CHOICE_NONE)
{
toner(button); // Play the button the user just pressed
while(checkButton() != CHOICE_NONE) ; // Now let's wait for user to release button
delay(10); // This helps with debouncing and accidental double taps
return button;
}
}
return CHOICE_NONE; // If we get here, we've timed out!
}
// Returns a '1' bit in the position corresponding to CHOICE_RED, CHOICE_GREEN, etc.
byte checkButton(void)
{
if (digitalRead(BUTTON_RED) == 0) return(CHOICE_RED);
else if (digitalRead(BUTTON_GREEN) == 0) return(CHOICE_GREEN);
else if (digitalRead(BUTTON_BLUE) == 0) return(CHOICE_BLUE);
else if (digitalRead(BUTTON_YELLOW) == 0) return(CHOICE_YELLOW);
return(CHOICE_NONE); // If no button is pressed, return none
}
// Light an LED and play tone
// Red, upper left: 440Hz - A4
// Green, upper right: 880Hz - A5
// Blue, lower left: 587.33Hz - D5
// Yellow, lower right: 784Hz - G5
void toner(byte which)
{
setLEDs(which); //Turn on a given LED
//Play the sound associated with the given LED
switch(which)
{
case CHOICE_RED:
play(LA4, Q);
break;
case CHOICE_GREEN:
play(LA5, Q);
break;
case CHOICE_BLUE:
play(D5, Q);
break;
case CHOICE_YELLOW:
play(G5, Q);
break;
}
setLEDs(CHOICE_OFF); // Turn off all LEDs
}
// Play the winner sound and lights
void play_winner(void) {
display.clearDisplay();
display.println("Congratulations! You won!");
display.display();
for(int i=0; i<10; i++) {
setLEDs(CHOICE_RED | CHOICE_GREEN | CHOICE_BLUE | CHOICE_YELLOW); // Turn on all LEDs
delay(80); // Wait for 50ms
setLEDs(CHOICE_OFF); // Turn off all LEDs
delay(80); // Wait for 50ms
}
winner_sound();
// Blinking LEDs rapidly while playing the winner sound
// Proceed with the rest of the function
// Move servo 90 degrees to pull on latch
for(pos = 0; pos < 120; pos += 2) {
servo.write(pos);
delay(10);
}
// wait 1/2 second for door to open
delay(500);
// Move servo back to initial position
for(pos = 90; pos>=1; pos-=2) {
servo.write(pos);
delay(10);
}
for(int i=0; i<10; i++) {
setLEDs(CHOICE_RED | CHOICE_GREEN | CHOICE_BLUE | CHOICE_YELLOW); // Turn on all LEDs
delay(80); // Wait for 50ms
setLEDs(CHOICE_OFF); // Turn off all LEDs
delay(80); // Wait for 50ms
}
attractMode();
}
// Play the winner sound
// We are the Champions!
void winner_sound(void)
{
play(G3, E);
play(C4, E);
play(E4, E);
play(G4, E);
play(C5, E);
play(E5, E);
play(G5, Z);
play(E5, Z);
play(Ab3, E);
play(C4, E);
play(Eb4, E);
play(Ab4, E);
play(C5, E);
play(Eb5, E);
play(Ab5, Z);
play(Eb5, Z);
play(Bb3, E);
play(D4, E);
play(F4, E);
play(Bb4, E);
play(D5, E);
play(F5, E);
play(Bb5, A);
play(Bb5, E);
play(Bb5, E);
play(Bb5, E);
play(C6, H);
}
// Play the loser sound/lights
void play_loser(void)
{
display.clearDisplay();
display.println("You lost! Try again");
display.display();
setLEDs(CHOICE_RED | CHOICE_GREEN);
play(B3,Q);
setLEDs(CHOICE_BLUE | CHOICE_YELLOW);
play(B3,Q);
setLEDs(CHOICE_RED | CHOICE_GREEN);
play(B3,Q);
setLEDs(CHOICE_BLUE | CHOICE_YELLOW);
play(B3,Q);
}
// Show an "attract mode" display while waiting for user to press button.
void attractMode(void)
{
display.clearDisplay();
display.println("Press any button to start!");
display.display();
while(1)
{
setLEDs(CHOICE_RED);
delay(100);
if (checkButton() != CHOICE_NONE) return;
setLEDs(CHOICE_BLUE);
delay(100);
if (checkButton() != CHOICE_NONE) return;
setLEDs(CHOICE_GREEN);
delay(100);
if (checkButton() != CHOICE_NONE) return;
setLEDs(CHOICE_YELLOW);
delay(100);
if (checkButton() != CHOICE_NONE) return;
}
}
void play(long note, int duration) {
tone(BUZZER,note,duration);
delay(1+duration);
}