/*
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()