/*
ARDUINO SIMON
Code: jbrad2089@gmail.com
BOARD: ATtiny1614/1604/814/804/414/404/214/204
Chip: ATtiny1614
Clock Speed: 4MHz
Programmer: jtag2updi (megaTinyCore)
ATTiny1614 Pins mapped to Ardunio Pins
+--------+
VCC + 1 14 + GND
(SS) 0 PA4 + 2 13 + PA3 10 (SCK)
1 PA5 + 3 12 + PA2 9 (MISO)
(DAC) 2 PA6 + 4 11 + PA1 8 (MOSI)
3 PA7 + 5 10 + PA0 11 (UPDI)
(RXD) 4 PB3 + 6 9 + PB0 7 (SCL)
(TXD) 5 PB2 + 7 8 + PB1 6 (SDA)
+--------+
*/
#include "Piano.h"
#include <TM1650.h>
#include <avr/sleep.h>
// Define pins
#define SPEAKER 8 //PA1
#define ON 5 //PB2
// LED display
#define DATA 2 //PA6
#define CLOCK 3 //PA7
#define POWER 7 //PB0
#define BRIGHTNESS 2
TM1650 display(DATA, //byte dataPin
CLOCK, //byte clockPin
2, //byte number of digits
true, //boolean activeDisplay = true
BRIGHTNESS //byte intensity
);
//Switches and LEDS
#define RED_PIN 0 //PA4
#define BLU_PIN 4 //PB3
#define YEL_PIN 10 //PA3
#define GRN_PIN 9 //PA2
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 1 //PA5
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
volatile bool shutDownPressed = false;
// 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
};
//Font for TM1650
#define SPACE 10
const PROGMEM byte NUMBER_FONT[] = {
//pgfedcba
0b00111111, // 0
0b00000110, // 1
0b01011011, // 2
0b01001111, // 3
0b01100110, // 4
0b01101101, // 5
0b01111101, // 6
0b00000111, // 7
0b01111111, // 8
0b01101111, // 9
0b00000000 // SPACE
};
//--------------------------------------------------------------------
//Handle pin change interrupt when button is pressed
//This wakes up the ATtiny1614 from its slumber
void SwitchInterrupt()
{
}
//--------------------------------------------------------------------
//Program Setup
void setup()
{
randomSeed(analogRead(RANDOM_SEED_PIN));
powerOff(); //Go into sleep until woken
}
//---------------------------------------------------------------------
//Shut down TM1650 and put ATtiny to sleep
void powerOff()
{
display.clearDisplay();
digitalWrite(POWER,HIGH); //Remove power from display
for (int p = 1; p <= 4; p++)
{
int pin = colorToPin[p];
digitalWrite(pin, HIGH); //Switch off all LEDs
}
pinMode(ON,INPUT_PULLUP);
while (digitalRead(ON) == LOW); //wait until ON/OFF button is released
attachInterrupt(ON,SwitchInterrupt,CHANGE);
//cbi(ADCSRA,ADEN); // switch Analog to Digitalconverter OFF
set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here
sleep_enable();
sleep_mode(); // System actually sleeps here
sleep_disable(); // System continues execution here when watchdog timed out
//sbi(ADCSRA,ADEN); // switch Analog to Digitalconverter ON
pinMode(ON,INPUT_PULLUP);
detachInterrupt(ON);
powerOn();
}
//---------------------------------------------------------------------
//Power up the TM1650 and put play initial animation
void powerOn()
{
//Speaker
pinMode(SPEAKER, OUTPUT);
//Display
pinMode(POWER,OUTPUT);
//Buttons / LEDs
pinMode(ON,INPUT_PULLUP);
for (int p = 1; p <= 4; p++)
{
int pin = colorToPin[p];
pinMode(pin, OUTPUT);
digitalWrite(pin, HIGH);
}
digitalWrite(POWER,LOW);
digitalWrite(DATA,HIGH);
digitalWrite(CLOCK,HIGH);
display.clearDisplay();
display.setupDisplay(true, BRIGHTNESS);
for (int d = 200; d > 100; d=d-20)
{
for (int n=1;n<=4;n++)
{
int pin = colorToPin[n];
digitalWrite(pin, LOW); //Switch on LED
delay(d);
digitalWrite(pin, HIGH); //Switch off LED
}
}
//playSong(melody_elise, true);
//wait until ON/OFF button is released
while (digitalRead(ON) == LOW);
shutDownPressed = false;
delay(1000);
//Set players score
score = 0;
displayNumber(score, false, true);
}
//--------------------------------------------------------------------
//Program Loop
void loop()
{
//Get next note to play
simonSays[score++] = random(1,5);
displayNumber(score, false, true);
//Play current tune
int n = 0;
while (!shutDownPressed && n < score)
{
showLED(simonSays[n], true);
playNote((colorToNote[simonSays[n]] & 0x1FFF) | DUR_4, false);
showLED(simonSays[n], false);
n++;
checkShutdown();
}
//wait for response
n = 0;
while (!shutDownPressed && n < score)
{
int expected = simonSays[n];
int button = getPressedButton(expected);
while (!shutDownPressed && button == 0)
{
delay(50);
checkShutdown();
button = getPressedButton(expected);
}
if (!shutDownPressed && button != expected)
{
//Lose game
flashValue(score, 10, 200);
score = 0;
shutDownPressed = true; //Shut down after playing a game
break;
}
else if (score == 99)
{
//Win game
flashValue(score, 20, 100);
playSong(melody_range, false);
score = 0;
shutDownPressed = true; //Shut down after playing a game
break;
}
n++;
}
uint32_t timeout = millis() + 1000;
while (!shutDownPressed && millis() < timeout)
{
checkShutdown();
}
if (shutDownPressed)
{
powerOff(); //Go into sleep until woken
}
}
//---------------------------------------------------------------------
// Plays a given song to completion
// melody - Pointer to notes array in program memory
// withLEDs - true to show random LED on each note played
void playSong(const 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)
{
displayNumber(numberOfNotes, false, true);
}
numberOfNotes--;
}
playNote(noteRaw, withLEDs);
noteRaw = pgm_read_word(&melody[thisNote++]);
} //while
delay(50);
noTone(SPEAKER);
if (withLEDs)
{
displayClear(); //Clear display
}
}
//---------------------------------------------------------------------
// Plays a note
// noteRaw - note/duration to play
// withLEDs - flash a random LED when playing the note
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);
}
tone(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;
delay(pauseBetweenNotes);
if (frequency != REST)
{
// stop the tone playing:
noTone(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++)
{
displayNumber(value, false, (x & 1) != 0);
delay(delta);
}
displayNumber(value, false, true);
}
//---------------------------------------------------------------------
//Write number to display
// num - (0 to 99)
// leadingZeros - true to have leading zeros
// on - true to show digit, false to show blank
void displayNumber(int num, bool leadingZeros, bool on)
{
num = max(min(num, 99), 0);
for (int i = 0; i < 2; i++)
{
if (on && (num > 0 || i == 0 || leadingZeros))
{
displayChar(i, num % 10);
}
else
{
displayChar(i, SPACE);
}
num = num / 10;
}
}
//---------------------------------------------------------------------
//Clear the display
void displayClear()
{
byte segments = pgm_read_byte(&NUMBER_FONT[SPACE]);
display.setSegments(segments, 0);
display.setSegments(segments, 1);
}
//---------------------------------------------------------------------
//Write digit to display
// digit - digit to write to (0 - left most to 1 - right most)
// char - character to display
void displayChar(int digit, int chr)
{
byte segments = pgm_read_byte(&NUMBER_FONT[chr]);
display.setSegments(segments, 1 - digit);
}
//---------------------------------------------------------------------
//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 (button != 0)
{
if (isButtonPressed(pin))
{
delay(5);
if (isButtonPressed(pin))
{
//Play tone continiously while button is pressed.
//Play NOTE_WRONG if button is not what is expected
tone(SPEAKER, ((button == expected) ? colorToNote[button] : NOTE_WRONG) & 0x1FFF, 0);
while (isButtonPressed(pin))
{
}
noTone(SPEAKER);
pressed = true;
}
}
}
return (pressed) ? button : 0;
}
//---------------------------------------------------------------------
// Checks to see if shitdown button is pressed.
// Sets a global flag
void checkShutdown()
{
if (!shutDownPressed)
{
shutDownPressed = (digitalRead(ON) == LOW);
}
}
Comments