TCRT 5000 Dual Sensor - Model Railroad Application

Hello Group,

I have several TCRT sensors being used as track block occupancy detectors and an oval setup of track and the oval is split in two making two track occupancy blocks. Now as you can see by the code below the there are different states the block can be in: occupied or un occupied. Green being clear/un occupied and Red being occupied. There are 2 sensors per block, one for entering and one for exiting. The setup works fine in each direction. However here is where I am stuck at. If I choose to reverse the locomotive in either direction the "block" is un changed and will not clear unless the sensor has been tripped. I am having difficulty in figuring this piece of the puzzle out. Thanks in advance.

// CONSTANT VARIABLES

const int westblocksensor    = A0; // BLOCK ONE
const int eastblocksensor    = A1; // BLOCK ONE 
const int westblocktwosensor = A2; // BLOCK TWO
const int eastblocktwosensor = A3; // BLOCK TWO

// VARIABLES THAT WILL CHANGE

int tcrtEBlockState    = 0; // BLOCK ONE
int tcrtWBlockState    = 0; // BLOCK ONE
int tcrtEBlockTwoState = 0; // BLOCK TWO
int tcrtWBlockTwoState = 0; // BLOCK TWO

int BLOCKONEGREEN = 11; // BLOCK ONE
int BLOCKONERED   = 12; // BLOCK ONE
int BLOCKTWOGREEN = 10; // BLOCK TWO
int BLOCKTWORED   =  9; // BLOCK TWO

//#define BLOCKONESTATE
//#define BLOCKLIGHTSTATES

void setup() {
  
// LEDS AS OUPUTS
 
Serial.begin(9600);
pinMode(BLOCKONEGREEN, OUTPUT); // BLOCK ONE
pinMode(BLOCKONERED,   OUTPUT); // BLOCK ONE
pinMode(BLOCKTWOGREEN, OUTPUT); // BLOCK TWO
pinMode(BLOCKTWORED,   OUTPUT); // BLOCK TWO

//pinMode(led, OUTPUT);     
  
// TCRT5000 AS INPUTS

pinMode(westblocksensor,    INPUT); // A0 BLOCK ONE
pinMode(eastblocksensor,    INPUT); // A1 BLOCK ONE
pinMode(westblocktwosensor, INPUT); // A2 BLOCK TWO
pinMode(eastblocktwosensor, INPUT); // A3 BLOCK TWO
digitalWrite(BLOCKONEGREEN, HIGH);  // DEFAULT GREEN ON
digitalWrite(BLOCKTWOGREEN, HIGH); // DEFAULT GREEN ON
} // END OF MASTER SETUP

// BLOCK LIGHT STATES

enum BLOCKLIGHTSTATES {
  ST_GREEN,      // GREEN
  ST_GREENTWO,   // GREEN TWO
  ST_RED,        // RED
  ST_REDTWO,     // RED TWO
};

enum BLOCKTWOLIGHTSTATES {
  ST_GREENTHREE, // GREEN THREE
  ST_GREENFOUR,  // GREEN FOUR
  ST_REDTHREE,   // RED THREE
  ST_REDFOUR,    // RED FOUR
};

// STATE OF BLOCK ONE GREEN

BLOCKLIGHTSTATES    BLOCKONESTATE = ST_GREEN;      // BLOCK ONE
BLOCKTWOLIGHTSTATES BLOCKTWOSTATE = ST_GREENTHREE; // BLOCK TWO

// CURRENT TIME

static unsigned long currentTime;

