#include <LiquidCrystal_I2C.h>
// Pantalla LCD
#define I2C_addr 0x27
#define columnsLCD 20
#define rowsLCD 4
LiquidCrystal_I2C lcd(I2C_addr, columnsLCD, rowsLCD);
// Timers
hw_timer_t *timerButton = NULL; // Se define el timer
hw_timer_t *timerParpadeo = NULL; // Se define el timer
#define tiempoParpadeo (0.5)*1e6 // microsegundos
#define limiteParpadeos ((3)*2 + 1) // numero de parpadeos
#define tiempoPulsacionLarga (2)*1e3 // milisegundos
unsigned long tiempoPulsado;
// Botones
#define pinB1 34
#define pinB2 35
#define pinB3 32
#define pinBR 33
// LEDs
#define pinL1 2
#define pinL2 4
#define pinL3 16
// Variables empleadas
volatile int state;
volatile bool conmutaL1;
volatile bool conmutaL2;
volatile bool conmutaL3;
volatile bool enciendeL1;
volatile bool enciendeL2;
volatile bool enciendeL3;
volatile bool apagaL1;
volatile bool apagaL2;
volatile bool apagaL3;
volatile bool parpadeoActivoL1;
volatile bool parpadeoActivoL2;
volatile bool parpadeoActivoL3;
volatile int numParpadeos;
volatile bool b1Suelto;
volatile bool b2Suelto;
volatile bool b3Suelto;
volatile bool bRSuelto;
volatile bool b2Pulsado;
volatile bool enConmutacion;
/**************************************** ISRs ******************************************/
void IRAM_ATTR ISR_b1(void) {
if (!enConmutacion) {
b1Suelto = true;
}
}
void IRAM_ATTR ISR_b2(void) {
if (!enConmutacion) {
if (digitalRead(pinB2) == 0) {
b2Pulsado = true;
} else {
b2Suelto = true;
}
}
}
void IRAM_ATTR ISR_b3(void) {
if (!enConmutacion) {
b3Suelto = true;
}
}
void IRAM_ATTR ISR_bR(void) {
if (!enConmutacion) {
bRSuelto = true;
}
}
void IRAM_ATTR ISR_ConmutaLed(void) {
if (numParpadeos < limiteParpadeos) {
if (parpadeoActivoL1)
{
conmutaL1 = true;
}
if (parpadeoActivoL2)
{
conmutaL2 = true;
}
if (parpadeoActivoL3)
{
conmutaL3 = true;
}
if (state != 4) {
numParpadeos++;
}
}
else {
timerStop(timerParpadeo); // Paro de parpadear
enConmutacion = false;
switch (state) {
case 1:
apagaL1 = true;
apagaL2 = true;
apagaL3 = true;
break;
case 2:
enciendeL1 = true;
apagaL2 = true;
apagaL3 = true;
break;
case 3:
enciendeL1 = true;
enciendeL2 = true;
apagaL3 = true;
break;
default:
break;
}
}
}
/**************************************** Funciones **************************************/
/*
PrintRow() escribe en la línea especificada el texto pasado como parámetro.
El texto aparecerá centrado en la fila.
*/
void PrintRow(const int row, const String text) {
String textRow = text;
// Calcula la posición inicial para que el texto
// aparezca centrado en la fila
const int startingPosition = (columnsLCD - textRow.length()) / 2;
// Muestra el texto centrado en la fila
lcd.setCursor(startingPosition, row);
lcd.print(textRow);
}
/*
SetLcdText() escribe en la pantalla LCD
el texto pasado como parámetro para cada fila.
*/
void SetLcdText(const String textR1,
const String textR2,
const String textR3,
const String textR4) {
lcd.clear();
PrintRow(0, textR1);
PrintRow(1, textR2);
PrintRow(2, textR3);
PrintRow(3, textR4);
}
void ResetFlags(void) {
enciendeL1 = false;
enciendeL2 = false;
enciendeL3 = false;
apagaL1 = false;
apagaL2 = false;
apagaL3 = false;
conmutaL1 = false;
conmutaL2 = false;
conmutaL3 = false;
parpadeoActivoL1 = false;
parpadeoActivoL2 = false;
parpadeoActivoL3 = false;
b1Suelto = false;
b2Suelto = false;
b3Suelto = false;
bRSuelto = false;
b2Pulsado = false;
enConmutacion = false;
}
void InitFSM(void) {
// LEDs
digitalWrite(pinL1, LOW);
digitalWrite(pinL2, LOW);
digitalWrite(pinL3, LOW);
SetLcdText("Comienza el juego", "CODIGO", "SECRETO!", "");
Serial.println("Comienza el juego: CODIGO SECRETO!");
ResetFlags();
}
void ToState2(void) {
// LEDs
digitalWrite(pinL1, LOW);
digitalWrite(pinL2, LOW);
digitalWrite(pinL3, LOW);
SetLcdText("PRIMER BOTON", "CORRECTO!", "", "");
Serial.println("Primer boton correcto!");
ResetFlags();
enConmutacion = true;
parpadeoActivoL1 = true;
numParpadeos = 1;
timerRestart(timerParpadeo); // Pone a 0 la cuenta
timerStart(timerParpadeo); // Hace que el timer comience a contar
}
void From1To1(void) {
// LEDs
digitalWrite(pinL1, LOW);
digitalWrite(pinL2, LOW);
digitalWrite(pinL3, LOW);
SetLcdText("PRIMER BOTON", "INCORRECTO!", "", "");
Serial.println("Primer boton incorrecto!");
ResetFlags();
enConmutacion = true;
parpadeoActivoL1 = true;
numParpadeos = 1;
timerRestart(timerParpadeo); // Pone a 0 la cuenta
timerStart(timerParpadeo); // Hace que el timer comience a contar
}
void From2To1(void) {
// LEDs
digitalWrite(pinL1, HIGH);
digitalWrite(pinL2, LOW);
digitalWrite(pinL3, LOW);
SetLcdText("SEGUNDO BOTON", "INCORRECTO!", "", "");
Serial.println("Segundo boton incorrecto!");
ResetFlags();
enConmutacion = true;
parpadeoActivoL2 = true;
numParpadeos = 1;
timerRestart(timerParpadeo); // Pone a 0 la cuenta
timerStart(timerParpadeo); // Hace que el timer comience a contar
}
void From3To1(void) {
// LEDs
digitalWrite(pinL1, HIGH);
digitalWrite(pinL2, HIGH);
digitalWrite(pinL3, LOW);
SetLcdText("TERCER BOTON", "INCORRECTO!", "", "");
Serial.println("Tercer boton incorrecto!");
ResetFlags();
enConmutacion = true;
parpadeoActivoL3 = true;
numParpadeos = 1;
timerRestart(timerParpadeo); // Pone a 0 la cuenta
timerStart(timerParpadeo); // Hace que el timer comience a contar
}
void ToState3(void) {
// LEDs
digitalWrite(pinL1, HIGH);
digitalWrite(pinL2, LOW);
digitalWrite(pinL3, LOW);
SetLcdText("SEGUNDO BOTON", "CORRECTO!", "", "");
Serial.println("Segundo boton correcto!");
ResetFlags();
enConmutacion = true;
parpadeoActivoL2 = true;
numParpadeos = 1;
timerRestart(timerParpadeo); // Pone a 0 la cuenta
timerStart(timerParpadeo); // Hace que el timer comience a contar
}
void ToState4(void) {
// LEDs
digitalWrite(pinL1, LOW);
digitalWrite(pinL2, LOW);
digitalWrite(pinL3, LOW);
SetLcdText("TERCER BOTON", "CORRECTO!", "HAS GANADO", "EL JUEGO!");
Serial.println("Tercer boton correcto!");
Serial.println("Has ganado el juego!");
ResetFlags();
parpadeoActivoL1 = true;
parpadeoActivoL2 = true;
parpadeoActivoL3 = true;
numParpadeos = 1;
timerRestart(timerParpadeo); // Pone a 0 la cuenta
timerStart(timerParpadeo); // Hace que el timer comience a contar
}
void CompruebaLEDs(void) {
if (conmutaL1) {
conmutaL1 = false;
digitalWrite(pinL1, !digitalRead(pinL1));
}
if (conmutaL2) {
conmutaL2 = false;
digitalWrite(pinL2, !digitalRead(pinL2));
}
if (conmutaL3) {
conmutaL3 = false;
digitalWrite(pinL3, !digitalRead(pinL3));
}
if (enciendeL1) {
enciendeL1 = false;
digitalWrite(pinL1, HIGH);
}
if (enciendeL2) {
enciendeL2 = false;
digitalWrite(pinL2, HIGH);
}
if (enciendeL3) {
enciendeL3 = false;
digitalWrite(pinL3, HIGH);
}
if (apagaL1) {
apagaL1 = false;
digitalWrite(pinL1, LOW);
}
if (apagaL2) {
apagaL2 = false;
digitalWrite(pinL2, LOW);
}
if (apagaL3) {
apagaL3 = false;
digitalWrite(pinL3, LOW);
}
}
void setup() {
// put your setup code here, to run once:
Serial.begin(115200);
// Pantalla LCD
lcd.init();
lcd.backlight();
// Definicion I/0
pinMode(pinB1, INPUT_PULLUP);
pinMode(pinB2, INPUT_PULLUP);
pinMode(pinB3, INPUT_PULLUP);
pinMode(pinBR, INPUT_PULLUP);
pinMode(pinL1, OUTPUT);
pinMode(pinL2, OUTPUT);
pinMode(pinL3, OUTPUT);
// Interrupciones
attachInterrupt(digitalPinToInterrupt(pinB1), &ISR_b1, RISING);
attachInterrupt(digitalPinToInterrupt(pinB2), &ISR_b2, CHANGE);
attachInterrupt(digitalPinToInterrupt(pinB3), &ISR_b3, RISING);
attachInterrupt(digitalPinToInterrupt(pinBR), &ISR_bR, RISING);
// Timers
timerButton = timerBegin(1000000);
timerStop(timerButton); // Para el timer
timerParpadeo = timerBegin(1000000); // Se pone directamente la frecuencia en Hz
timerAttachInterrupt(timerParpadeo, &ISR_ConmutaLed);
timerAlarm(timerParpadeo, tiempoParpadeo, true, 0); // (Se pone el número hasta el que cuenta: 1e6 = 1s)
timerStop(timerParpadeo); // Para el timer
// Inicializo variables
state = 1;
InitFSM();
}
void loop() {
switch (state) {
case 1:
CompruebaLEDs();
if (b1Suelto) {
b1Suelto = false;
state = 2;
ToState2();
}
if (bRSuelto) {
bRSuelto = false;
state = 1;
InitFSM();
}
if (b2Suelto || b3Suelto) {
b2Suelto = false;
b3Suelto = false;
From1To1();
}
break;
case 2:
CompruebaLEDs();
if (b2Pulsado) {
b2Pulsado = false;
timerRestart(timerButton); // Pone a 0 la cuenta
timerStart(timerButton); // Hace que el timer comience a contar
}
if (b2Suelto) {
b2Suelto = false;
tiempoPulsado = timerReadMillis(timerButton);
timerStop(timerButton);
if (tiempoPulsado > tiempoPulsacionLarga) {
state = 3;
ToState3();
} else {
state = 1;
From2To1();
}
}
if (bRSuelto) {
bRSuelto = false;
state = 1;
InitFSM();
}
if (b1Suelto || b3Suelto) {
b1Suelto = false;
b3Suelto = false;
state = 1;
From2To1();
}
break;
case 3:
CompruebaLEDs();
if (b3Suelto) {
b3Suelto = false;
state = 4;
ToState4();
}
if (bRSuelto) {
bRSuelto = false;
state = 1;
InitFSM();
}
if (b1Suelto || b2Suelto) {
b1Suelto = false;
b2Suelto = false;
state = 1;
From3To1();
}
break;
case 4:
CompruebaLEDs();
if (bRSuelto) {
bRSuelto = false;
state = 1;
InitFSM();
}
break;
default:
break;
}
delay(10); // this speeds up the simulation
}