// **** ERM_UD FSM ****
const long DBTIME = 25; // number of ms allowed to eliminate key bounce
const long FLASH_ON_TIME = 250; // duration of on times for flashing LEDs
const long FLASH_OFF_TIME = 250; // duration of off times for flashing LEDs
const long FLASH_TIME = FLASH_ON_TIME + FLASH_OFF_TIME; // total cycle time for flashing LEDs
const long CHASE_ON_TIME = 100; // duration of on times for chasing LEDs
const long CHASE_TIME = CHASE_ON_TIME*5; // total cycle time for chasing LEDs
// FSM Define States for ERM_UD Panel
int iState = -1; // The main state variable (initialised to an invalid state)
long state_Time = 0; // when the state last changed.
bool bChanged = false; // Has anything changed?
#define S_ERM_UD_S_INIT 0 // Initial State
#define S_ERM_UD_S_RSIL 1 // Ready next Service State
#define S_ERM_UD_S_SDIE 2 // Slow up Dispatch Ready State
#define S_ERM_UD_S_SAIL 3 // Slow Down Accepted State
#define S_ERM_UD_S_FDIC 4 // Fast up Dispatch Ready State
#define S_ERM_UD_S_FRSE 5 // Fast Route Set State
#define S_ERM_UD_S_FAIL 6 // Fast up Accepted State
// Declare Input Controls for FSM ERM_UD Panel
const int ERM_UD_I_TRD = 6; // Train Ready Dispatch Button @ Pin 6
bool lastButton_ERM_UD_I_TRD = false;
bool currentButton_ERM_UD_I_TRD = false;
long ERM_UD_I_TRD_Time = 0;
// Declare Input Signals From Other Panels
const int ERM_UD_I_UTA = 7; // received input from LFY_UA_O_ERM_SUTA @ pin 7
bool lastSignalIn_ERM_UD_I_UTA = false;
bool currentSignalIn_ERM_UD_I_UTA = false;
long ERM_UD_I_UTA_Time = 0;
const int ERM_UD_I_ANS = 8; // received input from LFY_UA_O_ERM_RNS @ pin 8
bool lastSignalIn_ERM_UD_I_ANS = false;
bool currentSignalIn_ERM_UD_I_ANS = false;
long ERM_UD_I_ANS_Time = 0;
// Declare Output Indications for FSM ERM_UD Panel
const int ERM_UD_O_TRD = 9; // Train Ready Dispatch LED @ Pin 9
bool ledOn_ERM_UD_O_TRD = false;
long ERM_UD_O_TRD_Time = 0;
const int ERM_UD_O_TA = 10; // slow Train Accepted LED @ Pin 10
bool ledOn_ERM_UD_O_TA = false;
long ERM_UD_O_TA_Time = 0;
// this one will be a chase, so it uses outputs 11 to 15
const int ERM_UD_O_ANS = 11; // Awaiting Next Service LED @ Pin 11 (to 15)
bool ledOn_ERM_UD_O_ANS = false;
long ERM_UD_O_ANS_Time = 0;
// Declare Output Signals for FSM ERM_UD Panel
const int ERM_UD_O_LFY_STRD = 16; // Output sent to LFY_UA_I_STRD Slow train ready dispatch LED @ Pin 12
bool signalOut_ERM_UD_O_LFY_STRD = false;
long ERM_UD_O_LFY_STRD_Time = 0;
bool debounce(bool state, bool val, long time, long dbTime, bool& lastVal, long& lastTime) {
// We will only register a change of state if the input value has not
// changed for a certain time
bool newState;
newState = state; // by default the new state is the old state
if (lastVal != val) { // if the input value has changed
lastTime = time; // set the last time it changed to "now"
lastVal = val; // and retain the current input value
} else { // otherwise (input value has NOT changed)
if ((time - lastTime) >= dbTime) { // has it remained stable long enough
newState = val; // change the state
}
}
return newState; // This is the new state
}
bool simpleDebounce(int inPin, bool& state, bool& lastVal, long& lastTime) {
// A debounce routine with a simpler interface.
long time = millis(); // time in milliseconds
bool val = digitalRead(inPin); // current pin value
state = debounce(state, val, time, DBTIME, lastVal, lastTime); // debounce the pin
return state; // return state
}
bool pressEdge(bool& state, bool lastVal) // detects the leading edge of a button press.
{
if ((!state) && lastVal) { // button pressed, but state not yet set
state = true; // set the state without waiting for debouncing
return true; // return a valid edge
}
else {
return false; // it's not a press edge.
}
}
// This is used to set integer states. The most notable one is the state variable!
void setStateI(int &iState, long &lTime, int iValue) {
if (iState != iValue) {
iState = iValue;
lTime = millis();
bChanged = true;
}
}
// This is used to set boolean states. This is used for most outputs
void setStateB(bool &bState, long &lTime, bool bValue) {
if (bState != bValue) {
bState = bValue;
lTime = millis();
bChanged = true;
}
}
// This will flash a LED on an output pin while the output is on
void flashLED(int iPin, bool bValue, long lTime) {
// following is a slightly complex C assignment.
// it always returns FALSE if bValue is false
// if bValue is TRUE it returns TRUE for the first FLASH_ON_TIME ms after
// the value is set (determined by lTime)
// then it returns false for FLASH_OFF_TIME ms
// then it repets...
bool bOn = bValue && ((((millis() - lTime) % FLASH_TIME) - FLASH_ON_TIME) <= 0);
digitalWrite(iPin, bOn);
}
// This will chase 5 LEDs on 5 consecutive output pins while the output is on
void chaseLED(int iPin, bool bValue, long lTime) {
// by default led -1 is to be on (that's no LEDs at all)
int iOn = -1;
if (bValue) {
// if the chasing is on, determine which LED should be on now
iOn = ((millis() - lTime) % CHASE_TIME) / CHASE_ON_TIME;
}
// go through all the LEDs, turning on only the one we want (or turning them all off)
for (int i = 0; i < 5; i++) {
digitalWrite(iPin+i, iOn == i);
}
}
// read the current inputs and process them
void readInputs() {
// read the current button states
simpleDebounce(ERM_UD_I_TRD, currentButton_ERM_UD_I_TRD, lastButton_ERM_UD_I_TRD, ERM_UD_I_TRD_Time);
// and the input signal states
simpleDebounce(ERM_UD_I_UTA, currentSignalIn_ERM_UD_I_UTA, lastSignalIn_ERM_UD_I_UTA, ERM_UD_I_UTA_Time);
simpleDebounce(ERM_UD_I_ANS, currentSignalIn_ERM_UD_I_ANS, lastSignalIn_ERM_UD_I_ANS, ERM_UD_I_ANS_Time);
}
// Find out if we have a transition
void processTransitions () // Transistion From ERM_UD_State
{
switch (iState)
{
case S_ERM_UD_S_INIT: // Initial State
if (currentSignalIn_ERM_UD_I_ANS)
{
setStateI(iState, state_Time, S_ERM_UD_S_RSIL);
}
break;
case S_ERM_UD_S_RSIL: // Ready Next Service
if (pressEdge(currentButton_ERM_UD_I_TRD, lastButton_ERM_UD_I_TRD))
{
setStateI(iState, state_Time, S_ERM_UD_S_SDIE);
}
break;
case S_ERM_UD_S_SDIE: // Slow Train Ready Dispatch
if (currentSignalIn_ERM_UD_I_UTA)
{
setStateI(iState, state_Time, S_ERM_UD_S_SAIL);
}
break;
case S_ERM_UD_S_SAIL: // Slow Train Accepted
if (currentSignalIn_ERM_UD_I_ANS)
{
setStateI(iState, state_Time, S_ERM_UD_S_RSIL);
}
break;
default:
Serial.println("Illegal State");
setStateI(iState, state_Time, S_ERM_UD_S_INIT);
break;
}
}
void setState() // Set the Output States
{
switch(iState)
{
case S_ERM_UD_S_INIT:
{
setStateB(ledOn_ERM_UD_O_TRD, ERM_UD_O_TRD_Time, false);
setStateB(ledOn_ERM_UD_O_TA, ERM_UD_O_TA_Time, false);
setStateB(ledOn_ERM_UD_O_ANS, ERM_UD_O_ANS_Time, false);
setStateB(signalOut_ERM_UD_O_LFY_STRD, ERM_UD_O_LFY_STRD_Time, false);
Serial.println("All LEDs Are Off");
}
break;
case S_ERM_UD_S_RSIL:
{
setStateB(ledOn_ERM_UD_O_TRD, ERM_UD_O_TRD_Time, false);
setStateB(ledOn_ERM_UD_O_TA, ERM_UD_O_TA_Time, false);
setStateB(ledOn_ERM_UD_O_ANS, ERM_UD_O_ANS_Time, true);
setStateB(signalOut_ERM_UD_O_LFY_STRD, ERM_UD_O_LFY_STRD_Time, false);
Serial.println("Awaiting Next Service LED Now ON");
}
break;
case S_ERM_UD_S_SDIE:
{
setStateB(ledOn_ERM_UD_O_TRD, ERM_UD_O_TRD_Time, true);
setStateB(ledOn_ERM_UD_O_TA, ERM_UD_O_TA_Time, false);
setStateB(ledOn_ERM_UD_O_ANS, ERM_UD_O_ANS_Time, false);
setStateB(signalOut_ERM_UD_O_LFY_STRD, ERM_UD_O_LFY_STRD_Time, true);
Serial.println("Slow Train Ready Dispatch LED Now ON");
Serial.println("Awaiting Next Service LED Is Now OFF");
}
break;
case S_ERM_UD_S_SAIL:
{
setStateB(ledOn_ERM_UD_O_TRD, ERM_UD_O_TRD_Time, false);
setStateB(ledOn_ERM_UD_O_TA, ERM_UD_O_TA_Time, true);
setStateB(ledOn_ERM_UD_O_ANS, ERM_UD_O_ANS_Time, false);
setStateB(signalOut_ERM_UD_O_LFY_STRD, ERM_UD_O_LFY_STRD_Time, false);
Serial.println("Slow Train Accepted LED Now ON");
Serial.println("Slow Train Ready Dispatch LED Is Now OFF");
}
break;
default:
{
Serial.println("Illegal Output State");
}
break;
}
}
void writeOutputs() // writes the current states to the LEDs
// These will be set On until the program has been tested
// Then set to the correct output (i.e fade, pulse, flash)
{
digitalWrite(ERM_UD_O_TRD, ledOn_ERM_UD_O_TRD);
flashLED(ERM_UD_O_TA, ledOn_ERM_UD_O_TA, ERM_UD_O_TA_Time);
chaseLED(ERM_UD_O_ANS, ledOn_ERM_UD_O_ANS, ERM_UD_O_ANS);
digitalWrite(ERM_UD_O_LFY_STRD, signalOut_ERM_UD_O_LFY_STRD);
}
void setup()
{
// initialize pins
// Input controls
pinMode(ERM_UD_I_TRD, INPUT);
// Input signals
pinMode(ERM_UD_I_UTA, INPUT);
pinMode(ERM_UD_I_ANS, INPUT);
// Output Indications
pinMode(ERM_UD_O_TRD, OUTPUT);
pinMode(ERM_UD_O_TA, OUTPUT);
pinMode(ERM_UD_O_ANS, OUTPUT); // chase outputs
pinMode(ERM_UD_O_ANS+1, OUTPUT);
pinMode(ERM_UD_O_ANS+2, OUTPUT);
pinMode(ERM_UD_O_ANS+3, OUTPUT);
pinMode(ERM_UD_O_ANS+4, OUTPUT);
// Output Signals
pinMode(ERM_UD_O_LFY_STRD, OUTPUT);
// initialize serial: This will only be used to verify program and then be removed
Serial.begin(9600);
int TfrmERM_UD_SetState (S_ERM_UD_S_INIT);
}
void loop()
{
readInputs();
processTransitions();
setState();
writeOutputs();
}