void loop(){
// GET CURRENT TIME
currentTime = millis();

// read the state of the tcrt5000:
tcrtWBlockState    = digitalRead(westblocksensor);    // BLOCK ONE
tcrtEBlockState    = digitalRead(eastblocksensor);    // BLOCK ONE
tcrtWBlockTwoState = digitalRead(westblocktwosensor); // BLOCK TWO
tcrtEBlockTwoState = digitalRead(eastblocktwosensor); // BLOCK TWO

Serial.println(tcrtWBlockState);  // BLOCK ONE
Serial.print(tcrtEBlockState);    // BLOCK ONE
Serial.print(tcrtWBlockTwoState); // BLOCK TWO
Serial.print(tcrtEBlockTwoState); // BLOCK TWO

switch (BLOCKONESTATE) {

// GREEN LIGHT WILL SWITCH TO RED IF SENSORS ARE TRIGGERED

case ST_GREEN:
BLOCK1GREEN(tcrtWBlockState, tcrtEBlockState);
break;
case ST_GREENTWO:
BLOCK1GREEN1(tcrtWBlockState, tcrtEBlockState);
break;
case ST_RED:
BLOCK1RED(tcrtWBlockState, tcrtEBlockState);
break;
case ST_REDTWO:
BLOCK1RED1(tcrtWBlockState, tcrtEBlockState);
break;
}

switch (BLOCKTWOSTATE) {

// GREEN LIGHT WILL SWITCH TO RED IF SENSORS ARE TRIGGERED

case ST_GREENTHREE:
BLOCK2GREEN(tcrtWBlockTwoState, tcrtEBlockTwoState);
break;
case ST_GREENFOUR:
BLOCK2GREEN2(tcrtWBlockTwoState, tcrtEBlockTwoState);
break;
case ST_REDTHREE:
BLOCK2RED(tcrtWBlockTwoState, tcrtEBlockTwoState);
break;
case ST_REDFOUR:
BLOCK2RED2(tcrtWBlockTwoState, tcrtEBlockTwoState);
break;
}

} // END MASTER LOOP

// BLOCK ONE STATE

void BLOCK1GREEN(int tcrtWBlockState, int tcrtEBlockState){
digitalWrite(BLOCKONEGREEN, HIGH);
digitalWrite(BLOCKONERED, LOW);

if (tcrtWBlockState == LOW && tcrtEBlockState == HIGH) {       // WEST TO EAST - LEFT TO RIGHT - COUNTER CLOCKWISE
// CHANGE TO RED
BLOCKONESTATE = ST_RED;
} else if (tcrtEBlockState == LOW && tcrtWBlockState == HIGH){ // EAST TO WEST - RIGHT TO LEFT - CLOCKWISE
// CHANGE TO RED
BLOCKONESTATE = ST_REDTWO;
}

} //END VOID

void BLOCK1GREEN1(int tcrtWBlockState, int tcrtEBlockState){

// DURATION RED TIMER
const unsigned long duration = 2000;
static unsigned long startTime = 0;

if (startTime == 0){

// TURN RED TIMER
digitalWrite(BLOCKONEGREEN, LOW);
digitalWrite(BLOCKONERED,  HIGH);

startTime = currentTime;

// DO NOTHING
return;
}

// IF UNKNOWN SECONDS HAVE PASSED
if (currentTime - startTime >= duration){
  
// RESET VARIABLES START FROM ZERO

startTime = 0;

// CHANGE TO GREEN

BLOCKONESTATE = ST_GREEN;
}

} // END VOID

void BLOCK1RED(int tcrtWBlockState, int tcrtEBlockState){
digitalWrite(BLOCKONEGREEN, LOW);
digitalWrite(BLOCKONERED, HIGH);

if (tcrtWBlockState == HIGH && tcrtEBlockState == LOW){

// CHANGE TO GREEN

BLOCKONESTATE = ST_GREENTWO;
}

} // END VOID

void BLOCK1RED1(int tcrtWBlockState, int tcrtEBlockState){
digitalWrite(BLOCKONEGREEN, LOW);
digitalWrite(BLOCKONERED, HIGH);

if (tcrtWBlockState == LOW && tcrtEBlockState == HIGH){

// CHANGE TO GREEN

BLOCKONESTATE = ST_GREENTWO; // TIMER
}

} // END VOID

// BLOCK TWO STATE

