//level crossing multitrack 2may update
#define GATE_SPEED          30 // [ms] lower number is higher servo speed
#define BLINK_SPEED         400 // [ms] smaller number is faster blinking
#define GATE_DELAY         1500 // [ms] time between start blinking and gate closing
#define END_OF_TRAIN_DELAY 2000 // [ms] time to wait before deciding this was the end of the train
#define GATE_OPEN_ANGLE_1    90
#define GATE_CLOSED_ANGLE_1  10
#define GATE_OPEN_ANGLE_2    90
#define GATE_CLOSED_ANGLE_2  10
#define SERVO1_PIN           12
#define SERVO2_PIN           11
#define LED1_PIN              8
#define LED2_PIN              9
#define NUM_SENSORS           6 // two sensors per track, one left and one right of the gate
byte sensor_pin[NUM_SENSORS]  = {2,3,4,5,6,7}; // sensor pin numbers

byte state = 1;
byte train_counter, n;
byte led1, led2, blink_enabled;
byte    angle1 = GATE_OPEN_ANGLE_1;
byte setpoint1 = GATE_OPEN_ANGLE_1;
byte    angle2 = GATE_OPEN_ANGLE_2;
byte setpoint2 = GATE_OPEN_ANGLE_2;
byte sensor_state[NUM_SENSORS]; // 0 idle, 1 detect arrival, 2 detect departure, 3 detect end of train
byte end_of_train[NUM_SENSORS]; // 0 idle, 1 end of train detected
unsigned long time_to_blink;
unsigned long time_to_close_gate;
unsigned long time_for_servo_step;
unsigned long time_end_of_train[NUM_SENSORS];

#include <Servo.h>
Servo gate_servo1;
Servo gate_servo2;

void setup() {
  pinMode(LED1_PIN, OUTPUT);
  pinMode(LED2_PIN, OUTPUT);
  for (byte i = 0; i < NUM_SENSORS; i++) pinMode(sensor_pin[i], INPUT_PULLUP);
  gate_servo1.attach(SERVO1_PIN);
  gate_servo1.write(angle1);
  gate_servo2.attach(SERVO2_PIN);
  gate_servo2.write(angle2);
  delay(1000);
  gate_servo1.detach();
  gate_servo2.detach();
  Serial.begin(9600);
  Serial.println("Railway Crossing Control Ready");
  Serial.println();
  Serial.println("Waiting for train");
  for (byte i = 0; i < NUM_SENSORS; i++) sensor_state[i] = 1; // enable sensors for train detection
}

void loop() {

  for (byte i = 0; i < NUM_SENSORS; i++) {
    if(sensor_state[i] == 1) { // detect arrival of new train
      if(!digitalRead(sensor_pin[i])) { // train detected
        train_counter++;
        sensor_state[i] = 0;
        if(i%2) n = i - 1; else n = i + 1;
        sensor_state[n] = 2; // buddy sensor departure detection enabled
        Serial.print("Arrival:   ");
        Serial.println(i);
        Serial.print("Trains:    ");
        Serial.println(train_counter);
      }
    }
    else if(sensor_state[i] > 1) {
      if(!digitalRead(sensor_pin[i])) { // departure detected
        time_end_of_train[i] = millis() + (unsigned long)END_OF_TRAIN_DELAY;
        if(i%2) n = i - 1; else n = i + 1;
        sensor_state[n] = 1; // buddy sensor enabled again
        if(sensor_state[i] == 2) {
          Serial.print("Departure: ");
          Serial.println(i);
        }
        sensor_state[i] = 3;
      }
      if(sensor_state[i] == 3) // decide if end of train has passed based on a timer
        if(millis() > time_end_of_train[i]) end_of_train[i] = 1; 
      if(end_of_train[i]) { // this takes care train_counter-- is executed only once
        train_counter--;
        end_of_train[i] = 0;
        sensor_state[i] = 1;
        Serial.print("Trains:    ");
        Serial.println(train_counter);
      }
    }
  }

  switch (state) {
    case 1: // Gates open. Not blinking. Waiting for train.
      if(train_counter) { // A train is detected.
      Serial.println("Binking started");
      blink_enabled = 1;
      time_to_close_gate = millis() + (unsigned long)GATE_DELAY;
      state = 2;
      }
    break;
      
    case 2: // Blinking. Wait until it's time to close the gates
      if (millis() > time_to_close_gate) { // Gate delay time has passed
        Serial.println("Gate1 closing");
        gate_servo1.attach(SERVO1_PIN);
        setpoint1 = GATE_CLOSED_ANGLE_1;
        state = 3;
      }
    break;

    case 3: // Gate1 closing. Blinking.
      if(angle1 == setpoint1) { // Gate1 closed
        gate_servo1.detach();
        Serial.println("Gate1 closed");
        Serial.println("Gate2 closing");
        gate_servo2.attach(SERVO2_PIN);
        setpoint2 = GATE_CLOSED_ANGLE_2;
        state = 4;
      }
    break;

    case 4: // Gate2 closing. Blinking.
      if(angle2 == setpoint2) { // Gate2 is fully closed
        gate_servo2.detach();
        Serial.println("Gate2 closed");
        state = 5;
      }
    break;
    
    case 5: // Gate2 closed. Blinking. Wait until all trains are gone.
      if(!train_counter) { // End of last train detected
        Serial.println("Gate2 opening");
        gate_servo2.attach(SERVO2_PIN);
        setpoint2 = GATE_OPEN_ANGLE_2;        
      state = 6;
      }
    break;
    
    case 6: // Gate2 opening. Blinking.
      if(train_counter) state = 3; // Another train is coming
      else if(angle2 == setpoint2) { // Gate2 open
        gate_servo2.detach();
        Serial.println("Gate2 open");
        Serial.println("Gate1 opening");
        gate_servo1.attach(SERVO1_PIN);
        setpoint1 = GATE_OPEN_ANGLE_1;
        state = 7;
      }
    break;
    
    case 7: // Gate1 opening. Blinking.
      if(train_counter) state = 2; // Another train is coming
      else if(angle1 == setpoint1) { // Gate1 open
        gate_servo1.detach();
        Serial.println("Gate1 open");
        Serial.println("Stop blinking");
        blink_enabled = 0; // Stop blinking
        led1 = 0;
        led2 = 0;
        Serial.println();
        Serial.println("Waiting for train");
        state = 1;
      }
    break;
  }

  if (millis() > time_for_servo_step) {
    time_for_servo_step = millis() + (unsigned long)GATE_SPEED;
    if (angle1 < setpoint1) angle1++;
    if (angle1 > setpoint1) angle1--;
    if (angle2 < setpoint2) angle2++;
    if (angle2 > setpoint2) angle2--;
    gate_servo1.write(angle1);
    gate_servo2.write(angle2);
  }
    
  if(blink_enabled == 1) {
    if(millis() > time_to_blink) {
      time_to_blink = millis() + (unsigned long)BLINK_SPEED;
      led1 = !led1;
      led2 = !led1;
    }
  }

  digitalWrite(LED1_PIN, led1);
  digitalWrite(LED2_PIN, led2);
}