John Bradnam
Published © GPL3+

Simon Mini Game

Small self contained Arduino Pro Mini Simon game

IntermediateFull instructions provided8 hours3,248
Simon Mini Game

Things used in this project

Hardware components

Arduino Pro Mini 328 - 5V/16MHz
SparkFun Arduino Pro Mini 328 - 5V/16MHz
×1
MAX7219 DIL 24 IC
×1
12 pin machine header for IC socket
×2
12mm x 12mm Tactile Switch with different colored button tops
×4
LED white 1210 SMD
×4
100R & 0R Resistors 1206 SMD
×1
100R Resistor 0805 SMD
×3
10K Resistor 0805 SMD
×1
0.1uF Ceramic Capacitor 0805 SMD
×1
10uF Ceramic Capacitor 1206 SMD
×1
2 Digit 0.56in Common Cathode 7 Segment Display
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)
FTDI Programmer

Story

Read more

Custom parts and enclosures

Case - Top

Case - Bottom

Display Bezel

Schematics

Schematic

PCB

Eagle Files

Schematic and PCB in Eagle Format

Code

SimonV1.ino

C/C++
/*
  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;
}

Piano.h

C Header File
#ifndef piano_h
#define piano_h

// Constants for notes
#define REST	 0
#define NOTE_B0  31
#define NOTE_C1  33
#define NOTE_CS1 35
#define NOTE_D1  37
#define NOTE_DS1 39
#define NOTE_E1  41
#define NOTE_F1  44
#define NOTE_FS1 46
#define NOTE_G1  49
#define NOTE_GS1 52
#define NOTE_A1  55
#define NOTE_AS1 58
#define NOTE_B1  62
#define NOTE_C2  65
#define NOTE_CS2 69
#define NOTE_D2  73
#define NOTE_DS2 78
#define NOTE_E2  82
#define NOTE_F2  87
#define NOTE_FS2 93
#define NOTE_G2  98
#define NOTE_GS2 104
#define NOTE_A2  110
#define NOTE_AS2 117
#define NOTE_B2  123
#define NOTE_C3  131
#define NOTE_CS3 139
#define NOTE_D3  147
#define NOTE_DS3 156
#define NOTE_E3  165
#define NOTE_F3  175
#define NOTE_FS3 185
#define NOTE_G3  196
#define NOTE_GS3 208
#define NOTE_A3  220
#define NOTE_AS3 233
#define NOTE_B3  247
#define NOTE_C4  262
#define NOTE_CS4 277
#define NOTE_D4  294
#define NOTE_DS4 311
#define NOTE_E4  330
#define NOTE_F4  349
#define NOTE_FS4 370
#define NOTE_G4  392
#define NOTE_GS4 415
#define NOTE_A4  440
#define NOTE_AS4 466
#define NOTE_B4  494
#define NOTE_C5  523
#define NOTE_CS5 554
#define NOTE_D5  587
#define NOTE_DS5 622
#define NOTE_E5  659
#define NOTE_F5  698
#define NOTE_FS5 740
#define NOTE_G5  784
#define NOTE_GS5 831
#define NOTE_A5  880
#define NOTE_AS5 932
#define NOTE_B5  988
#define NOTE_C6  1047
#define NOTE_CS6 1109
#define NOTE_D6  1175
#define NOTE_DS6 1245
#define NOTE_E6  1319
#define NOTE_F6  1397
#define NOTE_FS6 1480
#define NOTE_G6  1568
#define NOTE_GS6 1661
#define NOTE_A6  1760
#define NOTE_AS6 1865
#define NOTE_B6  1976
#define NOTE_C7  2093
#define NOTE_CS7 2217
#define NOTE_D7  2349
#define NOTE_DS7 2489
#define NOTE_E7  2637
#define NOTE_F7  2794
#define NOTE_FS7 2960
#define NOTE_G7  3136
#define NOTE_GS7 3322
#define NOTE_A7  3520
#define NOTE_AS7 3729
#define NOTE_B7  3951
#define NOTE_C8  4186
#define NOTE_CS8 4435
#define NOTE_D8  4699
#define NOTE_DS8 4978
#define END_OF_TUNE 0xFFFF

#define DUR_8 0xE000
#define DUR_6 0xC000
#define DUR_4 0x8000
#define DUR_3 0x6000
#define DUR_2 0x4000
#define DUR_1 0x2000

#endif

Credits

John Bradnam
150 projects • 182 followers

Comments