void BLOCK2GREEN(int tcrtWBlockTwoState, int tcrtEBlockTwoState){
digitalWrite(BLOCKTWOGREEN, HIGH);
digitalWrite(BLOCKTWORED, LOW);

if (tcrtWBlockTwoState == LOW && tcrtEBlockTwoState == HIGH){
// CHANGE TO RED
BLOCKTWOSTATE = ST_REDTHREE;
} else if (tcrtEBlockTwoState == LOW && tcrtWBlockTwoState == HIGH){
// CHANGE TO RED
BLOCKTWOSTATE = ST_REDFOUR;
}
}

void BLOCK2GREEN2(int tcrtWBlockTwoState, int tcrtEBlockTwoState){

// DURATION RED TIMER
const unsigned long duration = 2000;

// START TIME
static unsigned long startTime = 0;

if (startTime == 0){

// TURN RED TIMER
digitalWrite(BLOCKTWOGREEN, LOW);
digitalWrite(BLOCKTWORED,  HIGH);

// START TIMER

startTime = currentTime;

// DO NOTHING
return;
}

// IF UNKNOWN SECONDS HAVE PASSED
if (currentTime - startTime >= duration){
  
// RESET VARIABLES START FROM ZERO
startTime = 0;

// CHANGE TO GREEN

BLOCKTWOSTATE = ST_GREENTHREE;
}
}

void BLOCK2RED(int tcrtWBlockTwoState, int tcrtEBlockTwoState){
digitalWrite(BLOCKTWOGREEN, LOW);
digitalWrite(BLOCKTWORED, HIGH);

if (tcrtWBlockTwoState == HIGH && tcrtEBlockTwoState == LOW){

// CHANGE TO GREEN

BLOCKTWOSTATE = ST_GREENFOUR;
}
}

void BLOCK2RED2(int tcrtWBlockTwoState, int tcrtEBlockTwoState){
digitalWrite(BLOCKTWOGREEN, LOW);
digitalWrite(BLOCKTWORED, HIGH);

if (tcrtWBlockTwoState == LOW && tcrtEBlockTwoState == HIGH){

// CHANGE TO GREEN

BLOCKTWOSTATE = ST_GREENFOUR;
}
}

You code is a bit complex to look at for a short time only, but thinking about the problem, and assuming that a change from active to inactive describes the triggering of a sensor:

  1. Block_occupied state is set to false.
  2. If the sensor at either end transitions from inactive to active then block_occupied is true.
  3. If the sensor at the other end transitions from active to inactive, then block_occupied is false.

A sensor goes inactive->active when the train start passing over it and active->inactive when there was a train but it is now passed. Looking for the transition means that it should automatically handle either direction without knowing the travel direction.

Can you post a simple drawing showing where the sensors are located on the track, with labels? Also, do you have the sensors aimed diagonally so they don't trigger on gaps between cars?

As you can see from the image direction of travel is counter clockwise is west to east and counter clockwise is east to west. The red letters are the sensor inputs and locations one placed at the entrance and exit of the block which the block boundary is represented by the yellow line. As an example when sensor A0 is crossed (A0 is LOW State A1 is HIGH State) the LEDS go from green to RED, once A1 is crossed (A1 is LOW State and A0 is HIGH State) this triggers the timer to set the LEDS after 2 seconds back to Green. Samething happens in Block 2. Now if you go from East to West which is clockwise and A2 is triggered however the signal does NOT change from Red to Green in block two and this is because A3 hasn't been triggered.

DEMO-BLOCK-1.jpg

PilotinControl:
As you can see from the image direction of travel is counter clockwise is west to east and counter clockwise is east to west.

Honestly, I don't think so.

And even with that extremely basic image still no clue of what you're trying to do here, really.

Where are those sensors exactly - at the start and end of each block or just in the middle or some other configuration? How do you see whether a block is occupied or not (i.e. detecting trains entering/leaving a specific block)? How do you plan to detect direction?

