#include <Arduino.h>
#include <Servo.h>
// Define state constants
#define IDLE 0
#define TRAIN_APPROACH 1
#define TRAIN_CROSSING 2
#define TRAIN_EXIT 3
// Initial state
volatile int state = IDLE;  // Made volatile to ensure proper access across interrupts
// Define GPIO pins
const int S1_PIN = 8;  // GPIO pin for Sensor 1
const int S2_PIN = 9;  // GPIO pin for Sensor 2
const int S3_PIN = 13;  // GPIO pin for Sensor 3
const int GATE_SERVO1_PIN = 16;  // GPIO pin for the first gate servo
const int GATE_SERVO2_PIN = 17;  // GPIO pin for the second gate servo
// Define GPIO pins for the LEDs
const int LED1_PIN = 18;  // GPIO pin for LED 1
const int LED2_PIN = 19;  // GPIO pin for LED 2
// Servo objects
Servo gateServo1;
Servo gateServo2;
// Timing variables for LED blinking
unsigned long previousMillis = 0;
const long interval = 500;  // Interval at which to blink (in milliseconds)
bool led1State = LOW;
bool led2State = HIGH;
// Interrupt flags
volatile bool S1_triggered = false;
volatile bool S2_triggered = false;
volatile bool S3_triggered = false;
// Flags to manage gate state
bool gatesClosed = false;
bool gatesOpened = false;
void setup() {
  // Start serial communication for debugging
  Serial1.begin(115200);
  // Initialize sensors and attach interrupts
  attachInterrupt(digitalPinToInterrupt(S1_PIN), triggerS1, RISING);  // Trigger on rising edge
  attachInterrupt(digitalPinToInterrupt(S2_PIN), triggerS2, RISING);  // Trigger on rising edge
  attachInterrupt(digitalPinToInterrupt(S3_PIN), triggerS3, RISING);  // Trigger on rising edge
  // Attach the servos to the GPIO pins
  gateServo1.attach(GATE_SERVO1_PIN);
  gateServo2.attach(GATE_SERVO2_PIN);
  // Initialize the gates to open position
  openGates();
  // Initialize LEDs as output pins
  pinMode(LED1_PIN, OUTPUT);
  pinMode(LED2_PIN, OUTPUT);
  
  // Ensure LEDs are off initially
  digitalWrite(LED1_PIN, LOW);
  digitalWrite(LED2_PIN, LOW);
  Serial1.println("System initialized. Gates open.");
}
void loop() {
  unsigned long currentMillis = millis();
  
  switch(state) {
    case IDLE:
      if (S1_triggered) {
        S1_triggered = false;  // Reset the flag
        Serial1.println("Sensor 1 triggered. Train is approaching.");
        closeGates();  // Close gates as soon as S1 is triggered
        gatesClosed = true;
        blinkCrossingLights(currentMillis);  // Start blinking lights as soon as S1 is triggered
        state = TRAIN_APPROACH;
      }
      break;
    case TRAIN_APPROACH:
      blinkCrossingLights(currentMillis);
      if (S2_triggered) {
        S2_triggered = false;  // Reset the flag
        Serial1.println("Sensor 2 triggered. Train at crossing.");
        state = TRAIN_CROSSING;
      }
      break;
    case TRAIN_CROSSING:
      blinkCrossingLights(currentMillis);
      if (S3_triggered) {
        S3_triggered = false;  // Reset the flag
        Serial1.println("Sensor 3 triggered. Train has cleared crossing.");
        if (gatesClosed) {
          openGates();
          gatesOpened = true;
          gatesClosed = false;
        }
        state = TRAIN_EXIT;
      }
      break;
    case TRAIN_EXIT:
      blinkCrossingLights(currentMillis);
      // Adding a delay to ensure the train has passed
      delay(2000); // 2 seconds delay to allow the train to exit completely
      Serial1.println("Train has fully exited. Returning to IDLE.");
      turnOffLights();  // Turn off the lights when returning to IDLE
      gatesOpened = false;
      state = IDLE;
      break;
  }
  delay(100);  // Small delay to debounce sensors
}
// Servo control functions
void closeGates() {
  Serial1.println("Closing gates.");
  // Set both servos to 90 degrees (close position)
  gateServo1.write(90);
  gateServo2.write(90);
  delay(1000);  // Small delay to allow servos to reach the position
}
void openGates() {
  Serial1.println("Opening gates.");
  // Set both servos to 0 degrees (open position)
  gateServo1.write(0);
  gateServo2.write(0);
  delay(1000);  // Small delay to allow servos to reach the position
}
// Function to blink the LEDs alternately
void blinkCrossingLights(unsigned long currentMillis) {
  if (currentMillis - previousMillis >= interval) {
    // Save the last time you blinked the LEDs
    previousMillis = currentMillis;
    // Toggle the LED states
    led1State = !led1State;
    led2State = !led2State;
    // Set the LEDs according to the toggled states
    digitalWrite(LED1_PIN, led1State);
    digitalWrite(LED2_PIN, led2State);
  }
}
// Function to turn off the lights when the train has exited
void turnOffLights() {
  digitalWrite(LED1_PIN, LOW);
  digitalWrite(LED2_PIN, LOW);
  Serial1.println("All lights turned off.");
}
// Interrupt service routines (ISRs)
void triggerS1() {
  S1_triggered = true;
}
void triggerS2() {
  S2_triggered = true;
}
void triggerS3() {
  S3_triggered = true;
}