Hi @loco4515 !
This Wokwi project is following the diagram @alto777 provided in post 3 (thanks for the "1000 words diagram"!
):
https://wokwi.com/projects/457939146850369537
The red button emulates an IR sensor at the start of the block and the green one at the end. The start sensor reacts on the first falling edge (HIGH to LOW), the end button on the last rising edge (LOW to HIGH) assuming that the end sensor is continousely interrupted while the train passes it (if not we have to add a little bit of logic regarding the timing).
Sketch
/*
Forum: https://forum.arduino.cc/t/changing-led-state-with-ir-sensors/1434325/2
Wokwi: https://wokwi.com/projects/457939146850369537
Simple Signal Light
starts with green signal
press red button (train enters block)
signal switches to red
press green button (train starts leaving block)
release green button (train hast completely left block)
signal switches to green again
ec2021
*/
constexpr byte redPin {12};
constexpr byte greenPin {11};
constexpr byte blockInPin {7};
constexpr byte blockOutPin {6};
enum class BLOCK {FREE, OCCUPIED};
BLOCK blockState = BLOCK::FREE;
class blockSensor {
private:
byte pin;
byte state = HIGH;
byte lastState = HIGH;
unsigned long lastChange = 0;
boolean changed() {
byte actState = digitalRead(pin);
if (actState != lastState) {
lastChange = millis();
lastState = actState;
}
if (actState != state && millis() - lastChange > 30) {
state = actState;
return true;
}
return false;
};
public:
void init(byte Pin) {
pin = Pin;
pinMode(pin, INPUT_PULLUP);
};
boolean pressed() {
if (changed()) {
return !state;
} else {
return false;
}
};
boolean released() {
if (changed()) {
return state;
} else {
return false;
}
};
};
blockSensor blockIn, blockOut;
void setup() {
Serial.begin(115200);
pinMode(redPin, OUTPUT);
pinMode(greenPin, OUTPUT);
blockIn.init(blockInPin);
blockOut.init(blockOutPin);
signalToGreen();
Serial.println("Start");
}
void loop() {
stateMachine();
}
void signalToRed() {
digitalWrite(redPin, HIGH);
digitalWrite(greenPin, LOW);
}
void signalToGreen() {
digitalWrite(redPin, LOW);
digitalWrite(greenPin, HIGH);
}
void stateMachine() {
switch (blockState) {
case BLOCK::FREE:
if (blockIn.pressed()) {
Serial.println("Train entered block ...");
signalToRed();
blockState = BLOCK::OCCUPIED;
}
break;
case BLOCK::OCCUPIED:
if (blockOut.released()) {
Serial.println("Train left block ...");
signalToGreen();
blockState = BLOCK::FREE;
}
break;
}
}
For the moment It works in one direction only and demonstrates the principle. We can adopt the principle to work in both directions if required.
Just let us know ...
[Edit:] As it was easy to implement here the version for both directions (left to right or vice versa):
https://wokwi.com/projects/457940661339301889
Sketch Both Directions
/*
Forum: https://forum.arduino.cc/t/changing-led-state-with-ir-sensors/1434325/2
Wokwi: https://wokwi.com/projects/457940661339301889
Simple Signal Light, both directions
starts with green signal
press red or green button (train enters block from LEFT or RIGHT)
signal switches to red
press green or red button (train starts leaving block to the RIGHT or to the LEFT)
release green or red button (train hast completely left the block)
signal switches to green again
ec2021
*/
constexpr byte redPin {12};
constexpr byte greenPin {11};
constexpr byte blockLeftPin {7};
constexpr byte blockRightPin {6};
enum class BLOCK {FREE, OCCUPIED};
BLOCK blockState = BLOCK::FREE;
class blockSensor {
private:
byte pin;
byte state = HIGH;
byte lastState = HIGH;
unsigned long lastChange = 0;
boolean changed() {
byte actState = digitalRead(pin);
if (actState != lastState) {
lastChange = millis();
lastState = actState;
}
if (actState != state && millis() - lastChange > 30) {
state = actState;
return true;
}
return false;
};
public:
void init(byte Pin) {
pin = Pin;
pinMode(pin, INPUT_PULLUP);
};
boolean pressed() {
if (changed()) {
return !state;
} else {
return false;
}
};
boolean released() {
if (changed()) {
return state;
} else {
return false;
}
};
};
blockSensor blockLeft, blockRight;
blockSensor *blockPtr;
void setup() {
Serial.begin(115200);
pinMode(redPin, OUTPUT);
pinMode(greenPin, OUTPUT);
blockLeft.init(blockLeftPin);
blockRight.init(blockRightPin);
signalToGreen();
Serial.println("Start");
}
void loop() {
stateMachine();
}
void signalToRed() {
digitalWrite(redPin, HIGH);
digitalWrite(greenPin, LOW);
}
void signalToGreen() {
digitalWrite(redPin, LOW);
digitalWrite(greenPin, HIGH);
}
void stateMachine() {
switch (blockState) {
case BLOCK::FREE:
if (blockLeft.pressed()) {
blockOccupied("LEFT");
blockPtr = &blockRight;
}
if (blockRight.pressed()) {
blockOccupied("RIGHT");
blockPtr = &blockLeft;
}
break;
case BLOCK::OCCUPIED:
if (blockPtr->released()) {
Serial.println("Train left block ...");
signalToGreen();
blockState = BLOCK::FREE;
}
break;
}
}
void blockOccupied(char *msg) {
Serial.print("Train entered block from ");
Serial.println(msg);
signalToRed();
blockState = BLOCK::OCCUPIED;
}
[Edit no. 2] And here a version that considers that the IR light barrier may be released for short intervals while the train is leaving the block
https://wokwi.com/projects/457942657328082945
Sketch with debounced 'Leaving the Block'
/*
Forum: https://forum.arduino.cc/t/changing-led-state-with-ir-sensors/1434325/2
Wokwi: https://wokwi.com/projects/457942657328082945
Simple Signal Light, both directions, considering that the release my be triggered several times while the train
passes the end of block
starts with green signal
press red or green button (train enters block from LEFT or RIGHT)
signal switches to red
press green or red button (train starts leaving block to the RIGHT or to the LEFT)
release green or red button for at least minReleaseTime (train hast completely left the block)
signal switches to green again
If the button that signalizes the leaving of a block is pressed again within minReleaseTime the block will
be considered to be still occupied.
ec2021
*/
constexpr byte redPin {12};
constexpr byte greenPin {11};
constexpr byte blockLeftPin {7};
constexpr byte blockRightPin {6};
constexpr unsigned long minReleaseTime {1000}; // The time that is required after a release to change state back to FREE
enum class BLOCK {FREE, OCCUPIED, INTERRUPTED};
BLOCK blockState = BLOCK::FREE;
unsigned long releaseTime;
class blockSensor {
private:
byte pin;
byte state = HIGH;
byte lastState = HIGH;
unsigned long lastChange = 0;
boolean changed() {
byte actState = digitalRead(pin);
if (actState != lastState) {
lastChange = millis();
lastState = actState;
}
if (actState != state && millis() - lastChange > 30) {
state = actState;
return true;
}
return false;
};
public:
void init(byte Pin) {
pin = Pin;
pinMode(pin, INPUT_PULLUP);
};
boolean pressed() {
if (changed()) {
return !state;
} else {
return false;
}
};
boolean released() {
if (changed()) {
return state;
} else {
return false;
}
};
};
blockSensor blockLeft, blockRight;
blockSensor *blockPtr;
void setup() {
Serial.begin(115200);
pinMode(redPin, OUTPUT);
pinMode(greenPin, OUTPUT);
blockLeft.init(blockLeftPin);
blockRight.init(blockRightPin);
signalToGreen();
Serial.println("Start");
}
void loop() {
stateMachine();
}
void signalToRed() {
digitalWrite(redPin, HIGH);
digitalWrite(greenPin, LOW);
}
void signalToGreen() {
digitalWrite(redPin, LOW);
digitalWrite(greenPin, HIGH);
}
void stateMachine() {
switch (blockState) {
case BLOCK::FREE:
if (blockLeft.pressed()) {
blockOccupied("LEFT");
blockPtr = &blockRight;
}
if (blockRight.pressed()) {
blockOccupied("RIGHT");
blockPtr = &blockLeft;
}
break;
case BLOCK::OCCUPIED:
if (blockPtr->released()) {
Serial.println("Sensor released ...");
releaseTime = millis();
blockState = BLOCK::INTERRUPTED;
}
break;
case BLOCK::INTERRUPTED:
if (millis() - releaseTime >= minReleaseTime) {
Serial.println("Train left block ...");
signalToGreen();
blockState = BLOCK::FREE;
}
if (blockPtr->pressed()) {
//if the sensor triggers again let's go back to occupied
Serial.println("Oops, train still in block ...");
blockState = BLOCK::OCCUPIED;
}
break;
}
}
void blockOccupied(char *msg) {
Serial.print("Train entered block from ");
Serial.println(msg);
signalToRed();
blockState = BLOCK::OCCUPIED;
}
Have fun!