/**************************************************************************
Manuel Córdoba Ramos
Hecho con multicore.
Core 1: interrupciones + FSM (el 1 ejecuta el setup)
Core 0: control de LEDs + pantalla LCD
Es un juego de código secreto.
Se debe pulsar B1,B2,B3, con pulsaciones normales.
Cuando es correcto parpadea 3 veces y se queda encendido,
Si es incorrecto se apagan todos.
Se ha añadido un botón de reset.
***************************************************************************/
#include <LiquidCrystal_I2C.h>
#define I2C_addr 0x27
#define columnsLCD 20
#define rowsLCD 4
LiquidCrystal_I2C lcd(I2C_addr, columnsLCD, rowsLCD);
#define pinL1 2
#define pinL2 4
#define pinL3 16
#define pinB1 25
#define pinB2 26
#define pinB3 27
#define pinBR 33
TaskHandle_t handleTask1; // Definir Handle de la tarea 1
TaskHandle_t handleTask2; // Definir Handle de la tarea 2
hw_timer_t *timerParpadeo = NULL; // Se define el timer
#define tiempoParpadeo (0.25)*1e6 // microsegundos
#define limiteParpadeos ((3)*2 + 1) // numero de parpadeos
// Variables empleadas
volatile int state;
volatile int selectPrint;
volatile bool printLCD;
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 enConmutacion;
/**************************************** ISRs ******************************************/
void IRAM_ATTR ISR_b1(void) {
if (!enConmutacion) {
b1Suelto = true;
}
}
void IRAM_ATTR ISR_b2(void) {
if (!enConmutacion) {
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;
}
numParpadeos++;
}
else {
timerStop(timerParpadeo); // Paro de parpadear
enConmutacion = false;
switch (state) {
case 1:
enciendeL1 = true;
apagaL2 = true;
apagaL3 = true;
break;
case 2:
enciendeL1 = true;
enciendeL2 = true;
apagaL3 = true;
break;
case 3:
enciendeL1 = true;
enciendeL2 = true;
enciendeL3 = 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 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 CompruebaLCD(void) {
if (printLCD) {
printLCD = false;
switch (selectPrint) {
case 0:
SetLcdText("COMIENZA EL", "JUEGO!", "", "");
break;
case 1:
SetLcdText("PRIMER BOTON", "CORRECTO!", "", "");
break;
case 2:
SetLcdText("SEGUNDO BOTON", "CORRECTO!", "", "");
break;
case 3:
SetLcdText("TERCER BOTON", "CORRECTO!", "HAS GANADO", "EL JUEGO!");
break;
default:
break;
}
}
}
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;
enConmutacion = false;
}
void InitFSM(void) {
ResetFlags();
// LEDs
apagaL1 = true;
apagaL2 = true;
apagaL3 = true;
Serial.println("Comienza el juego!");
selectPrint = 0;
printLCD = true;
}
void ToState1(void) {
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
selectPrint = 1;
printLCD = true;
}
void ToState2(void) {
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
selectPrint = 2;
printLCD = true;
}
void ToState3(void) {
Serial.println("Tercer boton correcto!");
Serial.println("Has ganado el juego!");
ResetFlags();
enConmutacion = true;
parpadeoActivoL3 = true;
numParpadeos = 1;
timerRestart(timerParpadeo); // Pone a 0 la cuenta
timerStart(timerParpadeo); // Hace que el timer comience a contar
selectPrint = 3;
printLCD = true;
}
void setup() {
Serial.begin(115200);
Serial.print("Setup realizado por el CORE: ");
Serial.println(xPortGetCoreID()); // Muestra el core que ejecuta la instrucción
// Init 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);
digitalWrite(pinL1, LOW);
digitalWrite(pinL2, LOW);
digitalWrite(pinL3, LOW);
// Interrupciones
attachInterrupt(digitalPinToInterrupt(pinB1), &ISR_b1, RISING);
attachInterrupt(digitalPinToInterrupt(pinB2), &ISR_b2, RISING);
attachInterrupt(digitalPinToInterrupt(pinB3), &ISR_b3, RISING);
attachInterrupt(digitalPinToInterrupt(pinBR), &ISR_bR, RISING);
// Timers
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
// Definicion de tareas
// Creamos la Tarea 1
xTaskCreatePinnedToCore(
FSMThread, // Nombre de la función en la que se implementa la tarea
"Manejar la FSM", // Descripción de la tarea
2048, // Memoria (en Bytes) asignados a esta tarea (no poner muy alta)
NULL, // Parámetro de entrada de la tarea (no hay ningún parámetro)
0, // Prioridad de la tarea (cuanto más alto, más prioridad. No interesa por ahora, sólo cuando haya varias tareas en un mismo core)
&handleTask1, // Handle (manejador) de la tarea (definido arriba)
1); // Core donde va a correr la tarea (Puede ser core 0 o core 1)
// Creamos la Tarea 2
xTaskCreatePinnedToCore(
LEDsThread, // Nombre de la función en la que se implementa la tarea
"Manejar el comportamiento de los LEDs", // Descripción de la tarea
2048, // Memoria (en Bytes) asignados a esta tarea (no poner muy alta)
NULL, // Parámetro de entrada de la tarea (no hay ningún parámetro)
0, // Prioridad de la tarea (cuanto más alto, más prioridad. No interesa por ahora, sólo cuando haya varias tareas en un mismo core)
&handleTask2, // Handle (manejador) de la tarea (definido arriba)
0); // Core donde va a correr la tarea (Puede ser core 0 o core 1)
// Inicializo variables
state = 0;
InitFSM();
}
void loop() {
// put your main code here, to run repeatedly:
delay(10); // this speeds up the simulation
}
void FSMThread (void *parameter) {
while (1) {
switch (state) {
case 0:
if (b1Suelto) {
b1Suelto = false;
state = 1;
ToState1();
}
break;
case 1:
if (b2Suelto) {
b2Suelto = false;
state = 2;
ToState2();
}
if (b1Suelto || b3Suelto || bRSuelto) {
b1Suelto = false;
b3Suelto = false;
bRSuelto = false;
state = 0;
InitFSM();
}
break;
case 2:
if (b3Suelto) {
b3Suelto = false;
state = 3;
ToState3();
}
if (b1Suelto || b2Suelto || bRSuelto) {
b1Suelto = false;
b2Suelto = false;
bRSuelto = false;
state = 0;
InitFSM();
}
break;
case 3:
if (bRSuelto) {
bRSuelto = false;
state = 0;
InitFSM();
}
break;
default:
break;
}
delay(10); // this speeds up the simulation
}
}
void LEDsThread (void *parameter) {
while (1) {
CompruebaLEDs();
CompruebaLCD();
delay(10);
}
}