Your code does not quite reflect your description in the opening post. There are some delays that I can't place.
But anyway, you should change your thinking.
Your light can be in one of 4 states.
1)
Green, waiting for sensors to be triggered when train enters block.
2)
Red, waiting for sensors to be triggered when train leaves block.
3)
Flashing yellow 10 times after a train has left the block.
4)
Steady yellow (you don't mention how long).
Based on that, you can now define the states of statemachine.
enum LIGHTSTATES
{
ST_GREEN, // light green
ST_YELLOWFLASH, // light yellow flash
ST_YELLOW, // yellow on for given time
ST_RED, // light red
};
// state of statemachine for block 1; initial state green
LIGHTSTATES block1State = ST_GREEN;
The above first defines the four states. The enum is like a 'type' but it can only take the values that are specified. Next a variable is declared that will keep track of the state of block 1 (I assume you might want to extend this to more blocks in the future).
The state machine is basically very simple using a switch/case; it will become tedious to maintain if the number of states grows, but for this it will do.
We will base the rest of the code on delays based on millis(); see e.g. the BlinkWithoutDelay example that comes with the IDE. It's always a good habit to forget that delay exists; in this case because you might want to expand to two or more blocks and delays will block execution (and worse, detection of trains in other blocks).
...
...
// current time
static unsigned long currentTime;
void loop()
{
// read inputs
...
...
switch(block1State)
{
case ST_GREEN:
// do something
...
...
break;
case ST_RED:
// do something
...
...
break;
case ST_YELLOWFLASH:
// do something
...
...
break;
case ST_YELLOW:
// do something
...
...
break;
}
}
Now you can write functions to do the work for each state.
For the 'green' state, the code will simply switch to green and next wait for the inputs to be triggered. Only when one of the inputs is triggered, it will set the block1State variable to a different value; this value is the next step (state) that you want to be done (in this case 'red').
For the 'red' state, the code is basically the same; different light and switching to 'yellow flash'
/*
switch green light on for block 1
'wait' for sensors to trigger and switches to red
input: values of sensors A1 and A2
*/
void block1Green(int valA1, int valA2)
{
digitalWrite(ledPinGRN, LOW);
digitalWrite(ledPinYEL, HIGH);
digitalWrite(ledPinRED, HIGH);
if (valA1 > 600 || valA2 > 600)
{
// change state to red
block1State = ST_RED;
}
}
/*
switch red light on for block1
'wait' for sensors to trigger and switches to yellow flash
input: values of sensors A1 and A2
*/
void block1Red(int valA1, int valA2)
{
digitalWrite(ledPinGRN, HIGH);
digitalWrite(ledPinYEL, HIGH);
digitalWrite(ledPinRED, LOW);
if (valA1 > 600 || valA2 > 600)
{
// change state to yellow flash
block1State = ST_YELLOWFLASH;
}
}
The code for the 'yellow flash' is based on the BlinkWithoutDelay example.
/*
flash yellow led 10 times for block 1
after that, switch to steady yellow
input: values of sensors A1 and A2; both are not used but added for consistency in the calls to the light functions
*/
void block1YellowFlash(int valA1, int valA2)
{
// on and off duration
const unsigned long duration = 500;
// number of flashes
const byte numFlashes = 10;
// remember last time that we changed from 'yellow on' to 'yellow off' or vice versa
static unsigned long startTime = 0;
// keep a counter for the number of flashes
static byte counter = 0;
// if called after coming from red
if (startTime == 0)
{
// switch yellow on
digitalWrite(ledPinGRN, HIGH);
digitalWrite(ledPinYEL, LOW);
digitalWrite(ledPinRED, HIGH);
// start timing
startTime = currentTime;
// nothing else to do
return;
}
// if it's time to toggle the yellow led
// based on blink without delay
if (currentTime - startTime >= duration)
{
// set start time
startTime += duration;
// toggle the yellow led
digitalWrite(ledPinYEL, !digitalRead(ledPinYEL));
// increment the counter
counter++;
}
if (counter > numFlashes * 2)
{
// reset variables
// as a result, the next time this function is called, the sequence is started from scratch
startTime = 0;
counter = 0;
// change state to steady yellow
block1State = ST_YELLOW;
}
}
The code keeps track of a start time of a delay and a counter. The value 0 for start time indicates the start of a new sequence. In that case the yellow led is switched on and the start time is set to the 'current time'.
Next it checks if the blink duration has passed. If so, it toggles the yellow led, sets a new start time and increments a counter that keeps track of the number of flashes.
Next it checks the counter for the number of flashes; because the counter is incremented on every toggle, we multiply the check value by 2.
If the limit is reached, variables are reset so the next time that the function is called it will start from scratch and the state is changed to the next step that needs to be done ('yellow').
The last function is for steady 'yellow'.
/*
switch yellow light on for N seconds for block 1
after that, switch to green
input: values of sensors A1 and A2; both are not used but added for consistency in the calls to the light functions
*/
void block1Yellow(int valA1, int valA2)
{
// duration for yellow on
const unsigned long duration = 2000;
// start time of delay
static unsigned long startTime = 0;
if(startTime == 0)
{
// switch yellow on
digitalWrite(ledPinGRN, HIGH);
digitalWrite(ledPinYEL, LOW);
digitalWrite(ledPinRED, HIGH);
// set start time of delay
startTime = currentTime;
// nothing else to do
return;
}
// if N milliseconds passed
if(currentTime - startTime >= duration)
{
// reset variables so next call to this sequence will start sequence from scratch
startTime = 0;
// change state to green
block1State = ST_GREEN;
}
}
This function simply switches yellow on if 'the delay' was not started (start time is 0) and set the start time of the delay in that case.
Next it checks if the delay time has passed. If so, it resets the start time (see above) and switches to the next step (state; 'green').
The full code in the next post.
Note:
Be aware that the functions for the states are executed thousands of times per second. This might pose an issue because there is no check if a sensor goes to the non-triggered state. So if a train blocks e.g. the sensor A1 after the yellow LED has been steady on, it ill be seen as "OK, switch to green".