// controller state machine state variable
PregsState_t PregsState;
// controller status
PregsStatus_t PregsStatus;
// Debounce state machine state variable
debounceState_t debounceState;
// Switch debounce timer
long lastDebounceTime;
// Switch press status
switch_t switchStatus;
// Seconds timer
int timerSeconds;

// Specify PID control interface
PID PregsPID(&input, &output, &setpoint, kp, ki, kd, DIRECT);
// Specify MAX6675 thermocouple interface
MAX31855 thermocouple(thermocoupleSOPin, thermocoupleCSPin, thermocoupleCLKPin);

void setup()
{
  // SSR pin initialization 
  digitalWrite(ssrPin, LOW);
  pinMode(ssrPin, OUTPUT);
  // Presse Init
  pinMode(PinPresse, OUTPUT);
  digitalWrite(PinPresse, PRESSE_HAUT);
  // Buzzer pin initialization
  digitalWrite(buzzerPin, LOW);
  pinMode(buzzerPin, OUTPUT);
  // LED pins initialization
  digitalWrite(ledRedPin, LOW);
  pinMode(ledRedPin, OUTPUT);
  // Start-up splash
  digitalWrite(buzzerPin, HIGH);
  LCD_init();
  digitalWrite(buzzerPin, LOW);
  delay(2500);
  // Serial communication at 57600 bps
  serial_init();
  // Turn off LED (active low)
  digitalWrite(ledRedPin, HIGH);
  // Set window size
  windowSize = 2000;
  // Initialize time keeping variable
  nextCheck = millis();
  // Initialize thermocouple reading variable
  nextRead = millis();
}

