/*
Vamos aprender a usar a máquina de estados finitos com o Arduino.
As máquinas de estado finito (FSMs) são importantes para entender o
lógica de tomada de decisão, bem como controlar os sistemas digitais.
https://create.arduino.cc/projecthub/tolentinocotesta/let-s-learn-how-to-use-finite-state-machine-with-arduino-c524ac?ref=part&ref_id=8233&offset=13
*/
#include <YA_FSM.h>
// Semáforo de pedestres -> luz verde LIGADO até que o botão seja pressionado
#define YELLOW_TIME 2000
#define RED_TIME 10000
#define CALL_DELAY 5000
const byte BTN_CALL = 2;
const byte GREEN_LED = 12;
const byte YELLOW_LED = 11;
const byte RED_LED = 10;
// Auxiliar para imprimir informações em vez de inteiro quando o estado mudar
const char * const stateName[] PROGMEM = { "RED", "GREEN", "YELLOW", "CALL"};
// Alias do Estado
enum State {RED, GREEN, YELLOW, CALL};
// Entrada (trig transição do estado GREEN para CALL, as outras transições no tempo limite)
bool callButton = false;
// Variáveis de saída
bool redLed = false;
bool greenLed = false;
bool yellowLed = false;
// Cria um novo FSM
YA_FSM stateMachine;
// Define a função de retorno de chamada "on enter" (o mesmo para todos os estados "light")
void onEnter() {
Serial.print(stateMachine.ActiveStateName());
Serial.println(F(" light ON"));
}
// Define a função de retorno de chamada "on exit" (o mesmo para todos os estados "light")
void onExit() {
Serial.print(stateMachine.ActiveStateName());
Serial.println(F(" light OFF\n"));
}
// Define a função "on enter" para o estado do botão CALL
void onEnterCall() {
Serial.println(F("Chamada registrada, aguarde um pouco."));
}
// Configura a máquina de estado
void setupStateMachine() {
// Siga a ordem de enumeração para a definição do estado (será usado como índice)
// Adiciona os estados
stateMachine.AddState(stateName[RED], RED_TIME, onEnter, nullptr, onExit);
stateMachine.AddState(stateName[GREEN], 0, onEnter, nullptr, onExit);
stateMachine.AddState(stateName[YELLOW], YELLOW_TIME, onEnter, nullptr, onExit);
stateMachine.AddState(stateName[CALL], CALL_DELAY, onEnterCall, nullptr, nullptr);
stateMachine.AddAction(RED, YA_FSM::N, redLed); // N -> Enquanto o estado estiver ativo, o led vermelho está LIGADO
stateMachine.AddAction(GREEN, YA_FSM::S, greenLed); // S -> SETA o led verde aceso
stateMachine.AddAction(YELLOW, YA_FSM::R, greenLed); // R -> RESETA o led verde
stateMachine.AddAction(YELLOW, YA_FSM::N, yellowLed); // N -> Enquanto o estado estiver ativo, o led amarelo está LIGADO
// Adicione as transições com funções de callback de entrada dos gatilhos relacionados
stateMachine.AddTransition(RED, GREEN, []() {
// Neste exemplo, é apenas uma função lambda simples que retorna o valor do tempo limite do estado
return stateMachine.CurrentState()->timeout;
});
stateMachine.AddTransition(YELLOW, RED, []() {
return stateMachine.CurrentState()->timeout;
});
stateMachine.AddTransition(CALL, YELLOW, []() {
return stateMachine.CurrentState()->timeout;
});
stateMachine.AddTransition(GREEN, CALL, callButton);
}
void setup() {
// Configuração de entrada/saída
pinMode(BTN_CALL, INPUT);
pinMode(GREEN_LED, OUTPUT);
pinMode(YELLOW_LED, OUTPUT);
pinMode(RED_LED, OUTPUT);
Serial.begin(115200);
while (!Serial) {} // Necessário apenas para porta USB nativa
Serial.println(F("Iniciando a máquina de estados finitos...\n"));
setupStateMachine();
}
void loop() {
// Leitura do botão
callButton = (digitalRead(BTN_CALL) == LOW);
// Atualiza a máquina de estado
if (stateMachine.Update()) {
Serial.print(F("Estado ativo: "));
Serial.println(stateMachine.ActiveStateName());
}
// Define as saídas
digitalWrite(RED_LED, redLed);
digitalWrite(GREEN_LED, greenLed);
digitalWrite(YELLOW_LED, yellowLed);
}