/*
ARDUINO SIMON
Code: jbrad2089@gmail.com
*/
#include "Piano.h"
#include <NewTone.h>
#include "LedControl.h"
// FR ELISE (Initial tune)
const uint16_t melody_elise[] PROGMEM = {
DUR_4|NOTE_E5, DUR_4|NOTE_DS5, DUR_4|NOTE_E5, DUR_4|NOTE_DS5, DUR_4|NOTE_E5, DUR_4|NOTE_B4, DUR_4|NOTE_D5, DUR_4|NOTE_C5, DUR_2|NOTE_A4,
DUR_8|NOTE_C4, DUR_4|NOTE_E3, DUR_4|NOTE_A4, DUR_2|NOTE_B4, DUR_8|NOTE_E3, DUR_4|NOTE_GS3, DUR_4|NOTE_B4, DUR_2|NOTE_C5,
DUR_8|NOTE_E3,
DUR_4|NOTE_E5, DUR_4|NOTE_DS5, DUR_4|NOTE_E5, DUR_4|NOTE_DS5, DUR_4|NOTE_E5, DUR_4|NOTE_B4, DUR_4|NOTE_D5, DUR_4|NOTE_C5, DUR_1|NOTE_A4,
DUR_4|REST, END_OF_TUNE
};
// HOME ON THE RANGE (Win tune for reaching 99)
const uint16_t melody_range[] PROGMEM = {
DUR_8|NOTE_G3, DUR_8|NOTE_G3, DUR_8|NOTE_C4, DUR_8|NOTE_D4, DUR_4|NOTE_E4, DUR_8|NOTE_C4, DUR_8|NOTE_B3, DUR_8|NOTE_A3, DUR_8|NOTE_F4, DUR_8|NOTE_F4, DUR_4|NOTE_F4,
DUR_8|NOTE_E4, DUR_8|NOTE_F4, DUR_4|NOTE_G4, DUR_8|NOTE_C4, DUR_8|NOTE_C4, DUR_8|NOTE_C4, DUR_8|NOTE_B3, DUR_8|NOTE_C4, DUR_1|NOTE_D4,
DUR_8|NOTE_G3, DUR_8|NOTE_G3, DUR_8|NOTE_C4, DUR_8|NOTE_D4, DUR_4|NOTE_E4, DUR_8|NOTE_C4, DUR_8|NOTE_B3, DUR_8|NOTE_A3, DUR_8|NOTE_F4, DUR_8|NOTE_F4, DUR_4|NOTE_F4,
DUR_8|NOTE_F4, DUR_8|NOTE_F4, DUR_6|NOTE_E4, DUR_8|NOTE_D4, DUR_8|NOTE_C4, DUR_8|NOTE_B3, DUR_8|NOTE_C4, DUR_8|NOTE_D4, DUR_2|NOTE_C4, DUR_2|REST,
DUR_2|NOTE_G4, DUR_8|NOTE_F4, DUR_6|NOTE_E4, DUR_8|NOTE_D4, DUR_1|NOTE_E4,
DUR_8|NOTE_G3, DUR_8|NOTE_G3, DUR_4|NOTE_C4, DUR_8|NOTE_C4, DUR_8|NOTE_C4, DUR_8|NOTE_C4, DUR_8|NOTE_C4, DUR_8|NOTE_B3, DUR_8|NOTE_C4, DUR_2|NOTE_D4,
DUR_8|NOTE_G3, DUR_8|NOTE_G3, DUR_8|NOTE_C4, DUR_8|NOTE_D4, DUR_4|NOTE_E4, DUR_8|NOTE_C4, DUR_8|NOTE_B3, DUR_8|NOTE_A3, DUR_8|NOTE_F4, DUR_8|NOTE_F4, DUR_4|NOTE_F4,
DUR_8|NOTE_F4, DUR_8|NOTE_F4, DUR_4|NOTE_E4, DUR_8|NOTE_D4, DUR_8|NOTE_C4, DUR_8|NOTE_B3, DUR_8|NOTE_C4, DUR_8|NOTE_D4, DUR_1|NOTE_C4,
DUR_4|REST, END_OF_TUNE
};
// Define pins
#define SPEAKER 5
// LED display
#define DATA 11
#define CLOCK 3
#define LOAD 2
LedControl lc=LedControl(DATA,CLOCK,LOAD,1);
//Switches and LEDS
#define RED_PIN 6
#define BLU_PIN 7
#define YEL_PIN 8
#define GRN_PIN 9
uint8_t colorToPin[] = {0, RED_PIN, BLU_PIN, YEL_PIN, GRN_PIN };
uint16_t colorToNote[] = {0, DUR_8|NOTE_A3, DUR_8|NOTE_C4, DUR_8|NOTE_E4, DUR_8|NOTE_G4 };
#define NOTE_WRONG DUR_4|NOTE_A2
#define RANDOM_SEED_PIN A0
int score; //The current number of steps
uint8_t simonSays[100]; //Step storage buffer
int lastLED = 0; //Used when playing a tune so as not to repeat the same LED twice in a row
void setup()
{
//Speaker
pinMode(SPEAKER, OUTPUT);
//LCD Digits
lc.shutdown(0,false); //Wake up MAX7219
lc.setScanLimit(0, 2); //Using 2 digits
lc.setIntensity(0,1); //Minimum Brightness
lc.clearDisplay(0); //Clear display
//Buttons / LEDs
for (int p = 1; p <= 4; p++)
{
int pin = colorToPin[p];
pinMode(pin, OUTPUT);
digitalWrite(pin, HIGH);
}
//Play a tune as an opening theme
randomSeed(analogRead(RANDOM_SEED_PIN));
playSong(melody_elise, true);
//Set players score
score = 0;
showValue(score, true);
}
void loop()
{
//Get next note to play
simonSays[score++] = random(1,5);
showValue(score, true);
//Play current tune
for (int n = 0; n < score; n++)
{
showLED(simonSays[n], true);
playNote(colorToNote[simonSays[n]] & 0x1FFF | DUR_4, false);
showLED(simonSays[n], false);
}
//wait for response
for (int n = 0; n < score; n++)
{
int expected = simonSays[n];
int button = getPressedButton(expected);
while (button == 0)
{
delay(50);
button = getPressedButton(expected);
}
if (button != expected)
{
//Lose game
flashValue(score, 10, 200);
score = 0;
break;
}
else if (score == 99)
{
//Win game
flashValue(score, 20, 100);
playSong(melody_range, false);
score = 0;
break;
}
}
delay(1000);
}
// Plays a given song to completion
// melody - Pointer to notes array in program memory
// withLEDs - true to show random LED on each note played
int playSong(uint16_t* melody, bool withLEDs)
{
//First count every note to play so we can show a count down
int thisNote = 0;
int numberOfNotes = 0;
uint16_t noteRaw = pgm_read_word(&melody[thisNote++]);
while (noteRaw != END_OF_TUNE)
{
if ((noteRaw & 0x1FFF) != REST)
{
numberOfNotes++;
}
noteRaw = pgm_read_word(&melody[thisNote++]);
} //while
//Play each note in the melody until the END_OF_TUNE note is encountered
thisNote = 0;
noteRaw = pgm_read_word(&melody[thisNote++]);
while (noteRaw != END_OF_TUNE)
{
if ((noteRaw & 0x1FFF) != REST)
{
if (withLEDs)
{
showValue(numberOfNotes, true);
}
numberOfNotes--;
}
playNote(noteRaw, withLEDs);
noteRaw = pgm_read_word(&melody[thisNote++]);
} //while
delay(50);
noNewTone(SPEAKER);
if (withLEDs)
{
lc.clearDisplay(0); //Clear display
}
}
uint16_t playNote(uint16_t noteRaw, bool withLEDs)
{
// to calculate the note duration, take one second divided by the note type.
// e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.
uint16_t frequency = noteRaw & 0x1FFF;
uint8_t duration = (noteRaw & 0xE000) >> 13;
if (duration == 7)
duration = 8;
uint16_t noteDuration = 1800 / duration;
int led = 0;
if (frequency != REST)
{
if (withLEDs)
{
//Switch on a different LED to last time
led = random(1,5);
while (led == lastLED)
{
led = random(1,5);
}
lastLED = led;
showLED(led, true);
}
NewTone(SPEAKER, frequency, noteDuration);
}
// to distinguish the notes, set a minimum time between them.
// the note's duration + 30% seems to work well:
uint16_t pauseBetweenNotes = (noteDuration * 13) / 10;
while (pauseBetweenNotes > 0) {
_delay_ms(1);
pauseBetweenNotes--;
}
if (frequency != REST)
{
// stop the tone playing:
noNewTone(SPEAKER);
if (withLEDs)
{
showLED(led, false);
}
}
return frequency;
}
//Flashes a value on the display
// value - number to flash
// repeat - number of times to flash
// delta - time in mS between each state
void flashValue(int value, int repeat, int delta)
{
for (int x=0; x < repeat;x++)
{
showValue(value, (x & 1) != 0);
delay(delta);
}
showValue(value, true);
}
//Display a numeric value
// value - 0 to 99
// on - true to show value, false to turn off display
void showValue(int value, bool on)
{
value = min(value, 99);
int tens = value / 10;
int units = value % 10;
if (on)
{
lc.setDigit(0,1,units,false);
}
else
{
lc.setChar(0,1,' ', false);
}
if (on && tens != 0)
{
lc.setDigit(0,0,tens,false);
}
else
{
lc.setChar(0,0,' ', false);
}
}
//Shows or hides a LED
// led - 1 to 4, LED to change
// on - true is on, false is off
void showLED(int led, bool on)
{
if (led > 0 && led <=4)
{
uint8_t pin = colorToPin[led];
pinMode(pin, OUTPUT);
digitalWrite(pin, (on) ? LOW : HIGH);
}
}
//Tests if a button is pressed
// pin - Data pin button is connected to
// returns true if button is pressed
bool isButtonPressed(int pin)
{
pinMode(pin, INPUT); //Configure for input
digitalWrite(pin, LOW); //No pullup resistor
bool state = (digitalRead(pin) == LOW);
pinMode(pin, OUTPUT); //Configure for output
digitalWrite(pin, HIGH); //Turn off LED
return state;
}
//Tests if any of the buttons have been pressed and released
// expected - button that should be pressed
// returns the button that was pressed
// notes: Displays LED and plays tone while button is pressed.
// if button pressed matches expected, plays correct note otherwise plays NOTE_WRONG
int getPressedButton(int expected)
{
bool pressed = false;
int button = 0;
uint8_t pin = 0;
for (int b = 1; b <= 4; b++)
{
pin = colorToPin[b];
if (isButtonPressed(pin))
{
button = b;
break;
}
}
if (pin != 0)
{
if (isButtonPressed(pin))
{
_delay_ms(5);
if (isButtonPressed(pin))
{
//Play tone continiously while button is pressed.
//Play NOTE_WRONG if button is not what is expected
NewTone(SPEAKER, ((button == expected) ? colorToNote[button] : NOTE_WRONG) & 0x1FFF, 0);
while (isButtonPressed(pin))
{
}
noNewTone(SPEAKER);
pressed = true;
}
}
}
return (pressed) ? button : 0;
}
Comments