void loop()
{

  // Current time
  unsigned long now;
#ifdef LCD_GRAPH
  currentTime = millis();
#endif
  // Time to read thermocouple?
  if (millis() > nextRead)
  {
    // Read thermocouple next sampling period
    nextRead += SENSOR_SAMPLING_TIME;
    // Read current temperature
    input = thermocouple.readThermocouple(CELSIUS);
    // If thermocouple problem detected
    if ((input == FAULT_OPEN) || (input == FAULT_SHORT_GND) ||
      (input == FAULT_SHORT_VCC))
    {
      // Illegal operation
      PregsState = PREGS_STATE_ERROR;
      PregsStatus = PREGS_STATUS_OFF;
    }
  }

  if (millis() > nextCheck)
  {
    // Check input in the next seconds
    nextCheck += 1000;
    // If reflow process is on going
    if (PregsStatus == PREGS_STATUS_ON)
    {
      // Toggle red LED as system heart beat
      digitalWrite(ledRedPin, !(digitalRead(ledRedPin)));
      // Increase seconds timer for reflow curve analysis
      timerSeconds++;
      Generate_CSV();

    }
    else
    {
      // Turn off red LED
      digitalWrite(ledRedPin, HIGH);
    }
    // effacement du LCD et affichage des infos
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print(lcdMessagesPregsStatus[PregsState]);
    // error state
    if (PregsState == PREGS_STATE_ERROR)
    {
      // No thermocouple 
      int g = 0;
      lcd.clear();
      lcd.print("Thermo-couple:");
      lcd.setCursor(0, 1);
      lcd.print("Erreur !");
#if defined LCD_GRAPH
      graph_active = 0;
#endif
    }
    else
    {
      // Affichafe de la temperature
      lcd.setCursor(0, 1);
      lcd.print(input);
      lcd.write((uint8_t)0);
      lcd.print("C ");
    }
    //calcul initial des bargraph
#ifdef LCD_GRAPH
    if (graph_active == 1){
      lcd_graph_compute();
    }
#endif
  }

  // Etat de la machine
  switch (PregsState)
  {
  case PREGS_STATE_IDLE: //en attente
    //si la machine est au dessus de la temperature initiale:
    if (input >= TEMPERATURE_ROOM)
    {
      // Afficher "Attendre, chaud"	
      PregsState = PREGS_STATE_TOO_HOT;
    }
    else
    {
      // Si le switch "Start" est press?:
      if (switchStatus == SWITCH_1)
      {
        // Envois du flag de debut log
        Serial.println("Time Setpoint Input Output Presse");
        // initialisation du timer
        timerSeconds = 0;
        // Initialisation des Control PID 
        windowStartTime = millis();
        // Configuration de la temperature de pr?chauffage
        setpoint = TEMPERATURE_PREHEAT;
        // Configuration du sous-programme PID
        PregsPID.SetOutputLimits(0, windowSize);
        PregsPID.SetSampleTime(PID_SAMPLE_TIME);
        // Turn the PID on
        PregsPID.SetMode(AUTOMATIC);
        // Amorce le processus "pr?-chauffage
        PregsState = PREGS_STATE_PREHEAT;
      }
    }
    break;

  case PREGS_STATE_PREHEAT: // Pre-chauffage
    PregsStatus = PREGS_STATUS_ON; //mise en route du chauffage
    PregsPID.SetTunings(PID_KP_PREHEAT, PID_KI_PREHEAT, PID_KD_PREHEAT); //activation des pid de ce step
    // Si la temperature mesuree est celle de la fin du prechauffage passer a l'etape suivante...
    // Sinon continuer le prechauffage
    if (input >= TEMPERATURE_PREHEAT - PREHEAT_DiNAMIC)
    {
      // la temperature est celle de fin de prechauffage:
      previousMillis1 = millis();
      PregsState = PREGS_STATE_RAMP; //passer a l'etape "Rampe"
    }
    break;

  case PREGS_STATE_RAMP: // rampe
    PregsStatus = PREGS_STATUS_ON;
    elapsedTime = (millis() - (previousMillis1));
    minutot1 = (elapsedTime / 1000) / 60;  // minutes
    secotot1 = (elapsedTime / 1000) % 60; // secondes
    centitot1 = (elapsedTime % 1000) / 10;  //centieme 

    if (setpoint < TEMPERATURE_RAMP_MAX){
      //si la temperature maximale de la rampe n'est pas atteinte:
      if (secotot1 > RAMPE_STEP) // si le step est atteind (intervales de temps de montee de temperature)
      {
        //augmenter la valeur de consigne
        setpoint += RAMP_TEMPERATURE_STEP; //point a atteindre egale point precedent + graduation rampe
        PregsPID.SetTunings(PID_KP_RAMP, PID_KI_RAMP, PID_KD_RAMP); // activation des PID de cette fonction
        previousMillis1 = millis();//remise a 0 du compteur de step
      }
    }
    //si la temperature maximale de la rampe est atteinte:
    if (setpoint >= TEMPERATURE_RAMP_MAX)
    {
      previousMillis1 = millis(); //remise a 0 du chrono
      PregsState = PREGS_STATE_STAB; // passer a l'etape de stabilisation

    }
    break;

  case PREGS_STATE_STAB: //stabilisation
    PregsStatus = PREGS_STATUS_ON;
    elapsedTime = (millis() - (previousMillis1));
    centitot1 = (elapsedTime % 1000) / 10; //centieme to display
    secotot1 = (elapsedTime / 1000) % 60;  // second to display
    minutot1 = (elapsedTime / 1000) / 60;  // minute to display
    if (minutot1<STAB_TEMPO){ //la fin stabilisation n'est pas atteinte, regler les pid, et la consigne a atteindre
      PregsPID.SetTunings(PID_KP_STAB, PID_KI_STAB, PID_KD_STAB);
      setpoint = TEMPERATURE_STAB + STAB_COMPENSATION;
    }
    if (minutot1 >= STAB_TEMPO){// la fin stabilisation
      previousMillis1 = millis(); //remise a 0 des compteurs

        //***** Activation presse ******
      digitalWrite(PinPresse, PRESSE_BAS);
      presse_status = 1;

      //Passer a l'etape "cuisson"
      PregsState = PREGS_STATE_CUIS;
    }

    break;

  case PREGS_STATE_CUIS: // "Cuisson"
    setpoint = TEMPERATURE_CUIS;
    PregsPID.SetTunings(PID_KP_CUIS, PID_KI_CUIS, PID_KD_CUIS);
    PregsStatus = PREGS_STATUS_ON;
    elapsedTime = (millis() - (previousMillis1));
    minutot1 = (elapsedTime / 1000) / 60;  // minute to display
    secotot1 = (elapsedTime / 1000) % 60;  // second to display
    centitot1 = (elapsedTime % 1000) / 10; //centieme to display
    //attendre la fin de temporisation de cuisson
    if (minutot1 >= CUIS_TEMPO){
      PregsState = PREGS_STATE_COOL;//Fin de cuisson, saut a l'etape de refroidissement
    }
    break;

  case PREGS_STATE_COOL:
    setpoint = TEMPERATURE_COOL_MIN;
    PregsPID.SetMode(AUTOMATIC);
    PregsPID.SetTunings(1, 1, 1);
    delay(1000);
    //***** Ouverture presse ****
    if (input <= TEMPERATURE_OUVERTURE_PRESSE){ //si la temperature d'ouverture de la presse est atteinte, ouvrir, sinon attendre cycle suivant
      digitalWrite(PinPresse, PRESSE_HAUT);
      presse_status = 0;
    }

    if (input <= TEMPERATURE_COOL_MIN) //si la temperature de refroidissement est atteinte:
    {
      buzzerPeriod = millis() + 1000;
      // Turn on buzzer LED to indicate completion
      digitalWrite(buzzerPin, HIGH);
      // Arret du proc?ssus
      PregsStatus = PREGS_STATUS_OFF;
      //affichage fin de processus
      PregsState = PREGS_STATE_COMPLETE;
    }
    break;

  case PREGS_STATE_COMPLETE:
    if (millis() > buzzerPeriod)
    {
      // Turn off buzzer and green LED
      digitalWrite(buzzerPin, LOW);
      PregsState = PREGS_STATE_IDLE;//retour en attente 
    }
    break;

  case PREGS_STATE_TOO_HOT:
    if (input < TEMPERATURE_ROOM)
    {
      PregsState = PREGS_STATE_IDLE;
    }
    break;

  case PREGS_STATE_ERROR:
    // If thermocouple problem is still present
    if ((input == FAULT_OPEN) || (input == FAULT_SHORT_GND) ||
      (input == FAULT_SHORT_VCC))
    {
      PregsState = PREGS_STATE_ERROR;
    }
    else
    {
      PregsState = PREGS_STATE_IDLE;
    }
    break;
  }

  // If switch 1 is pressed
  if (switchStatus == SWITCH_1)
  {
    if (PregsStatus == PREGS_STATUS_ON)
    {
      PregsStatus = PREGS_STATUS_OFF;
      PregsState = PREGS_STATE_IDLE;
    }
  }

  switch (debounceState)
  {
  case DEBOUNCE_STATE_IDLE:
    switchStatus = SWITCH_NONE;
    if (analogRead(switchPin) == 0)
    {
      lastDebounceTime = millis();
      debounceState = DEBOUNCE_STATE_CHECK;
    }
    break;

  case DEBOUNCE_STATE_CHECK:
    if (analogRead(switchPin) == 0)
    {
      if ((millis() - lastDebounceTime) > DEBOUNCE_PERIOD_MIN)
      {
        debounceState = DEBOUNCE_STATE_RELEASE;
      }
    }
    else
    {
      debounceState = DEBOUNCE_STATE_IDLE;
    }
    break;

  case DEBOUNCE_STATE_RELEASE:
    if (analogRead(switchPin) > 0)
    {
      switchStatus = SWITCH_1;
      debounceState = DEBOUNCE_STATE_IDLE;
    }
    break;
  }

  // PID computation and SSR control
  if (PregsStatus == PREGS_STATUS_ON)
  {
    now = millis();

    PregsPID.Compute();

    if ((now - windowStartTime) > windowSize)
    {
      windowStartTime += windowSize;
    }
    if (output > (now - windowStartTime)) digitalWrite(ssrPin, HIGH);
    else digitalWrite(ssrPin, LOW);
  }
  else
  {
    digitalWrite(ssrPin, LOW);
  }
}

