/*
   parking-access-s02 is demo application of parking access.
      
   Copyright (C) 2023 Maurilio Pizzurro 

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 1, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this library.  If not, see <http://www.gnu.org/licenses/>.
*/

/* 
    Pulsante verde per simulare la barriera IR
    Pulsante giallo per simulare il contatto chiave
*/
// **Todo:*** Usa il motore passo passo al posto del serve 


#include <IRremote.h>
#include <LiquidCrystal.h>

#include <AccelStepper.h>
#include "rail.h"
#define DRV_STEP    4
#define DRV_DIR     6

AccelStepper stepper(AccelStepper::DRIVER, DRV_STEP, DRV_DIR);

#define PIN_RECEIVER 2   // Signal Pin of IR receiver

IRrecv receiver(PIN_RECEIVER);

Rail myRail(stepper);

LiquidCrystal lcd(12, 11, 10, 9, 8, 7);

/* SERVO_UP servo in posizione verticale */
#define SERVO_UP      180
/* SERVO_UP servo in posizione orizzontale */
#define SERVO_DOWN    90

/* PIN_IR_BARRIER contatto pulito relay IR RX */
const byte PIN_IR_BARRIER   = A2;
/* PIN_KEY contatto pulito comando chiave */
const byte PIN_KEY          = A1;

// g_state è la variabile di stato
uint8_t   g_state       = 0;
uint8_t   g_oldState    = 255;
uint8_t   g_servoPos    = SERVO_DOWN;

uint32_t  g_saveMillis  = 0;

bool      g_onEntryState    = false;
bool      barrierSave;
bool      g_enableFlashingLight;
bool      g_from_start = false;

/* S_XXX stati della macchina a stati g_state */
#define S_START     0
#define S_INOPENING 1
#define S_ISUP      2
#define S_INCLOSING 3

#define IRCMD_PLAY  168

/* lcdPrintPos() visualizza su lcd la posizione angolare del servo*/
void lcdPrintPos(uint8_t pos) {
    char posBuff[4];
    posBuff[ 4 - 1 ] = '\0';
    sprintf(posBuff, "%#3u", pos);
    lcd.setCursor(1, 1);
    lcd.print(posBuff);
}

/* lcdPrintState() visualizza su lcd lo stato corrente */
void lcdPrintState(uint8_t n) {
    char *s[] = {"S_START    "
               , "S_INOPENING"
               , "S_ISUP     "
               , "S_INCLOSING"};
   
    lcd.setCursor(0, 0);
    lcd.print("RUN ");
    lcd.setCursor(5, 0);
    lcd.print(s[n]);
}

/* flashingLight() accendo/spegne il lampeggiante */
void flashingLight(bool tf) {
    #define FL_TIMING 200
    static uint32_t timer = 0;
    static uint8_t timing = 0;
    if (!tf) {
        digitalWrite(LED_BUILTIN, LOW);
        digitalWrite(A0, LOW);
        timer = 0;
        timing = 0;
        return;
    }
      
    if (millis() - timer > timing) {
        bool yelIsOn = !digitalRead(A0);
        timer = millis();
        timing = FL_TIMING;
        digitalWrite(LED_BUILTIN, !yelIsOn);
        digitalWrite(A0, yelIsOn);
    }

}

/* lcdPrintTimeout() visualizza su lcd il timeout residuo */
void lcdPrintTimeout(uint16_t t) {
    char timeBuff[5];
    timeBuff[ 5 - 1 ] = '\0';
    sprintf(timeBuff, "%#4u", t);
    lcd.setCursor(11, 1);
    lcd.print(timeBuff);
}

void setup()
{
    Serial.begin(115200);
    pinMode(PIN_IR_BARRIER, INPUT_PULLUP);
    pinMode(PIN_KEY, INPUT_PULLUP);
    pinMode(LED_BUILTIN, OUTPUT);
    pinMode(A0, OUTPUT);

    receiver.enableIRIn(); // Start the receiver

    lcd.begin(16, 2);
    myRail.begin();

    Serial.print("Press play or\nyellow button\n");
       
    lcd.setCursor(0, 1);
    lcd.print((char)223);
    lcd.setCursor(6,1);
    lcd.print("Tms: ");
    lcdPrintPos(myRail.currentPos());
   
}


