/*
Author: Juan M. Gandarias
Date: 03/11/2023
Description: ejemplo_fsm_builder-2
*/
#define LED_PIN 26 // LED conectado a GPIO26
#define BUTTON_PIN 14 // BUTTON conectado a GPIO14
hw_timer_t *timer0 = NULL; // Puntero al timer0 del hardware
hw_timer_t *timer1 = NULL; // Puntero al timer1 del hardware
volatile uint8_t interrupt_counter = 0; // Variable volátil para contar cuántas veces se produce la interrupción
volatile uint8_t fsm_state = 0; // Variable volátil para almacenar el estado de la FSM
volatile bool long_pressed = false; // Variable volátil para detectar cuándo se ha presionado
volatile bool button_pressed = false; // Variable volátil que almacena el estado del botón (true = pulsado)
volatile bool button_interrupted = false; // Variable volátil que indica si el botón se ha interrumpido o no
volatile bool timer0_interrupted = false; // Variable volátil que indica si ha habido una interrupción del timer0
volatile bool timer1_interrupted = false; // Variable volátil que indica si ha habido una interrupción del timer1
// Callback ISR botón cambia de estado (se pulsa o se suelta)
void IRAM_ATTR buttonStateChange()
{
button_interrupted = true; // Se ha interrumpido el botón
button_pressed = !button_pressed; // Almaceno el cambio de estado del botón
}
// Callback ISR interrupción del timer 0
void IRAM_ATTR timer0Interrupt()
{
timer0_interrupted = true; // Se ha interrumpido el timer 0
}
// Callback ISR interrupción del timer 1
void IRAM_ATTR timer1Interrupt()
{
timer1_interrupted = true; // Se ha interrumpido el timer 1
}
// El estado 0 y el 1 ejecutan el mismo código
void functionStates01()
{
if (button_interrupted) {
if (button_pressed) { // Si está pulsado
Serial.println("Button pressed"); // Escribo que el botón se ha presionado
Serial.println("Counting time ON"); // Escribo que se está contando el tiempo de pulsación
timerAlarmEnable(timer1); // Activo el timer1 para que empiece a contar el tiempo
timer1_interrupted = false; // Lo pongo a false para que no cuente la primera interrupción al activar el timer
} else { // Si no está pulsado
Serial.println("Button released"); // Escribo que se ha soltado el botón
if (!long_pressed) // Presionado menos de 1s
{
digitalWrite(LED_PIN, !digitalRead(LED_PIN)); // Se cambia el estado del LED
fsm_state = int(digitalRead(LED_PIN)); // FSM en estado 0 si el LED está apagado, 1 si está encendido (truco para poder usar el mismo código en ambos estados)
Serial.print("Estado: ");
Serial.println(fsm_state); // Escribo que cambio a estado 0 o 1
}
else // Presionado más de 1s
{
fsm_state = 2; // FSM en estado 2
Serial.print("Estado: ");
Serial.println(fsm_state); // Escribo que cambio a estado 2
timerAlarmEnable(timer0); // Activo el timer0 para empezar a parpadear
}
timerAlarmDisable(timer1); // Desactivo el timer1 para dejar de contar tiempo presionado
Serial.println("Counting time OFF");
long_pressed = false; // Se pone a FALSE la variable booleana que indica que la pulsación ha sido larga
}
button_interrupted = false;
}
if (timer1_interrupted) { // Si se interrumpe el timer1
long_pressed = true; // Se pone a TRUE la variable booleana que indica que la pulsación ha sido larga
Serial.println("Pulsación larga");
timer1_interrupted = false;
}
}
// Función setup
void setup()
{
Serial.begin(115200);
// Establecer modos de los GPIO digitales del LED y BUTTON
pinMode(LED_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
// Inicializar interrupción por pulsación del botón
attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), buttonStateChange, CHANGE); // Flanco de bajada = botón se presiona
// Inicializar el timer0
// Parámetro 1: Timer que queremos usar. El ESP32 tiene 4 timers => valores válidos 0,1,2,3
// Parámetro 2: Prescaler. El reloj por defecto del ESP32 va a 80MHz. Si ponemos 80, dividimos el reloj por 80, dándonos 1 000 000 ticks/s
// Parámetro 3: true indica que el timer empieza en ON, false sería OFF
timer0 = timerBegin(0, 80, true); // Timer 0, divisor de reloj 80
timerAttachInterrupt(timer0, &timer0Interrupt, true); // Adjuntar la función de manejo de interrupción
timerAlarmWrite(timer0, 5e5, true); // Interrupción cada medio segundo (500ms ON, 500ms OFF)
timerAlarmDisable(timer0); // La alarma empieza apagada
// Inicializar el timer1
timer1 = timerBegin(1, 80, true); // Timer 1, divisor de reloj 80
timerAttachInterrupt(timer1, &timer1Interrupt, true); // Adjuntar la función de manejo de interrupción
timerAlarmWrite(timer1, 1e6, true); // Interrupción cada segundo
timerAlarmDisable(timer1); // La alarma empieza apagada
// Empezamos con el LED apagado
digitalWrite(LED_PIN, LOW);
}
// Función loop
void loop()
{
switch (fsm_state) {
case 0:
functionStates01();
break;
case 1:
functionStates01();
break;
case 2:
if (button_interrupted && !button_pressed) {// Si se suelta el botón
Serial.println("Button released"); // Escribo que se ha soltado el botón
digitalWrite(LED_PIN, LOW); // Apago el LED
timerAlarmDisable(timer0); // Desactivo el timer0 para dejar a parpadear
timerAlarmDisable(timer1); // Desactivo el timer1 para dejar de contar tiempo presionado
Serial.println("Counting time OFF");
fsm_state = 0; // Vuelvo al estado 0
Serial.print("Estado: ");
Serial.println(fsm_state); // Escribo que cambio a estado 0
long_pressed = false; // Poner a FALSE la variable long_pressed
button_interrupted = false;
}
if (timer0_interrupted) { // Si se interrumpe el timer0
digitalWrite(LED_PIN, !digitalRead(LED_PIN)); // Alternar el estado del LED
timer0_interrupted = false;
}
break;
}
delay(10);
}