The yellow line represents the division point of block 1 and block 2. The sensors are placed between the rails at the entrance and exit of the block. The trains passes over A0.....which triggers LOW and the signal goes from red to green. It travels around the half circle then passes over A1 which triggers a 2 second timer then the signal goes from Red to Green...the train passes over sensor A2 which triggers the signal to go from Green to Red, then the train passes over sensor A3 which triggers a 2 second timer and then the signal changes from Red to Green and the cycle continues. However say the train stops in the middle between the 2 sensors of A0 and A1 and reverses direction....it passes over A0 the signal is NOT triggered and does NOT change from Red to Green....if you pass over sensor A3 the signal does trigger from Green to Red. This is where I am stuck.

PilotinControl:
However say the train stops in the middle between the 2 sensors of A0 and A1 and reverses direction....it passes over A0 the signal is NOT triggered and does NOT change from Red to Green....if you pass over sensor A3 the signal does trigger from Green to Red. This is where I am stuck.

How about: if sensor A3 triggered and zone west occupied then when A0 clears, clear zone west.

In other words you are stating: A0 = LOW && A3 = LOW Block 1 would go from RED to GREEN traveling East to West? or is it A3 = LOW && A0 = LOW Block 1 would go from RED to GREEN traveling East to West? or is it A0 = HIGH && A3 = LOW? Or is it something totally different?

Your problem is that you can't sense direction with one sensor. What you have works with an assumed direction of travel.

How would your system work if you started with the train in the center of the block and moved in the reverse direction (I guess between A0 and A1)? A0 would be tripped and the block would be occupied, correct?

Need to double the number of sensors at the ends of the blocks. Example: A0a, A0b. A1b, A1a (a - outer sensor, b inner sensor).
A0a>>A0b - moving into block, at the A0 end. Block occupied. Will be cleared EITHER when A1b>>A1a sequence OR A0b>>A0a sequence.
A0b>>A0a - moving out of block, at the A0 end.

Here's some pseudocode for how I'm thinking. It's only half thought out so it may be full of holes.

Note: goes true/false implies a transition, is true/false implies a steady state

// assumed: true == 1, false == 0, sensor blocked by train == 1, set = bit (bool val) set to 1, reset = bit cleared to 0
if A0 goes true
set westoccupied = true // a train is in west zone
// later train reverses and recrosses A0 going east
If A0 goes true do nothing – or set westoccupied = true
If A3 goes true{
set eastoccupied = true
if A0 == true && westoccupied == true
set A0_to_A3_transit = true
}
if A0 goes false && A0_to_A3_transit == true && A3 == true
reset westoccupied to == false
reset A0_to_A3_transit = false

@ MikeLittle: not exactly with how the setup is going to be there will never be a train entering a middle of a block with out triggering a sensor. If the system is shutdown there will be a "last" state implemented to remember the last state of the block: occupied or un-occupied.

@ dougp: I see your logic, I guess I am going to have to implement true/false in addition to the High/Low states?

PilotinControl:
@ dougp: I see your logic, I guess I am going to have to implement true/false in addition to the High/Low states?

Not sure what you mean by that. High = true = 1. It seems what I outlined necessitates sensing the rising / falling edges of the sensor input(s) and then creating some state variables (westoccupied, in_transit_x, etc.) to keep track of what's going on.

You only know that a sensor has been crossed. You do not know which direction it was crossed, unless you assume that there is only one direction of travel. But in the OP, you say that sometimes you reverse direction.

Therefore, you either have to add hardware to determine direction, or you have to make assumptions in the code that will make the best guess of the inputs.

No new sensors:
If any sensor it tripped (either A0 or A1), and the current state of the block is empty, then the assumption is that it is now occupied.

If any sensor is tripped, and the current state is occupied, then the assumption is empty.

Since you don't know which direction the train is traveling when the sensor is tripped, you have to rely on the current state of the block to determine what state it is in after the trip.