void loop() {

    myRail.run();    // questo permette al motore di muoversi
    flashingLight(g_enableFlashingLight);

    uint8_t command = 0;

    if (receiver.decode()) {
        command = receiver.decodedIRData.command;
        receiver.resume();  // Receive the next value
    } else if (digitalRead(PIN_KEY) == LOW) {
        command = IRCMD_PLAY;
    } 

    if (g_oldState != g_state) {
        g_oldState = g_state;
        g_onEntryState = true;
        lcdPrintState(g_state);
       
    } else {
        g_onEntryState = false;
    }
        
    switch (g_state) {
        
        case S_START:
            // *** ENTRY STATE ***
            if (g_onEntryState) {
                Serial.println(F("Enter to S_START"));
                
                if (myRail.currentPos() == 0) {
                    g_from_start = true;
                    g_state = S_INCLOSING;
                    break;
                }
            }

            // *** RUN STATE ***
            if (command == IRCMD_PLAY) {
                // !!! CHANGE STATE !!!
                g_state = S_INOPENING;  
                g_from_start = true;
            }

            // *** EXIT STATE ***
            if (g_state != g_oldState) {
                Serial.println(F("Exit from S_START"));
            }         
            break;

        case S_INOPENING:
            // *** ENTRY STATE *** 
            if (g_onEntryState) {
                Serial.println(F("Enter to S_INOPENING"));
                // Se viene da START prepara la partenza ritardata
                if (g_from_start == true) {
                    g_saveMillis = millis();
                    g_enableFlashingLight = true;
                } else {
                    // altrimenti partenza istantanea
                    myRail.open();
                }
            }

            // *** RUN STATE *** 
            // Partenza ritardata di 2000ms     
            if (g_from_start == true) {
                if (millis() - g_saveMillis > 2000) {
                    Serial.println("open now");
                    g_from_start = false;
                    myRail.open();
                }
                // *** BREAK anticipato ***
                break;
            }    

            if (myRail.isOpen()) {
                // !!! CHANGE STATE !!!
                g_state = S_ISUP;  
                g_saveMillis = millis();
            } 
                
            

            lcdPrintPos(myRail.currentPos());
            
            // *** EXIT STATE *** 
            if (g_state != g_oldState) {
                Serial.println(F("Exit from S_INOPENING"));

            } 
            break;

        case S_ISUP:
            // *** ENTRY STATE ***
            if (g_onEntryState) {
                Serial.println(F("Enter to S_ISUP"));
            }
            
            // *** RUN STATE ***
            if (millis() - g_saveMillis > 2000) {
                // !!! CHANGE STATE !!!
                g_state = S_INCLOSING;
                lcdPrintTimeout(0);
            } else {
                uint32_t dt = millis() - g_saveMillis;
                lcdPrintTimeout(2000 - dt);
            }
            // *** CHECK IR BARRIER ***
            if (digitalRead(PIN_IR_BARRIER) == LOW) {
                // *** RESTART TIMER ***
                g_saveMillis = millis();
            }

            // *** EXIT STATE ***
            if (g_state != g_oldState) {
                Serial.println(F("Exit from S_ISUP"));
            }
            break;

        case S_INCLOSING:
            // *** ENTRY STATE ***
            if (g_onEntryState) {
                Serial.println(F("Enter to S_INCLOSING"));
                // Se viene da START prepara la partenza ritardata
                if (g_from_start == true) {
                    g_saveMillis = millis();
                    g_enableFlashingLight = true;
                } else {  
                    // altrimenti partenza istantanea
                    myRail.close();
                }
            }

            // *** RUN STATE ***
            // Partenza ritardata di 2000ms 
            if (g_from_start == true) {
                if (millis() - g_saveMillis > 2000) {
                    Serial.println("close now");
                    g_from_start = false;
                    myRail.close();
                }
                // *** BREAK anticipato ***
                break;
            }    

            lcdPrintPos(myRail.currentPos());
            
            if (myRail.isClosed()) {
                // !!! CHANGE STATE !!!
                g_state = S_START;
            } 
            
            // *** CHECK IR BARRIER ***
            if (digitalRead(PIN_IR_BARRIER) == LOW && !myRail.isStop()) {
                barrierSave = true;
                myRail.stop();
                g_saveMillis = millis();
            }
            // attende 250 ms prima di riaprire
            if (barrierSave && (myRail.isStop()) && millis() - g_saveMillis > 250) {
                // !!! CHANGE STATE !!!
                g_state = S_INOPENING; 
                barrierSave = false;
            }

            // *** EXIT STATE ***
            if (g_state != g_oldState) {
                Serial.println(F("Exit from S_INCLOSING")); 
                g_enableFlashingLight = false;

            }

            break;
    } // end switch
} // end loop()
A4988