/*
parking access is demo application born to show a state
machine implementation.
Copyright (C) 2022 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/>.
*/
/*
Parking access application
Pulsante verde per simulare la barriera IR
*/
#include <IRremote.h>
#include <LiquidCrystal.h>
#include <Servo.h>
#include <JC_Button.h>
#include "motore.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;
#define PIN_MOTORE_APERTURA A0
#define PIN_MOTORE_CHIUSURA A1
#define PIN_ALIM_FOTO A2
#define PIN_LAMPEGGIANTE A3
// g_state è la variabile di stato
uint8_t g_state = 0;
uint32_t g_saveMillis = 0;
uint8_t g_servoPos = SERVO_DOWN;
/* S_XXX stati della macchina a stati g_state */
#define S_START 0
#define S_CHECK_PLAY 4
#define S_INOPENING 1
#define S_ISOPEN 2
#define S_INCLOSING 3
#define IRCMD_PLAY 168
#define IR_BARRIER_DEBOUNCE 50
Button irBarrierIntern(PIN_IR_BARRIER, IR_BARRIER_DEBOUNCE);
enum MotorState { FERMO, APRE, CHIUDE };
MotorState statoMotore = FERMO;
//CmdMotore cmdAntaSX(PIN_MOTORE_APERTURA, PIN_MOTORE_CHIUSURA);
// 509443625 ns (509ms) Motore gira in apertura
// si ferma per intervento fotocellula
// 2333348750 ns (2333ms) tempo di occlusione
// Riparte motore in apertura e resta per
// 1501005938 ns (1501ms)
STimer timer0(1);
//STimer genericTimer(100);
//STimer timer1(1000);
MotorState readMotorState() {
return statoMotore;
}
void comandoMotore(MotorState cmd) {
static byte saveCmd = 255;
if (saveCmd == cmd) {
return;
}
switch(cmd) {
case FERMO:
digitalWrite(PIN_MOTORE_APERTURA, LOW);
digitalWrite(PIN_MOTORE_CHIUSURA, LOW);
Serial.println("FERMO ESEGUITO");
statoMotore = saveCmd = cmd;
break;
case APRE:
//digitalWrite(PIN_MOTORE_CHIUSURA, LOW);
//delay(100);
digitalWrite(PIN_MOTORE_APERTURA, HIGH);
Serial.println("APRE ESEGUITO");
statoMotore = saveCmd = cmd;
break;
case CHIUDE:
//digitalWrite(PIN_MOTORE_APERTURA, LOW);
//delay(100);
digitalWrite(PIN_MOTORE_CHIUSURA, HIGH);
Serial.println("CHIUDE ESEGUITO");
statoMotore = saveCmd = cmd;
break;
}
}
uint32_t t = 0;
uint32_t secondsCounter;
uint32_t oldSecondsCounter = 2;
void setup()
{
Serial.begin(115200);
t -= 1;
Serial.println(t);
uint32_t a = micros();
delayMicroseconds(478);
uint32_t b = micros();
uint32_t c = millis();
Serial.println(a);
Serial.println(b);
Serial.println(c);
uint64_t mus = millis() * 1000;
mus = micros() - mus;
Serial.println((uint32_t)mus);
//pinMode(PIN_IR_BARRIER, INPUT_PULLUP);
irBarrierIntern.begin();
pinMode(PIN_MOTORE_APERTURA, OUTPUT);
pinMode(PIN_MOTORE_CHIUSURA, OUTPUT);
pinMode(PIN_ALIM_FOTO, OUTPUT);
pinMode(PIN_LAMPEGGIANTE, OUTPUT);
pinMode(LED_BUILTIN, OUTPUT);
//cmdAntaSX.begin();
RCservo.attach(5);
Serial.println(millis());
receiver.enableIRIn(); // Start the receiver
lcd.begin(16, 2);
Serial.println(millis());
lcd.print("Press play");
//delay(300);
//Serial.println(millis()); // 319 ms
timer0.setValA(250);
// Da STOP ad APRI immediato
//cmdAntaSX.cmd(CmdMotore::Cmd::APRI);
// passaggio immediato
//cmdAntaSX.cmd(Cmd::STOP);
//cmdAntaSX.cmd(CmdMotore::Cmd::CHIUDI);
//cmdAntaSX.cmd(CmdMotore::Cmd::APRI);
/*
Passaggi immediati:
STOP -> APRI
STOP -> CHIDI
APRI -> STOP
CHIUDI -> STOP
Non è così:
(APERTURA | CHIUSURA) -> STOP {
STOP IMMEDIATO.
salvaMillis = millis()
}
STOP -> (APRI | CHIUDI) {
if (millis() - salvaMillis > 1000 ) {
ACT((APRI | CHIUDI))
CMD_PENDING = NULL
} else {
CMD_PENDING = (APRI | CHIUDI)
}
}
*/
//RCservo.write(g_servoPos);
}
void powerFoto(bool tf) {
digitalWrite(PIN_ALIM_FOTO, tf);
}
uint32_t timerAzione;
uint32_t deltaTimerAzione;
uint32_t motorTimer;
uint32_t motorPause;
uint32_t debugTimer;
byte saveCommand;
byte istate;
uint32_t timerRef;
uint16_t blink = 250;
uint8_t testTimer = 2;
void loop() {
/*if (!timerRef) {
timerRef = 1;
Serial.println(millis());
}*/
switch (testTimer) {
case 0:
if (timer0.isExpired(250, false)) {
bool lamp = digitalRead(LED_BUILTIN);
digitalWrite(LED_BUILTIN, !lamp);
timer0.reload();
}
break;
case 1:
if (timer0.isExpired(timer0.getValA())) {
bool lamp = digitalRead(LED_BUILTIN);
digitalWrite(LED_BUILTIN, !lamp);
if (!timer0.onEnter()) {
uint16_t a = timer0.getValA();
if (a == 300) {
Serial.println(a);
timer0.pause();
break;
}
if (timer0.isRun()) {
if (a < 500) {
a = a + 10;
timer0.setValA(a);
}
timer0.reload(a);
}
}
}
case 2:
if (millis() - timerRef >= 1000) {
timerRef += 1000;
secondsCounter++;
}
if (oldSecondsCounter != secondsCounter) {
uint32_t tm = millis() - (secondsCounter * 1000);
tm = (secondsCounter * 1000 + tm);
delay(5);
Serial.println(tm);
oldSecondsCounter = secondsCounter;
}
}
timer0.update();
//cmdAntaSX.update();
irBarrierIntern.read();
uint8_t command = 0;
if (receiver.decode()) {
command = receiver.decodedIRData.command;
receiver.resume(); // Receive the next value
}
switch (g_state) {
case S_START:
/*if (!timerRef) {
timerRef = 1;
Serial.println(millis());
}*/
// istate = 5 impostato nel case S_INCLOSING
// g_state = S_START;
// comandoMotore(FERMO);
// istate = 5;
//if (istate == 5) {
if (digitalRead(PIN_LAMPEGGIANTE)) {
powerFoto(LOW);
// attende 1 secondo senza esguire il resto del codice.
// questo vuole dire che se premo Play subito
// dopo che l'anta è chiusa questa non si riapre.
// Per aprire devo premere Play dopo lo spegnimento del
// lampeggiante.
if (!(millis() - g_saveMillis > 1000)) {
//if (!genericTimer.isExpired(1000, true, false)) {
break;
} else {
//genericTimer.reset();
// è trascorso 1 secondo
digitalWrite(PIN_LAMPEGGIANTE, LOW);
}
}
if (command == IRCMD_PLAY) {
powerFoto(HIGH);
digitalWrite(PIN_LAMPEGGIANTE, HIGH);
//Serial.println("check");
g_saveMillis = millis();
// !!! CAMBIA STATO !!!
g_state = S_CHECK_PLAY;
istate = 0;
}
break;
case S_CHECK_PLAY:
// permette di avere le fotocellule alimentate per
// almeno 250ms, prima di poterne leggere lo stato
if (istate == 0) {
if (millis() - g_saveMillis > 250) {
g_saveMillis = millis();
istate = 1;
}
}
if (istate == 1) {
// resta in attesa 1000 ms consultanto le fotocellule
// con lampeggiante acceso
if (irBarrierIntern.pressedFor(200)) {
g_saveMillis = millis();
powerFoto(LOW);
digitalWrite(PIN_LAMPEGGIANTE, LOW);
g_state = S_START;
break;
}
if (millis() - g_saveMillis > 1000) {
// scaduti i 1000 ms cambia stato
g_saveMillis = millis();
// !!! CAMBIA STATO
g_state = S_INOPENING;
}
}
break;
case S_INOPENING:
if (irBarrierIntern.isPressed()) {
if (readMotorState() == APRE) {
// *** Avvia il timer ***
motorTimer = millis();
motorPause = 1000;
}
comandoMotore(FERMO);
// !!! BREAK !!!
break;
}
// Se il motore è fermo e motorPause è trascorso
// avvia il motore in apertura.
if ( (readMotorState() == FERMO) &&
( (millis() - motorTimer) > motorPause) ) {
comandoMotore(APRE);
// !!! resetta il timer
motorTimer = 0;
motorPause = 0;
// !!! save millis()
g_saveMillis = millis();
}
// Se il motore è fermo esce dallo switch
if (readMotorState() == FERMO)
break;
// Eseguito se il motore è NON FERMO
// accumulatore di tempo lavoro
if (millis() - g_saveMillis >= 100) {
g_saveMillis = millis();
// accumula 100 ms
timerAzione += 100;
}
if (timerAzione >= 2000) {
// azzera accumulatore
timerAzione = 0;
// !!! CAMBIA STATO !!!
g_state = S_ISOPEN;
comandoMotore(FERMO);
g_saveMillis = millis();
}
break;
case S_ISOPEN:
if (millis() - g_saveMillis > 2000) {
/* CHANGE STATE */
g_saveMillis = millis();
g_state = S_INCLOSING;
}
/* CHECK IR BARRIER */
if (irBarrierIntern.isPressed()) {
/* RESTART TIMER */
g_saveMillis = millis();
comandoMotore(FERMO);
}
break;
case S_INCLOSING:
//comandoMotore(CHIUDE);
if (irBarrierIntern.isPressed()) {
if (readMotorState() == CHIUDE) {
// *** Avvia il timer ***
motorTimer = millis();
motorPause = 1000;
}
comandoMotore(FERMO);
// !!! BREAK !!!
break;
}
// Se il motore è fermo e motorPause è trascorso
// avvia il motore in chiusura.
if ( (readMotorState() == FERMO) &&
( (millis() - motorTimer) > motorPause) ) {
comandoMotore(CHIUDE);
// !!! resetta il timer
motorTimer = 0;
motorPause = 0;
// !!! save millis()
g_saveMillis = millis();
}
// Se il motore è fermo esce dallo switch
if (readMotorState() == FERMO)
break;
// Eseguito se il motore è NON FERMO
// accumulatore di tempo lavoro
if (millis() - g_saveMillis >= 100) {
g_saveMillis = millis();
// accumula 100 ms
timerAzione += 100;
}
if (timerAzione >= 2000) {
timerAzione = 0;
// !!! CAMBIA STATO !!!
g_state = S_START;
istate = 5;
comandoMotore(FERMO);
g_saveMillis = millis();
}
break;
} // end switch
} // end loop()
nano:12
nano:11
nano:10
nano:9
nano:8
nano:7
nano:6
nano:5
nano:4
nano:3
nano:2
nano:GND.2
nano:RESET.2
nano:0
nano:1
nano:13
nano:3.3V
nano:AREF
nano:A0
nano:A1
nano:A2
nano:A3
nano:A4
nano:A5
nano:A6
nano:A7
nano:5V
nano:RESET
nano:GND.1
nano:VIN
nano:12.2
nano:5V.2
nano:13.2
nano:11.2
nano:RESET.3
nano:GND.3
lcd:VSS
lcd:VDD
lcd:V0
lcd:RS
lcd:RW
lcd:E
lcd:D0
lcd:D1
lcd:D2
lcd:D3
lcd:D4
lcd:D5
lcd:D6
lcd:D7
lcd:A
lcd:K
r1:1
r1:2
ir:GND
ir:VCC
ir:DAT
servo1:GND
servo1:V+
servo1:PWM
btn1:1.l
btn1:2.l
btn1:1.r
btn1:2.r
r2:1
r2:2
r3:1
r3:2
led1:A
led1:C
led2:A
led2:C
led3:A
led3:C
Power IR
r4:1
r4:2
sw1:1
sw1:2
sw1:3
led4:A
led4:C
Lampeggiante
r5:1
r5:2
logic1:D0
logic1:D1
logic1:D2
logic1:D3
logic1:D4
logic1:D5
logic1:D6
logic1:D7
logic1:GND