#include <Arduino.h>
#line 1 "C:\\Users\\juanm\\Desktop\\informatica_industrial_giei_ejercicios\\Ejercicios\\tema6_sistemas_de_tiempo_real\\07_fsm_method_1\\07_fsm_method_1.ino"
/*
Author: Juan M. Gandarias
Date: 03/11/2023
Description: 06_fsm_hardware_solution
*/

#define LED_PIN 26    // LED connected to GPIO26
#define BUTTON_PIN 14 // BUTTON connected to GPIO14

hw_timer_t *timer_time = NULL;  // Pointer to hardware timer_time (used for detecting long press)
hw_timer_t *timer_blink = NULL; // Pointer to hardware timer_blink (used for blinking LED)

volatile uint8_t interrupt_counter = 0; // Volatile variable to count how many times the interrupt occurs
volatile uint8_t fsm_state = 0;         // Volatile variable to store the FSM state

// Interrupt flags
volatile bool button_changed = false;
volatile bool long_press = false; // Volatile variable to indicate a long press
volatile bool timer_blink_interrupt = false;
volatile bool button_state = false;

volatile bool first_time_interrupt = true; // To avoid setting long_press on the first timer interrupt

// Callback ISR button is released
#line 48 "C:\\Users\\juanm\\Desktop\\informatica_industrial_giei_ejercicios\\Ejercicios\\tema6_sistemas_de_tiempo_real\\07_fsm_method_1\\07_fsm_method_1.ino"
void setup();
#line 82 "C:\\Users\\juanm\\Desktop\\informatica_industrial_giei_ejercicios\\Ejercicios\\tema6_sistemas_de_tiempo_real\\07_fsm_method_1\\07_fsm_method_1.ino"
void loop();
#line 25 "C:\\Users\\juanm\\Desktop\\informatica_industrial_giei_ejercicios\\Ejercicios\\tema6_sistemas_de_tiempo_real\\07_fsm_method_1\\07_fsm_method_1.ino"
void IRAM_ATTR buttonChange()
{
  button_state = !digitalRead(BUTTON_PIN); // Read the button state, invert it because INPUT_PULLUP is used
  button_changed = true;                   // Set button changed flag
}

// Callback ISR timer 0 interrupt
void IRAM_ATTR timer_timeInterrupt()
{
  if (!first_time_interrupt)
  { // We have to ignore the first interrupt that happens when enabling the timer
    long_press = true;
  }
  first_time_interrupt = false;
}

// Callback ISR timer 1 interrupt
void IRAM_ATTR timer_blinkInterrupt()
{
  timer_blink_interrupt = true;
}

// Setup function
void setup()
{
  Serial.begin(115200);

  // Set modes for LED and BUTTON digital GPIO pins
  pinMode(LED_PIN, OUTPUT);
  pinMode(BUTTON_PIN, INPUT_PULLUP);

  // Initialize interrupt for button press
  attachInterrupt(digitalPinToInterrupt(BUTTON_PIN), buttonChange, CHANGE); // Attach interrupt to button pin for both RISING and FALLING edges

  // Initialize timer_time
  // Parameter 1: Timer we want to use. ESP32 has 4 timers => valid values 0,1,2,3
  // Parameter 2: Prescaler. ESP32 default clock runs at 80MHz. If we set 80, we divide the clock by 80, giving us 1,000,000 ticks/s
  // Parameter 3: true indicates the timer starts ON, false would be OFF (important to put it to false to avoid firing immediately after timerAlarmEnable)
  timer_time = timerBegin(0, 80, true);                         // Timer 0, clock divisor 80
  timerAttachInterrupt(timer_time, &timer_timeInterrupt, true); // Attach the interrupt handler function
  timerAlarmWrite(timer_time, 5e5, true);                       // Interrupt every half second (500ms ON, 500ms OFF)
  timerAlarmDisable(timer_time);                                // Disable timer_time to avoid starting counting immediately

  // Initialize timer_blink
  timer_blink = timerBegin(1, 80, true);                          // Timer 1, clock divisor 80
  timerAttachInterrupt(timer_blink, &timer_blinkInterrupt, true); // Attach the interrupt handler function
  timerAlarmWrite(timer_blink, 1e6, true);                        // Interrupt every second
  timerAlarmDisable(timer_blink);                                 // Disable timer_blink to avoid starting counting immediately

  // Start with the LED off
  digitalWrite(LED_PIN, LOW);

  Serial.print("FSM state: ");
  Serial.println(fsm_state);
}

// Loop function
void loop()
{
  if (button_changed)
  {
    // Check the button state and set flags accordingly
    if (fsm_state == 0 && button_state)
    {
      // Enable timer_time to start counting time
      timerAlarmEnable(timer_time); // Enable timer_time to start counting time
      fsm_state = 1;                // FSM goes to state 1

      // Debug information
      Serial.println("Counting time ON");
      Serial.println("Button pressed");
      Serial.print("FSM state: ");
      Serial.println(fsm_state);
    }
    else if (fsm_state == 1 && !button_state)
    {
      timerAlarmDisable(timer_time); // Disable timer_time to stop counting time
      first_time_interrupt = true;   // Reset first_time_interrupt for next use
      if (long_press)                // Pressed more than 1s
      {
        fsm_state = 3;                 // FSM goes to state 3
        timerAlarmEnable(timer_blink); // Enable timer_blink to start blinking
        long_press = false;            // Reset long_press variable for next use
      }
      else // Pressed less than 1s
      {
        digitalWrite(LED_PIN, HIGH); // Turn LED ON
        fsm_state = 2;               // FSM in state 0 if LED is off, 1 if it's on
      }
      // Debug information
      Serial.println("Button released");
      Serial.print("FSM state: ");
      Serial.println(fsm_state);
    }
    else
    {
      digitalWrite(LED_PIN, LOW);
      fsm_state = 0;
      timerAlarmDisable(timer_blink); // Disable timer_blink to stop blinking
      // Debug information
      Serial.println("Button released");
      Serial.print("FSM state: ");
      Serial.println(fsm_state);
    }
    button_changed = false; // Reset button changed flag
  }

  if (timer_blink_interrupt)
  {
    timer_blink_interrupt = false;
    digitalWrite(LED_PIN, !digitalRead(LED_PIN)); // Toggle LED state
    // Debug information
    Serial.println("Blinking...");
  }

  delay(5); // Small delay added for wokwi
}

