/*
parking-access-s02 is demo application of parking access.
Copyright (C) 2022 Maurilio Pizzurro
Modified by Claudio Fin 05/06/2022 (for multiple states active)
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
*/
#include <IRremote.h>
#include <LiquidCrystal.h>
#include <Servo.h>
#define PIN_RECEIVER 2 // Signal Pin of IR receiver
IRrecv receiver(PIN_RECEIVER);
Servo RCservo;
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 = 4;
/* PIN_KEY contatto pulito comando chiave */
const byte PIN_KEY = 6;
#define NUMSTATES 7
#define NUMTIMERS 2
// g_state è la variabile di stato
bool g_state[NUMSTATES] = { false }; // stati attivi attuali
bool g_oldState[NUMSTATES] = { false }; // copia g_state per disattivazioni
bool g_newState[NUMSTATES] = { false }; // array vuoto per attivazioni
bool g_onEntryState[NUMSTATES] = { false }; // nuove attivazioni
uint8_t g_servoPos = SERVO_DOWN;
uint32_t g_millis; // tempo sistema ad ogni ciclo
uint32_t g_saveMillis[NUMTIMERS] = { 0 }; // variabili per timer
/* Nomi simbolici timer */
#define T0 0
#define T1 1
/* S_XXX stati della macchina a stati */
#define S_START 0
#define S_INOPENING 1
#define S_ISUP 2
#define S_INCLOSING 3
#define S_BLINK 4
#define S_DUMMY1 5
#define S_DUMMY2 6
#define IRCMD_PLAY 168
//-------------------------------------------------------------------------
/* timerElapse() ritorna il tempo trascorso del timer 't' */
uint32_t timerElapse(uint8_t t) {
return g_millis - g_saveMillis[t];
}
//-------------------------------------------------------------------------
/* timerElapsed() ritorna true se per timer 't' scaduto tempo 'p' */
bool timerElapsed(uint8_t t, uint32_t p) {
return (g_millis - g_saveMillis[t]) >= p;
}
//-------------------------------------------------------------------------
/* resetTimer() imposta timer 't' a millis ciclo attuale */
void resetTimer(uint8_t t) {
g_saveMillis[t] = g_millis;
}
//-------------------------------------------------------------------------
/* 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]);
}
//-------------------------------------------------------------------------
/* 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);
Serial.print("Press play or\nyellow button\n");
RCservo.attach(5);
RCservo.write(g_servoPos);
lcd.setCursor(0, 1);
lcd.print((char)223);
lcd.setCursor(6,1);
lcd.print("Tms: ");
lcdPrintPos(g_servoPos);
g_newState[S_START] = true; // stati iniziali attivi
}
//-------------------------------------------------------------------------
void loop() {
g_millis = millis();
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;
}
for (int i=0; i<NUMSTATES; i++)
{
g_onEntryState[i] = g_newState[i];
g_state[i] = g_newState[i] | g_oldState[i];
g_oldState[i] = g_state[i];
g_newState[i] = false;
}
for (int self=0; self<NUMSTATES; self++) // cicla tutti gli stati
{
if (S_START == self && g_state[self])
{
/* ENTRY STATE */
if (g_onEntryState[self]) {
Serial.println(F("Enter to S_START"));
}
/* RUN STATE */
if (command == IRCMD_PLAY) {
/* FORK */
g_oldState[self] = false;
g_newState[S_INOPENING] = true;
g_newState[S_BLINK] = true;
}
/* EXIT STATE */
if (g_state[self] != g_oldState[self]) {
Serial.println(F(" Exit from S_START"));
}
}
if (S_INOPENING == self && g_state[self])
{
/* ENTRY STATE */
if (g_onEntryState[self]) {
Serial.println(F("Enter to S_INOPENING"));
}
/* RUN STATE */
if (timerElapsed(T0, 20))
{
resetTimer(T0);
if (g_servoPos <= SERVO_UP) {
g_servoPos++;
RCservo.write(g_servoPos);
lcdPrintPos(g_servoPos);
}
if (g_servoPos == SERVO_UP) {
/* CHANGE STATE */
g_oldState[self] = false;
g_newState[S_ISUP] = true;
resetTimer(T0);
}
}
/* EXIT STATE */
if (g_state[self] != g_oldState[self]) {
Serial.println(F(" Exit from S_INOPENING"));
}
}
if (S_ISUP == self && g_state[self])
{
/* ENTRY STATE */
if (g_onEntryState[self]) {
Serial.println(F("Enter to S_ISUP"));
}
/* RUN STATE */
if (timerElapsed(T0, 3000)) {
/* CHANGE STATE */
g_oldState[self] = false;
g_newState[S_INCLOSING] = true;
lcdPrintTimeout(0);
} else {
//uint32_t dt = millis() - g_saveMillis[T0];
lcdPrintTimeout(3000 - timerElapse(T0));
}
/* CHECK IR BARRIER */
if (digitalRead(PIN_IR_BARRIER) == LOW) {
/* RESTART TIMER */
resetTimer(T0);
}
/* EXIT STATE */
if (g_state[self] != g_oldState[self]) {
Serial.println(F(" Exit from S_ISUP"));
}
}
if (S_INCLOSING == self && g_state[self])
{
/* ENTRY STATE */
if (g_onEntryState[self]) {
Serial.println(F("Enter to S_INCLOSING"));
}
/* RUN STATE */
if (timerElapsed(T0, 20)) {
resetTimer(T0);
if (g_servoPos >= SERVO_DOWN) {
g_servoPos--;
RCservo.write(g_servoPos);
lcdPrintPos(g_servoPos);
}
if (g_servoPos == SERVO_DOWN) {
/* CHANGE STATE */
g_oldState[self] = false;
g_newState[S_DUMMY2] = true;
}
}
/* CHECK IR BARRIER */
if (digitalRead(PIN_IR_BARRIER) == LOW) {
/* CHANGE STATE */
g_oldState[self] = false;
g_newState[S_INOPENING] = true;
}
/* EXIT STATE */
if (g_state[self] != g_oldState[self]) {
Serial.println(F(" Exit from S_INCLOSING"));
}
}
if (S_BLINK == self && g_state[self])
{
/* ENTRY STATE */
if (g_onEntryState[self]) {
Serial.println(F("Enter to S_BLINK"));
digitalWrite(A0, 1);
resetTimer(T1);
}
/* RUN STATE */
if (timerElapsed(T1, 250)) {
bool yel = digitalRead(A0);
digitalWrite(LED_BUILTIN, yel);
digitalWrite(A0, !yel);
resetTimer(T1);
}
if (g_state[S_DUMMY2]) {
/* CHANGE STATE */
g_oldState[self] = false;
g_newState[S_DUMMY1] = true;
}
/* EXIT STATE */
if (g_state[self] != g_oldState[self])
{
Serial.println(F(" Exit from S_BLINK"));
digitalWrite(LED_BUILTIN, LOW);
digitalWrite(A0, LOW);
}
}
} // end for
/* JOIN */
if (g_state[S_DUMMY1] && g_state[S_DUMMY2])
{
g_oldState[S_DUMMY1] = false;
g_oldState[S_DUMMY2] = false;
g_newState[S_START] = true;
}
} // end loop()
//-------------------------------------------------------------------------