Double sensors at the block ends let you know what direction the train is moving in regards to the block. You would set the block state as in above, but you don't have to assume the current state of the block is accurate. The system can start in any condition and know what state it is in as soon as the train moves over a sensor. Until then, it is blind.

I agree you need further sensors to easily
Determine direction -maybe you could get away with just a third sensor in the middle of a section ?

Ok....so I re arranged the code to make it so that direction is not a factor any more....however I have a new problem that has crept up and my code is below:

// CONSTANT VARIABLES

const int blockonewestsensor = A0; // BLOCK ONE WEST
const int blockoneeastsensor = A1; // BLOCK ONE EAST
const int blocktwowestsensor = A2; // BLOCK TWO WEST
const int blocktwoeastsensor = A3; // BLOCK TWO EAST

// VARIABLES THAT WILL CHANGE

int tcrtWBlock1State = 0;         // variable for reading the TCRT5000 status
int tcrtEBlock1State = 0;
int tcrtWBlock2State = 0;         
int tcrtEBlock2State = 0;

int BLOCKONERED   = 12; // BLOCK ONE
int BLOCKONEGREEN = 11; // BLOCK ONE
int BLOCKTWOGREEN = 10; // BLOCK TWO
int BLOCKTWORED   =  9; // BLOCK TWO

void setup() {
  
// initialize the LED pin as an output:
 
Serial.begin(9600);
pinMode(BLOCKONEGREEN, OUTPUT); // BLOCK ONE
pinMode(BLOCKONERED,   OUTPUT); // BLOCK ONE
pinMode(BLOCKTWOGREEN, OUTPUT); // BLOCK TWO
pinMode(BLOCKTWORED,   OUTPUT); // BLOCK TWO

pinMode(blockonewestsensor, INPUT);
pinMode(blockoneeastsensor, INPUT);
pinMode(blocktwowestsensor, INPUT);
pinMode(blocktwoeastsensor, INPUT);
//digitalWrite(blockoneeastsensor, HIGH); 
//digitalWrite(blockonewestsensor, HIGH);
digitalWrite(BLOCKONEGREEN, HIGH);
digitalWrite(BLOCKTWOGREEN, HIGH);

} // END MASTER SETUP

void loop(){

// read the state of the tcrt5000:
tcrtWBlock1State = digitalRead(blockonewestsensor); // BLOCK ONE
tcrtEBlock1State = digitalRead(blockoneeastsensor); // BLOCK ONE
tcrtWBlock2State = digitalRead(blocktwowestsensor); // BLOCK TWO
tcrtEBlock2State = digitalRead(blocktwoeastsensor); // BLOCK TWO

Serial.println(tcrtWBlock1State); // BLOCK ONE
Serial.print(tcrtEBlock1State);   // BLOCK ONE
Serial.print(tcrtWBlock2State);   // BLOCK TWO
Serial.print(tcrtEBlock2State);   // BLOCK TWO


if (tcrtEBlock2State == LOW && tcrtWBlock1State == HIGH || tcrtWBlock2State == LOW && tcrtEBlock1State == HIGH) {      
digitalWrite(BLOCKONERED,   LOW);
digitalWrite(BLOCKONEGREEN, HIGH);
digitalWrite(BLOCKTWORED,   HIGH);
digitalWrite(BLOCKTWOGREEN, LOW); 
} else if (tcrtEBlock1State == LOW && tcrtWBlock2State == HIGH || tcrtWBlock1State == LOW && tcrtEBlock2State == HIGH) {      
digitalWrite(BLOCKONERED,   HIGH);
digitalWrite(BLOCKONEGREEN, LOW);
digitalWrite(BLOCKTWORED,   LOW);
digitalWrite(BLOCKTWOGREEN, HIGH);
} 

} // END MASTER LOOP

I have kept everything simple... the problem is now instead of changing from red to green....it alternates twice before settling down on the correct aspect....is it a debouncing issue? Thanks in advance!