// https://wokwi.com/projects/366388639343894529
// https://forum.arduino.cc/t/approach-island-approach-rr-crossing/1121055
// inputs
#define NORTH_APPROACH 5
#define ISLAND_CIRCUIT 6
#define SOUTH_APPROACH 7
// outputs
#define LIGHT_RIGHT 10
#define LIGHT_LEFT 9
#define BELL_RELAY 11
unsigned long now; // current time for all
enum {
ST_OFF = 0,
ST_APPROACH_N,
ST_APPROACH_S,
ST_ISLAND,
ST_CLEARING_N,
ST_CLEARING_S,
};
byte crossingState, current, last;
enum {OFF, ON};
byte flashQQ; // are we flashing or no?
# define ACTIVE true // my switches pulled high. OMG tangle of sense and == LOW
bool north, island, south;
void setup() {
Serial.begin(115200);
Serial.println("approach islan approach world!\n");
pinMode(NORTH_APPROACH, INPUT_PULLUP); //Track circuit one. 60ft/39.5m long. Primary circuit the train activates 90% of the time.
pinMode(ISLAND_CIRCUIT, INPUT_PULLUP); //Island circuit. 30ft/9m long. Circuit travels through a #5 Right hand Turnout that diverges.
pinMode(SOUTH_APPROACH, INPUT_PULLUP); //Track circuit two. Same length as number one, but needs to be able to activate the crossing from the other direction.
pinMode(BELL_RELAY, OUTPUT); //RR Bell Coil/SoundByte Speaker.
pinMode(LIGHT_LEFT, OUTPUT); //Left Flashing Light.
pinMode(LIGHT_RIGHT, OUTPUT); //Right Flashing Light.
report(); // first report
}
void loop() {
// just to the output function
flashQQ = digitalRead(ISLAND_CIRCUIT) ? ON : OFF;
outputFSM();
static unsigned long lastTime;
now = millis();
if (now - lastTime < 100) return; // loop throttle
lastTime = now;
north = digitalRead(NORTH_APPROACH) == ACTIVE;
island = digitalRead(ISLAND_CIRCUIT) == ACTIVE;
south = digitalRead(SOUTH_APPROACH) == ACTIVE;
report();
int last = 0; //Previous state case. 0=ST_OFF, 1=ST_APPROACH_N, 2=ST_APPROACH_S, 3=ST_ISLAND, 4=ST_CLEARING_N, 5=ST_CLEARING_S.
int current = 0; //Current state case. 0=ST_OFF, 1=ST_APPROACH_N, 2=ST_APPROACH_S, 3=ST_ISLAND, 4=ST_CLEARING_N, 5=ST_CLEARING_S.
switch (crossingState) {
case ST_OFF:
crossingoff();
break;
case ST_APPROACH_N:
appnorth();
break;
case ST_APPROACH_S:
appsouth();
break;
case ST_ISLAND:
crossingblocked();
break;
case ST_CLEARING_N:
clearingnorth();
break;
case ST_CLEARING_S:
clearingsouth();
break;
}
}
char *stateTags[] = {
"OFF ",
"N_APPROACH_N",
"S_APPROACH_S",
"ISLAND ",
"N_CLEARING_N",
"S_CLEARING_S",
};
void report()
{
byte needTo = 0;
static byte printedInputs;
static byte printedState;
byte inputs = 4 * north + 2 * island + south;
if (crossingState != printedState) {
needTo = 1;
printedState = crossingState;
}
if (inputs != printedInputs) {
needTo = 1;
printedInputs = inputs;
}
if (needTo) {
Serial.print(stateTags[crossingState]); Serial.print(" ");
Serial.print(!north ? "NORTH" : " "); Serial.print(" ");
Serial.print(!island ? "ISLAND" : " "); Serial.print(" ");
Serial.print(!south ? "SOUTH" : " "); Serial.print(" ");
Serial.println();
}
}
// change for wiring differences
# define LED_OFF LOW
# define LED_ON HIGH
# define RELAY_ON HIGH
# define RELAY_OFF LOW
void outputFSM()
{
static int phase;
static unsigned long lastTime;
if (now - lastTime < 500) return;
lastTime = now;
if (flashQQ == OFF) {
digitalWrite(LIGHT_LEFT, LED_OFF);
digitalWrite(LIGHT_RIGHT, LED_OFF);
digitalWrite(BELL_RELAY, RELAY_OFF);
phase = 0;
return;
}
if (!phase) {
digitalWrite(LIGHT_LEFT, LED_ON);
digitalWrite(LIGHT_RIGHT,LED_OFF);
digitalWrite(BELL_RELAY, RELAY_ON);
}
else {
digitalWrite(LIGHT_LEFT, LED_OFF);
digitalWrite(LIGHT_RIGHT, LED_ON);
digitalWrite(BELL_RELAY, RELAY_OFF);
}
phase = !phase;
}
/*
void crossingoff() {}
void appnorth() {}
void appsouth() {}
void crossingblocked() {}
void clearingnorth() {}
void clearingsouth() {}
*/
void crossingoff() {
flashQQ = OFF; // turns off all lights and bells until a meet break condition is satisfied.
if (north == LOW && island == HIGH && south == HIGH && current == 0) { //If the north circuit activates first whule the remaining two are empty. Start the light and bell sequence.
current = 1;
last = current;
crossingState = ST_APPROACH_N;
} else if (south == LOW && island == HIGH && south == HIGH && current == 0) { //If the south circuit activates first whuke the remaining two are empty. Start the light and bell sequence.
current = 2;
last = current;
crossingState = ST_APPROACH_S;
}
}
void appnorth() {
flashQQ = ON; // turns on all lights and bells until a meet break condition is satisfied.
if (north == LOW && island == LOW && south == HIGH && last == 1) { //Keep the light and bell sequence going if one of the approach circuits and the island circuit are activated.
current = 3;
last = current;
crossingState = ST_ISLAND;
} else if (north == LOW && island == LOW && south == LOW && last == 1) { //Keep the light and bell sequence going if all of the circuits are activated.
current = 3;
last = current;
crossingState = ST_ISLAND;
} else if (north == HIGH && island == LOW && south == HIGH && last == 1) { //Keep the light and bell sequence going if the island is activated even if the approaches aren't.
current = 3;
last = current;
crossingState = ST_ISLAND;
} else if (north == LOW && island == HIGH && south == LOW && last == 1) { //Keep the light and bell sequence going if both approach circuits are activated even if the island isn't.
current = 3;
last = current;
crossingState = ST_ISLAND;
} else if (north == LOW && island == HIGH && south == LOW && last == 1) { //Stop the light and bell sequence if the train leaves the approach circuit.
current = 0;
last = current;
crossingState = ST_OFF;
}
}
void appsouth() {
flashQQ = ON; // turns on all lights and bells until a meet break condition is satisfied.
if (north == LOW && island == LOW && south == HIGH && last == 2) { //Keep the light and bell sequence going if one of the approach circuits and the island circuit are activated.
current = 3;
last = current;
crossingState = ST_ISLAND;
} else if (north == LOW && island == LOW && south == LOW && last == 2) { //Keep the light and bell sequence going if all of the circuits are activated.
current = 3;
last = current;
crossingState = ST_ISLAND;
} else if (north == HIGH && island == LOW && south == HIGH && last == 2) { //Keep the light and bell sequence going if the island is activated even if the approaches aren't.
current = 3;
last = current;
crossingState = ST_ISLAND;
} else if (north == LOW && island == HIGH && south == LOW && last == 2) { //Keep the light and bell sequence going if both approach circuits are activated even if the island isn't.
current = 3;
last = current;
crossingState = ST_ISLAND;
} else if (north == LOW && island == HIGH && south == LOW && last ==2) { //Stop the light and bell sequence if the train leaves the approach circuit.
current = 0;
last = current;
crossingState = ST_OFF;
}
}
void crossingblocked() {
flashQQ = ON; // turns on all lights and bells until a meet break condition is satisfied.
if (north == LOW && south == HIGH && island == HIGH && last == 3) { //If the train has reached the island and either crosses over it completely or backs off, stop the bell and light sequence even if the train is on an approach circuit.
current = 4;
last = current;
crossingState = ST_CLEARING_N;
} else if (south == LOW && island == HIGH && north == HIGH && last == 3) { //If the train has reached the island and either crosses over it completely or backs off, stop the bell and light sequence even if the train is on an approach circuit.
current = 5;
last = current;
crossingState = ST_CLEARING_S;
}
}
void clearingnorth() {
flashQQ = OFF; // turns off all lights and bells until a meet break condition is satisfied.
if (north == HIGH && island == HIGH && south == HIGH && last == 4) { //If all of the circuits are empty, return to the default state.
current = 0;
last = current;
crossingState = ST_OFF;
} else if (north == LOW && island == HIGH && south == LOW && last == 4) { //Reactivate the light and bell sequence if both approach circuits are activated even if the island isn't.
current = 3;
last = current;
crossingState = ST_ISLAND;
} else if (north == LOW && island == LOW && south == LOW && last == 4) { //Reactivate the light and bell sequence if all of the circuits are activated.
current = 3;
last = current;
crossingState = ST_ISLAND;
} else if (north == LOW && island == LOW && south == HIGH && last == 4) { //Reactivate the light and bell sequence if one of the approach circuits and the island circuit are activated.
current = 3;
last = current;
crossingState = ST_ISLAND;
} else if (north == HIGH && island == LOW && south == LOW && last == 4) { //Reactivate the light and bell sequence if one of the approach circuits and the island circuit are activated.
current = 3;
last = current;
crossingState = ST_ISLAND;
} else if (north == HIGH && island == LOW && south == HIGH && last == 4) { //Reactivate the light and bell sequence if the island is activated even if the approaches aren't.
current = 3;
last = current;
crossingState = ST_ISLAND;
}
}
void clearingsouth() {
flashQQ = OFF; // turns off all lights and bells until a meet break condition is satisfied.
if (north == HIGH && island == HIGH && south == HIGH && last == 5) { //If all of the circuits are empty, return to the default state.
current = 0;
last = current;
crossingState = ST_OFF;
} else if (north == LOW && island == HIGH && south == LOW && last == 5) { //Reactivate the light and bell sequence if both approach circuits are activated even if the island isn't.
current = 3;
last = current;
crossingState = ST_ISLAND;
} else if (north == LOW && island == LOW && south == LOW && last == 5) { //Reactivate the light and bell sequence if all of the circuits are activated.
current = 3;
last = current;
crossingState = ST_ISLAND;
} else if (north == LOW && island == LOW && south == HIGH && last == 5) { //Reactivate the light and bell sequence if one of the approach circuits and the island circuit are activated.
current = 3;
last = current;
crossingState = ST_ISLAND;
} else if (north == HIGH && island == LOW && south == LOW && last == 5) { //Reactivate the light and bell sequence if one of the approach circuits and the island circuit are activated.
current = 3;
last = current;
crossingState = ST_ISLAND;
} else if (north == HIGH && island == LOW && south == HIGH && last == 5) { //Reactivate the light and bell sequence if the island is activated even if the approaches aren't.
current = 3;
last = current;
crossingState = ST_ISLAND;
}
}