#include "pitches.h"
#define DO_RGB
#define COUNT 5
#define GAME_LENGTH 64
#define DEBOUNCE_MS 20
#define DOUBLEPRESS_IGNORE_MS 500
#define CUE_PERIOD 2000
#define CUE_FLASH_LENGTH 350
#define PIN_SPEAKER 9
#define NOTE_MS 150
// Speaker tones
const int button_tones[] = {
NOTE_C4, NOTE_D4, NOTE_E4, NOTE_F4, NOTE_G4
};
// Speaker state
int speaker_state = -1;
long speaker_off_millis = 0;
/*
byte led_pin [COUNT] = { 2, 7,12,14,16};
byte but_pin [COUNT] = { 4, 8,13,15,17}; // D15=A1, D17=A3
*/
byte but_pin [COUNT] = {17,15,13, 8, 4};
byte led_pin [COUNT] = {16,12,10, 2, 7}; // D15=A1, D17=A3
// gamma:
// https://learn.adafruit.com/led-tricks-gamma-correction/the-quick-fix
extern const uint8_t gamma8[];
// A0-A7 == D14 - D21
// Analog out pins: 3,5,6,9,10,11
#define R_IN A4
#define G_IN A5
#define B_IN A6
#define R_OUT 3
#define G_OUT 5
#define B_OUT 6
byte but_state [COUNT];
bool do_sound;
int but_num;
byte last_but_num = -1;
bool new_press;
uint32_t last_press = 0;
byte zero_arr [COUNT];
byte one_arr [COUNT];
// Game array and game progress
int game_array [GAME_LENGTH];
int game_progress; // length of the memory range
int round_progress; // number of correct presses
bool restart = true;
//RGB tracking
unsigned long rgb_last_change_time = 0;
int r_in_prev = 0;
int g_in_prev = 0;
int b_in_prev = 0;
#define TIMEOUT_S 20
bool firstboot = true;
uint32_t last_cue_millis = 0;
uint32_t start_cue_millis = 0;
void setup() {
Serial.begin(115200);
for (int i = 0; i<COUNT ; i++)
{
pinMode(led_pin[i], OUTPUT);
pinMode(but_pin[i], INPUT_PULLUP);
}
pinMode(PIN_SPEAKER, OUTPUT);
#ifdef DO_RGB
pinMode(R_IN, INPUT);
pinMode(G_IN, INPUT);
pinMode(B_IN, INPUT);
pinMode(R_OUT, OUTPUT);
pinMode(G_OUT, OUTPUT);
pinMode(B_OUT, OUTPUT);
r_in_prev = analogRead(R_IN);
g_in_prev = analogRead(G_IN);
b_in_prev = analogRead(B_IN);
#endif
// turn on sound by holding button 0 and 2
do_sound = !(digitalRead(but_pin[0]) || digitalRead(but_pin[2]));
// Create zero and one array as convenience
for (int i=0; i<COUNT; i++){
zero_arr[i] = 0;
one_arr[i] = 1;
}
// Seed random number gen
pinMode(A0, INPUT);
randomSeed(analogRead(A0));
}
/*
* main loop
*/
void loop() {
#ifdef DO_RGB
// Update RGB Led
do_rgb();
#endif
// Turn of speaker if past note duration
if (millis() > speaker_off_millis && speaker_state >= 0){
set_speaker_state(-1);
}
// Zero out leds. Poll button state
set_led_state(zero_arr);
get_but_state();
but_num = get_but_num();
new_press = false;
if (but_num != last_but_num && but_num > -1){
new_press = true;
last_but_num = but_num;
last_press = millis();
}
if (millis() - last_press > DOUBLEPRESS_IGNORE_MS){
last_but_num = but_num; // kindness feature. Don't punish double button presses
}
// Game progress
if (restart){
reset_game();
restart = false;
}
// Show array up to progress
if (round_progress == 0) {
flash_cue();
}
// Check button press against game array
if (new_press) {
// there was a press!
Serial.print("Pressed ");
Serial.print(but_num);
Serial.print(" ");
if (game_array[round_progress] == but_num) {
// Correct!
round_progress++;
if (round_progress > game_progress){
game_progress ++;
round_progress = 0;
if (game_progress > 2){
delay(200);
show_win();
}
}
Serial.print("Correct. Game progress: ");
Serial.print(game_progress);
Serial.print(". round progress: ");
Serial.println(round_progress);
last_cue_millis = millis() - CUE_PERIOD/2;
}else{
// Incorrect
Serial.print("Incorrect: ");
Serial.print(game_array[round_progress]);
Serial.print(" != ");
Serial.println(but_num);
if (game_progress > 2 )show_loss();
reset_game();
last_cue_millis = millis();
}
}
// set_led_state(but_state);
// set_led_state(get_but_num());
// set_led_state(3, 0);
}
int do_rgb()
{
int r_in = analogRead(R_IN);
int g_in = analogRead(G_IN);
int b_in = analogRead(B_IN);
if (abs(r_in - r_in_prev) + abs(g_in - g_in_prev) + abs(b_in - b_in_prev) > 15){
//Knobs are touched
rgb_last_change_time = millis();
firstboot = false;
r_in_prev = r_in;
g_in_prev = g_in;
b_in_prev = b_in;
}
if (!firstboot & millis() - rgb_last_change_time < TIMEOUT_S * 1000){
analogWrite(R_OUT,pgm_read_byte(&gamma8[r_in/4 ]));
analogWrite(G_OUT,pgm_read_byte(&gamma8[g_in/4 ]));
analogWrite(B_OUT,pgm_read_byte(&gamma8[b_in/4 ]));
}else{
digitalWrite(R_OUT,1);
digitalWrite(G_OUT,1);
digitalWrite(B_OUT,1);
}
}
/*
* Game methods
*/
void reset_game(){
// Random(COUNT) -> 0 to COUNT (exclusive)
game_progress = 1;
round_progress = 0;
randomSeed(millis());
int last = -1;
for (int i=0; i<GAME_LENGTH; i++){
game_array[i] = random(COUNT);
while (game_array[i] == last)
{
game_array[i] = random(COUNT);
}
last = game_array[i];
Serial.print(game_array[i]);
}
Serial.println();
}
int flash_cue(){
uint32_t m = millis();
if (m - last_cue_millis < CUE_PERIOD){
return -1;
}else{
int time_in_cue = m - last_cue_millis - CUE_PERIOD;
int flash_indx = time_in_cue / CUE_FLASH_LENGTH;
int flash_progress = time_in_cue % CUE_FLASH_LENGTH;
if (flash_indx > game_progress){
last_cue_millis = m;
return -1;
}
if (flash_progress < 2*CUE_FLASH_LENGTH/3){
set_led_state(game_array[flash_indx], 1);
}else{
set_led_state(game_array[flash_indx], 0);
}
}
}
/*
* Light show methods (blocking!)
*/
void show_loss(){
set_led_state(zero_arr);
set_speaker_state(1);
delay(90);
for (int i=0; i<4*COUNT; i++){
set_led_state(i%COUNT, (i/COUNT+1)%2);
set_speaker_state((COUNT-i)%COUNT);
delay(90);
}
}
void show_win(){
set_speaker_state(4);
for (int i=0; i<2; i++){
set_led_state(one_arr);
delay(60);
set_led_state(zero_arr);
delay(100);
set_speaker_state(i+2);
}
}
/*
* set led state
*/
void set_led_state(byte state[COUNT]){
for (int i = 0; i<COUNT ; i++)
{
digitalWrite(led_pin[i], state[i]);
}
}
void set_led_state(byte led_num, byte state){
digitalWrite(led_pin[led_num], state);
}
void set_led_state(byte led_num){
set_led_state(led_num, 1);
if (led_num == -1){
for (int i = 0; i<COUNT ; i++)
{
digitalWrite(led_pin[i], 0);
}
}
}
/*
* set speaker state
*/
int set_speaker_state(int s){
if (!do_sound) return -1;
if (speaker_state == s) return s;
speaker_state = s;
// Serial.print(s);
// Serial.println(" speaker");
if (s < 0){
noTone(PIN_SPEAKER);
}else{
tone(PIN_SPEAKER, button_tones[s]);
speaker_off_millis = millis() + NOTE_MS;
}
return s;
}
/*
* read_but_state
*/
void get_but_state(){
bool do_debounce = false;
for (int i = 0; i<COUNT ; i++)
{
but_state[i] = 1-digitalRead(but_pin[i]);
if (but_state[i]) set_led_state(i); // immediate visual feedback
if (but_state[i]){
do_debounce = true;
set_speaker_state(i);
}
}
if (do_debounce) {
delay(DEBOUNCE_MS);
}
}
int get_but_num(){
//assumes get_but_state has been called
for (int i = 0; i<COUNT ; i++)
{
if (but_state[i]){
return i;
}
}
return -1;
}
const uint8_t PROGMEM gamma8[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2,
2, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5,
5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10,
10, 10, 11, 11, 11, 12, 12, 13, 13, 13, 14, 14, 15, 15, 16, 16,
17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 24, 24, 25,
25, 26, 27, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 35, 36,
37, 38, 39, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 50,
51, 52, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 66, 67, 68,
69, 70, 72, 73, 74, 75, 77, 78, 79, 81, 82, 83, 85, 86, 87, 89,
90, 92, 93, 95, 96, 98, 99,101,102,104,105,107,109,110,112,114,
115,117,119,120,122,124,126,127,129,131,133,135,137,138,140,142,
144,146,148,150,152,154,156,158,160,162,164,167,169,171,173,175,
177,180,182,184,186,189,191,193,196,198,200,203,205,208,210,213,
215,218,220,223,225,228,231,233,236,239,241,244,247,249,252,255 };