#include <Keypad.h>
#include <LiquidCrystal.h>
#include <Tone.h>
Tone tone1;
const byte led1 = 4;
const byte led2 = 3;
const byte relay=2;
LiquidCrystal lcd(13, 12, 11, 10, 9, 8);
const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
byte rowPins[ROWS] = {6, 7, A1, A0};
byte colPins[COLS] = {A2, A3, A4, A5};
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
void setup()
{
pinMode(led1, OUTPUT);
pinMode(led2, OUTPUT);
pinMode(relay, OUTPUT);
tone1.begin(5);
lcd.begin(16, 2);
Serial.begin(9600);
}
void loop()
{
byte choosed= mainMenu();
if (choosed==0)
{
basicTimerMode();
}
else if (choosed==1)
{
disarmTheBombMode();
}
else if(choosed == 2)
{
dominationMode();
}
}
/**
* Crea el bucle para pedir una tecla en el keypad
* **/
char getPressedKey()
{
char key=NO_KEY;
while (key == NO_KEY)
{
key=keypad.getKey();
}
return key;
}
/*
* Se utiliza para indicar que un numero ingresado
* en el keypad no es valido
*
* recibe la posicion que debe tener en el lcd y el renglon
*
* imprime una X en la posicion, la borra y toca una nota
* al terminar el cursor vuelve a la posicion recibida
* **/
void printInvalidKey(byte posicionx, byte renglon)
{
lcd.setCursor(posicionx, renglon);
lcd.print('X');
tone1.play(NOTE_C4, 200);
delay(90);
lcd.setCursor(posicionx, renglon);
lcd.print(' ');
lcd.setCursor(posicionx, renglon);
}
void playOnGameOver(byte seconds=30)
{
byte fifthOfSec=0;
char key= NO_KEY;
digitalWrite(relay, HIGH);
while(key!='A')
{
key= keypad.getKey();
if (fifthOfSec > seconds*5)
{
digitalWrite(relay, LOW);
}
else
{
digitalWrite(led1, HIGH);
digitalWrite(led2, HIGH);
delay(100);
key= keypad.getKey();
digitalWrite(led1, LOW);
digitalWrite(led2, LOW);
delay(100);
key= keypad.getKey();
}
fifthOfSec++;
}
digitalWrite(relay, LOW);
}
//----------------------------------------------------------------------------
//------------------------------- TEMPORIZADOR -------------------------------
//----------------------------------------------------------------------------
/**
* Recibe un arreglo de tiempo, por cada segundo que pasa le resta 1 segundo
*
* @param hs_min_seg es el arreglo con los datos del runTimer, sus tres elementos son
* [0]: horas, [1]: minutos, [2]: segundos
*
* @param speedUpAtEnd se utiliza para verificar si se debe acelerar
* los leds y el *pip* que hace el runTimer cada que resta un segundo
*
* @post si no paso un segundo no se modificara el runTimer
* **/
void runTimer(byte hs_min_seg[3], bool speedUpAtEnd=false, bool silentMode=false)
{
static unsigned long secMillis = 0;
static byte waitPartOfSeg = 0;
uint16_t toneSpeed = 200;
uint16_t period = 1000;
byte horas = hs_min_seg[0];
byte minutos = hs_min_seg[1];
byte segundos = hs_min_seg[2];
if(speedUpAtEnd==true)
{
speedUpTimer(segundos, period, toneSpeed);
}
if (segundos == 0 && minutos > 0)
{
minutos--;
segundos = 60;
}
else if (minutos == 0 && horas > 0)
{
horas--;
minutos = 59;
segundos = 60;
}
if (horas==0 && minutos==0 && segundos==0)
{
hs_min_seg[0] = 0;
hs_min_seg[1] = 0;
hs_min_seg[2] = 0;
}
else if ((millis()-secMillis) >= period)
{
secMillis = millis();
if (waitPartOfSeg * period >= 1000)
{
segundos--;
waitPartOfSeg = 1;
}
if (silentMode == false)
{
tone1.play(NOTE_G5, toneSpeed);
digitalWrite(led2, HIGH);
digitalWrite(led1, HIGH);
delay(10);
digitalWrite(led2, LOW);
digitalWrite(led1, LOW);
delay(10);
}
waitPartOfSeg++;
}
hs_min_seg[0] = horas;
hs_min_seg[1] = minutos;
hs_min_seg[2] = segundos;
}
/**
* Acelera el ruido del runTimer
* cuando quedan < de 10 segundos *2
* cuando quedan < de 5 segundos *4
* **/
void speedUpTimer(byte segundos, uint16_t& period, uint16_t& toneSpeed)
{
if (segundos > 10)
{
period = 1000;
}
else if (segundos <= 10 && segundos > 5)
{
period = 500;
}
else if (segundos <= 5)
{
toneSpeed = 100;
period = 250;
}
}
/**
* Imprime el tiempo que recibe en el lcd, formato 00:00:00 (9 chars) y a partir de la posicion
* especificada
*
* @param hs_min_seg es el arreglo que contiene el runTimer
* debe estar ordenado como horas, minutos, segundos
*
* @param posx es la posicion en x a partir de la que se imprime el runTimer
*
* @param posy es el renglon en el que se imprime el runTimer
* **/
void printTimer(byte hs_min_seg[3], byte posx, byte posy)
{
byte horas = hs_min_seg[0];
byte minutos = hs_min_seg[1];
byte segundos = hs_min_seg[2];
lcd.setCursor(posx, posy);
// Imprime las horas
if (horas < 10)
{
lcd.print("0");
}
lcd.print(horas);
lcd.print(":");
// Imprime los minutos
if (minutos < 10)
{
lcd.print("0");
}
lcd.print(minutos);
lcd.print(":");
// Imprime los segundos
if (segundos < 10)
{
lcd.print("0");
}
lcd.print(segundos);
}
void sumSecsToTimer(byte hs_min_seg[3], byte amount)
{
hs_min_seg[2] += amount;
hs_min_seg[1] += hs_min_seg[2]/60;
hs_min_seg[2] %= 60;
hs_min_seg[0] += hs_min_seg[1]/60;
hs_min_seg[1] %= 60;
}
void countUpTimer(byte hs_min_seg[3], bool silentMode=false)
{
static unsigned long secMillis = 0;
if ((millis()-secMillis) >= 1000)
{
secMillis = millis();
tone1.play(NOTE_G5, 250);
sumSecsToTimer(hs_min_seg, 1);
if (silentMode == false)
{
digitalWrite(led2, HIGH);
digitalWrite(led1, HIGH);
delay(10);
digitalWrite(led2, LOW);
digitalWrite(led1, LOW);
delay(10);
}
}
}
//----------------------------------------------------------------------------
//----------------------------- FIN TEMPORIZADOR -----------------------------
//----------------------------------------------------------------------------
void setTimer(byte hs_ms_sgs[3])
{
byte hms[6] = {0};
char key;
lcd.clear();
lcd.print("Ingrese Tiempo");
byte i = 0;
while (i < 6)
{
hs_ms_sgs[0] = hms[0]*10 + hms[1];
hs_ms_sgs[1] = hms[2]*10 + hms[3];
hs_ms_sgs[2] = hms[4]*10 + hms[5];
lcd.setCursor(15, 1);
lcd.print(" ");
delay(100);
printTimer(hs_ms_sgs, 8, 1);
key = getPressedKey();
if (key == 'A')
{
break;
}
else if (key == 'B')
{
i = 0;
hms[0]=0; hms[1]=0; hms[2]=0;
hms[3]=0; hms[4]=0; hms[5]=0;
lcd.clear();
lcd.print("Ingrese Tiempo");
tone1.play(NOTE_E5, 100);
}
else
{
//si no es un numero o si el primer numero es 0
if ((key<'0' || key>'9') || (i==0 && key=='0'))
{
printInvalidKey(15, 1);
}
else
{
for (byte j=0; j<5;j++)
{
hms[j]= hms[j+1];
}
tone1.play(NOTE_B6, 200);//key normal
hms[5]=key-'0';
i++;
}
}
}
//hms=(h,h, m,m, s,s)
hms[3] += hms[4]/6;//hms[4] decena segundos
hms[4] = hms[4]%6;
hms[1] += hms[2]/6;//hms[2] decena de minutos
hms[2] = hms[2]%6;
hs_ms_sgs[0] = hms[0]*10 + hms[1];
hs_ms_sgs[1] = hms[2]*10 + hms[3];
hs_ms_sgs[2] = hms[4]*10 + hms[5];
lcd.clear();
lcd.print("TIEMPO FINAL");
printTimer(hs_ms_sgs, 8, 1);
tone1.play(NOTE_C6, 200);
delay(900);
}
void setPassword(char password[4])
{
char key;
byte currentDigit = 0;
lcd.clear();
lcd.print("Ingrese codigo");
while(currentDigit < 4)
{
lcd.setCursor(6 + currentDigit, 1);
key= getPressedKey();
if (key=='B')
{
currentDigit=0;
lcd.setCursor(6, 1);
lcd.print(" ");
tone1.play(NOTE_E5, 100);
}
else if (key<'0' || key>'9')
{
printInvalidKey(6+currentDigit, 1);
}
else
{
lcd.print(key);
password[currentDigit] = key;
currentDigit++;
tone1.play(NOTE_C6, 200);
}
}
delay(500);
lcd.clear();
lcd.print("Ingresaste: ");
delay(100);
lcd.setCursor(6,1);
// el lcd.print(password); imprime un caracter 'c' de mas
// no se por que, antes no lo hacia
lcd.print(password[0]);
lcd.print(password[1]);
lcd.print(password[2]);
lcd.print(password[3]);
tone1.play(NOTE_E6, 200);
delay(1500);
lcd.clear();
}
bool enterPassword(char password[4], byte time_hms[3])
{
char entered[4]={0};
bool areEqual;
char key;
byte currentDigit = 0;
lcd.clear();
lcd.print("Codigo: ");
while (currentDigit<4 && (time_hms[0]>0 || time_hms[1]>0 || time_hms[2]>0))
{
runTimer(time_hms, true);
lcd.setCursor(6+currentDigit, 1);
key= keypad.getKey();
if(key != NO_KEY)
{
if (key<'0' || key>'9')
{
printInvalidKey(6+currentDigit, 1);
}
else if (key=='B')
{
currentDigit=0;
lcd.setCursor(6, 1);
lcd.print(" ");
tone1.play(NOTE_E5, 100);
}
else
{
lcd.setCursor(6+currentDigit, 1);
lcd.print(key);
entered[currentDigit]=key;
tone1.play(NOTE_C6, 200);
delay(100);
lcd.setCursor(6+currentDigit, 1);
lcd.print("*");
currentDigit++;
}
}
}
areEqual=true;
for (byte i=0; i<4; i++)
{
if (entered[i] != password[i])
{
areEqual=false;
}
}
return areEqual;
}
void getPreparationTimer(byte preparationTimer[3])
{
lcd.clear();
lcd.print("Tiempo armado?");
lcd.setCursor(0, 1);
lcd.print("A: SI B:NO");
char key= getPressedKey();
if(key=='A')
{
lcd.clear();
lcd.print("TIEMPO ARMADO:");
tone1.play(NOTE_E6, 200);
delay(800);
setTimer(preparationTimer);
}
}
void runPreparationTimer(byte preparationTimer[3])
{
bool adviceToStart=(preparationTimer[0]>0 || preparationTimer[1]>0 || preparationTimer[2]>0);
lcd.clear();
lcd.print("TIEMPO ARMADO:");
while(preparationTimer[0]>0 || preparationTimer[1]>0 || preparationTimer[2]>0)
{
runTimer(preparationTimer);
printTimer(preparationTimer, 4, 1);
}
lcd.clear();
lcd.print("Bomba armada");
lcd.setCursor(0, 1);
lcd.print("Correctamente");
delay(1000);
lcd.clear();
if (adviceToStart==true)
{
playOnGameOver(5);
}
}
void basicTimerMode()
{
byte preparationTimer[3]={0};
byte timer[3]={0};
lcd.clear();
lcd.print("Juego");
lcd.setCursor(0, 1);
lcd.print(" Temporizado");
delay(500);
getPreparationTimer(preparationTimer);
lcd.clear();
lcd.print("TIEMPO DE JUEGO");
tone1.play(NOTE_E6, 200);
delay(800);
setTimer(timer);
runPreparationTimer(preparationTimer);
lcd.clear();
lcd.print("Tiempo Restante");
while (timer[0]>0 || timer[1]>0 || timer[2]>0)
{
runTimer(timer);
printTimer(timer, 4, 1);
}
lcd.clear();
lcd.print("Termino la");
lcd.setCursor(0, 1);
lcd.print(" Partida");
playOnGameOver();
}
void disarmTheBombMode()
{
byte preparationTimer[3]={0};
byte mainTimer[3]={0};
char settedPassword[4];
char enteredPassword[4];
bool correctPassword=false;
byte attempts=3;
byte changeInSec=70;
char key;
bool gameIsOver=false;
lcd.clear();
lcd.print("Desarmar Bomba");
delay(500);
setPassword(settedPassword);
getPreparationTimer(preparationTimer);
lcd.clear();
lcd.print("TIEMPO DE JUEGO");
tone1.play(NOTE_E6, 200);
delay(800);
setTimer(mainTimer);
runPreparationTimer(preparationTimer);
while ((mainTimer[0]>0 || mainTimer[1]>0 || mainTimer[2]>0) && gameIsOver==false)
{
runTimer(mainTimer, true);
if (changeInSec != mainTimer[2])
{
changeInSec= mainTimer[2];
lcd.clear();
lcd.print("Tiempo Restante");
printTimer(mainTimer, 4, 1);
}
key= keypad.getKey();
if (key!=NO_KEY)
{
lcd.clear();
lcd.print("ENTRANDO.");
delay(200);
lcd.print(".");
delay(200);
lcd.print(".");
delay(200);
lcd.clear();
correctPassword= enterPassword(settedPassword, mainTimer);
if (correctPassword==true)
{
gameIsOver=true;
}
else
{
attempts--;
lcd.clear();
lcd.print("intentos: ");
lcd.print(attempts);
delay(500);
if (attempts==0)
{
gameIsOver=true;
}
}
}
}
if (correctPassword==true)
{
lcd.clear();
lcd.print("BIEN HECHO");
lcd.setCursor(0, 1);
lcd.print(" INSECTO");
delay(600);
lcd.clear();
lcd.print("LA BOMBA");
lcd.setCursor(0, 1);
delay(100);
lcd.print("FUE DESACTIVADA");
}
else
{
lcd.clear();
lcd.print("LA RE CAGASTE");
delay(500);
lcd.clear();
lcd.print("LA BOMBA");
lcd.setCursor(0, 1);
delay(100);
lcd.print(" EXPLOTO!");
}
playOnGameOver();
}
//-------------------------------------------------------------------
//------------------ DOMINATION AUXILIAR FUNCTIONS ------------------
//-------------------------------------------------------------------
void addSecToTeam(char& currentTeam, byte blueTimer[3], byte redTimer[3])
{
if (currentTeam == 'B')
{
sumSecsToTimer(blueTimer, 1);
}
else if(currentTeam == 'R')
{
sumSecsToTimer(redTimer, 1);
}
}
void printTimers(byte mainTimer[3], byte blueTimer[3], byte redTimer[3], bool printMain=true)
{
static char timerToPrint= 'B';
if(printMain==true)
{
lcd.clear();
lcd.print("Tiempo ");
printTimer(mainTimer, 7, 0);
}
if (timerToPrint == 'B')
{
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 1);
delay(100);
lcd.print("Azul ");
printTimer(blueTimer, 6, 1);
timerToPrint= 'R';
}
else if(timerToPrint == 'R')
{
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 1);
delay(100);
lcd.print("Rojo: ");
printTimer(redTimer, 6, 1);
timerToPrint= 'B';
}
}
void checkTimeForComeBack(uint32_t& totalSecs, byte currentTimer[3], byte otherTimer[3], bool& gameOver)
{
uint32_t currentSecs= currentTimer[0]*3600 + currentTimer[1]*60 + currentTimer[2];
uint32_t otherSecs= otherTimer[0]*3600 + otherTimer[1]*60 + otherTimer[2];
static const uint32_t halfTotalSecs= totalSecs >> 1;
if (currentSecs > halfTotalSecs)
{
gameOver= true;
}
}
void playDominationGameOver(byte blueTimer[3], byte redTimer[3]) {
byte mainTimer[3]= {0, 0, 200};
byte changeInSec= 0;
char key= NO_KEY;
runTimer(mainTimer, true, true);//reset the runTimer static vars
digitalWrite(relay, HIGH);
while (mainTimer[2]>0 && key != 'A')
{
runTimer(mainTimer, false, true);
key = keypad.getKey();
if (changeInSec != mainTimer[2])
{
changeInSec=mainTimer[2];
printTimers(mainTimer, blueTimer, redTimer, false);
}
digitalWrite(led1, HIGH);
digitalWrite(led2, HIGH);
delay(100);
digitalWrite(led1, LOW);
digitalWrite(led2, LOW);
delay(100);
}
digitalWrite(relay, LOW);
}
//-------------------------------------------------------------------
//------------------ DOMINATION AUXILIAR FUNCTIONS ------------------
//-------------------------------------------------------------------
void dominationMode()
{
byte blueTimer[3]={0};
byte redTimer[3]={0};
byte preparationTimer[3]={0};
byte mainTimer[3]={0};
uint32_t gameTotalSecs;
char currentTeam='0';
char key;
byte changeInSec=70;
bool gameIsOver=false;
lcd.clear();
lcd.print("Dominacion");
delay(500);
getPreparationTimer(preparationTimer);
lcd.clear();
lcd.print("TIEMPO DE JUEGO");
tone1.play(NOTE_E6, 200);
delay(800);
setTimer(mainTimer);
gameTotalSecs= mainTimer[0]*3600 + mainTimer[1]*60+ mainTimer[2];
runPreparationTimer(preparationTimer);
while ((mainTimer[0]>0 || mainTimer[1]>0 || mainTimer[2]>0) && gameIsOver==false)
{
if (currentTeam=='B' || currentTeam=='R')
{
runTimer(mainTimer);
}
if (changeInSec != mainTimer[2])
{
changeInSec= mainTimer[2];
printTimers(mainTimer, blueTimer, redTimer);
addSecToTeam(currentTeam, blueTimer, redTimer);
}
key= keypad.getKey();
if (key == '*')
{
digitalWrite(relay, HIGH);
delay(5000);
currentTeam= 'B';
digitalWrite(relay, LOW);
}
else if (key == 'D')
{
digitalWrite(relay, HIGH);
delay(5000);
currentTeam= 'R';
digitalWrite(relay, LOW);
}
if(currentTeam=='B')
{
checkTimeForComeBack(gameTotalSecs, blueTimer, redTimer, gameIsOver);
}
else if(currentTeam=='R')
{
checkTimeForComeBack(gameTotalSecs, redTimer, blueTimer, gameIsOver);
}
}
if(currentTeam=='B')
{
lcd.clear();
lcd.print(" GANO AZUL");
}
else
{
lcd.clear();
lcd.print(" GANO ROJO");
}
playDominationGameOver(blueTimer, redTimer);
}
byte mainMenu()
{
lcd.clear();
const char rightArrow='>';
const char leftArrow='<';
char key='0';
char gameModes[3][15]={"Temporizador", "Desarma Bomba", "Dominacion"};
lcd.clear();
lcd.print("Modo de juego");
byte i=0;
bool seleccionando=true;
while(seleccionando)
{
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(2, 1);
lcd.print(gameModes[i]);
if (i==0)
{
lcd.setCursor(15, 1);
lcd.print(rightArrow);
}
else if (i==2)
{
lcd.setCursor(0, 1);
lcd.print(leftArrow);
}
else
{
lcd.setCursor(0, 1);
lcd.print(leftArrow);
lcd.setCursor(15, 1);
lcd.print(rightArrow);
}
key= getPressedKey();
if (key=='A')
{
seleccionando=false;
}
else if (key=='*')
{
if (i==0)
{
printInvalidKey(0, 1);
}
else
{
tone1.play(NOTE_B6, 200);//key normal
i--;
}
}
else if (key=='#')
{
if (i==2)
{
printInvalidKey(15, 1);
}
else
{
tone1.play(NOTE_B6, 200);//key normal
i++;
}
}
}
return i;
}