#include <AgileStateMachine.h>
const byte SEC_FTC1 = 26; // Safety photocell
const byte BTN_START = 27; // Gate open request
const byte LED_OPEN = 14;
const byte LED_CLOSE = 12;
const byte LED_FLASHER = 13;
uint32_t openedDelay = 10000; // Time the gate remain opened
uint32_t openTime = 8000; // Time needed to complete movement
uint32_t closeTime = 8000; // Time needed to complete movement
uint32_t waitTime = 3000; // Time to wait before restart after safety FTC
// The Finite State Machine
StateMachine fsm;
// Input/Output State Machine interface
bool inStartButton, inSafetyFTC, inResetGate;
bool outOpen, outClose, outFlashBlink;
State* stClosed;
State* stClosing;
State* stOpened;
State* stOpening;
State* stStopWait;
// External interrupt for safety photocell
void IRAM_ATTR SafetyPhotecellOn() {
// Check photocell only while the motor is moving.
if (outOpen || outClose) {
inSafetyFTC = true;
}
}
/////////// STATE MACHINE FUNCTIONS //////////////////
// This function will be executed before exit the current state
void onLeaving(){
Serial.print("Leaving state: ");
Serial.println(fsm.getActiveStateName());
}
// This function will be executed before enter next state
void onEntering(){
Serial.print("Entered state: ");
Serial.println(fsm.getActiveStateName());
}
// Definition and modeling of the finite state machine
void setupStateMachine(){
/* Create states and assign name and callback functions */
stClosed = fsm.addState("Closed", onEntering, onLeaving);
stClosing = fsm.addState("Closing", onEntering, onLeaving);
stOpened = fsm.addState("Opened", onEntering, onLeaving);
stOpening = fsm.addState("Opening", onEntering, onLeaving);
stStopWait = fsm.addState("Stop & Wait", waitTime, onEntering, onLeaving);
/* Define transitions to target state and trigger condition (callback function or bool var) */
stClosed->addTransition(stOpening, inStartButton);
stOpening->addTransition(stOpened, openTime);
stOpened->addTransition(stClosing, openedDelay);
stClosing->addTransition(stClosed, closeTime);
stClosing->addTransition(stStopWait, inSafetyFTC);
stStopWait->addTransition(stOpening, inResetGate);
/* Add actions for the states where needed */
// Start blink flasher immediatly
stOpening->addAction(Action::Type::S, outFlashBlink);
stClosing->addAction(Action::Type::S, outFlashBlink);
stOpened->addAction(Action::Type::R, outFlashBlink);
stClosed->addAction(Action::Type::R, outFlashBlink);
// Start gate motor after some delay
stOpening->addAction(Action::Type::D, outOpen, 2000);
stClosing->addAction(Action::Type::D, outClose, 2000);
// Safety handling (only on closing)
stStopWait->addAction(Action::Type::R, outOpen);
stStopWait->addAction(Action::Type::R, outClose);
stStopWait->addAction(Action::Type::R, inSafetyFTC);
/* Set initial state and start the Machine State */
fsm.setInitialState(stClosed);
fsm.start();
Serial.print("Active state: ");
Serial.println(fsm.getActiveStateName());
Serial.println();
}
void setup() {
pinMode(SEC_FTC1, INPUT_PULLUP);
pinMode(BTN_START, INPUT_PULLUP);
pinMode(LED_OPEN, OUTPUT);
pinMode(LED_CLOSE, OUTPUT);
pinMode(LED_FLASHER, OUTPUT);
Serial.begin(115200);
Serial.println("Starting State Machine...\n");
setupStateMachine();
// External interrupt for safety photocell
attachInterrupt(digitalPinToInterrupt(SEC_FTC1), SafetyPhotecellOn, FALLING);
}
void loop() {
// Get machine state inputs
inStartButton = (digitalRead(BTN_START) == LOW);
inResetGate = (fsm.getCurrentState() == stStopWait) && inStartButton;
// Run State Machine (true is state changed)
if (fsm.execute()) {
Serial.println();
}
// Set outputs according to state
digitalWrite(LED_OPEN, outOpen);
digitalWrite(LED_CLOSE, outClose);
// Blink flasher
if (outFlashBlink || fsm.getCurrentState() == stStopWait) {
static bool level = LOW;
static uint32_t bTime;
if (millis() - bTime >= 500 ) {
level = !level;
bTime = millis();
digitalWrite(LED_FLASHER, level);
}
}
else if (fsm.getCurrentState() == stOpened) {
digitalWrite(LED_FLASHER, HIGH);
delay(1);
}
else {
digitalWrite(LED_FLASHER, LOW);
delay(1);
}
}
CLOSE
OPEN
FLASH