Model Rail Road Block Signal with IR Detector

OK so I have tried so many things and gotten myself so confused I have erased all the junk out of my original code and need to start fresh with some help.

I am trying to make a block signal using IR detectors.

I have the IR detectors working and a simple IF/ELSE statement making the light stay on green, when a train breaks it, it goes to red, then when the train leaves it, it goes back to green.

I’m trying to make it so once the train leaves the sensor, it turn RED OFF, then turns YELLOW ON for 5 seconds, then back to GREEN ON waiting on the next train.

I feel like this should be really simple but every time I come across a new method it seems to get in the way or undo a previous step.

// These constants won't change:
const int analogPin0 = A0;    // pin that the sensor 1 is attached to
const int analogPin2 = A2;    // pin that the sensor 2 is attached to
const int ledPinGreen = 8;       // Green LED
const int ledPinYellow = 9;       // Yellow LED  
const int ledPinRed = 10;      // Red LED
const int threshold = 900;   // an arbitrary threshold level that's in the range of the analog input

void setup() {
  // initialize the LED pin as an output:
  pinMode(ledPinGreen, OUTPUT);
  pinMode(ledPinYellow, OUTPUT);
  pinMode(ledPinRed, OUTPUT); 
  // initialize serial communications:
  Serial.begin(9600);


}

void loop() {
  // read the value of the IR Sensor:
  int analogValue0 = analogRead(analogPin0);
  int analogValue2 = analogRead(analogPin2); 
  
 
  
 if (analogValue0 > threshold){
    digitalWrite(ledPinRed, HIGH);
    digitalWrite(ledPinGreen, LOW);
    
    digitalWrite(ledPinYellow, LOW);
     }
   
   
   else {
    digitalWrite(ledPinRed, LOW);
    digitalWrite(ledPinYellow, LOW);
    digitalWrite(ledPinGreen, HIGH);
    
  }


  

 
  // print the analog value:
  Serial.println(analogValue0);
  Serial.println(analogValue2);
  delay(100);        // delay in between reads for stability
}

You need to use a "state machine" approach. Your code also makes no attempt to handle the timing elements of your problem (say the 5 second "yellow" period). This gives you a rough idea of how you could approach the problem.

// signal states: green ; red ; yellow
// sensor states: free ; blocked 


if ( sensor_value == blocked ) {
  signal_state = red 
}

if ( sensor_value == free && signal_state == red  ) {
   signal_state = yellow ;
   entered_Yellow_State_at_ms = millis() ;
}


if ( signal_state == yellow && millis() - entered_Yellow_State_at_ms > 5000 ) {
   signal_state = green ;
}


if ( signal_state == yellow ) {
   // show yellow light
}
else if ( signal_state == green ) {
   // show green light
}
else if ( signal_state == red ) {
   // show green red
}

The demo Several Things at a Time illustrates the use of millis() to manage timing. It may help with understanding the technique.

And I agree with @6v6gt.

If you write your program so that the sensors update variables and your signals operate based on the values in those variables then the two parts of the program are effectively separate from one another. That makes the coding much simpler.

...R

So what about using my 2nd sensor to control the light conditions? The if/else statement below halfway work on my breadboard. Do I have them structured wrong?

// These constants won't change:
const int analogPin0 = A0;    // pin that the sensor 1 is attached to
const int analogPin2 = A2;    // pin that the sensor 2 is attached to
const int ledPinGreen = 8;       // Green LED
const int ledPinYellow = 9;       // Yellow LED  
const int ledPinRed = 10;      // Red LED
const int threshold = 900;   // an arbitrary threshold level that's in the range of the analog input

void setup() {
  // initialize the LED pin as an output:
  pinMode(ledPinGreen, OUTPUT);
  pinMode(ledPinYellow, OUTPUT);
  pinMode(ledPinRed, OUTPUT); 
  // initialize serial communications:
  Serial.begin(9600);
}

void loop() {
  // read the value of the IR Sensor:
  int analogValue0 = analogRead(analogPin0);
  int analogValue2 = analogRead(analogPin2); 

 if (analogValue0 > threshold){
    digitalWrite(ledPinRed, HIGH);
    digitalWrite(ledPinGreen, LOW);
    digitalWrite(ledPinYellow, LOW);
     }
 if (analogValue0 > threshold && analogValue2 > threshold){
    digitalWrite(ledPinRed, HIGH);
    digitalWrite(ledPinGreen, LOW);
    digitalWrite(ledPinYellow, LOW);
     }

 if (analogValue0 < threshold && analogValue2 > threshold){
    digitalWrite(ledPinRed, LOW);
    digitalWrite(ledPinGreen, LOW);
    digitalWrite(ledPinYellow, HIGH);
     }

     
   
     else {
    digitalWrite(ledPinRed, LOW);
    digitalWrite(ledPinYellow, LOW);
    digitalWrite(ledPinGreen, HIGH);
    
  }

  // print the analog value:
  Serial.println(analogValue0);
  Serial.println(analogValue2);
  delay(100);        // delay in between reads for stability
}

What is the relationship between the two sensors? I assume they’re at different positions on the layout. How (in English, not C++) do you expect them to control the lights? Can locomotives approach from two directions? Does that change the algorithm.

Trying to execute your code mentally:

If analogValue0 > threshold the first if clause is true so the red light comes on. The second if does the same thing so the value of analog2 is irrelevant.

We’ve already established that analogValue0 > threshold so the next if is false, so we come to the else clause and so the green light comes on. Red was on so briefly you won’t’ see it.

Similarly, if analogValue0 < threshold the light illuminated depends on analog2: if it’s greater than threshold, light is yellow, otherwise green.

You may be able to use the two sensors to control the lights, but as you have it the logic appears flawed. I’d suggest though that you take the advise about states given above and control it that way.

State machines have a bit of a learning curve - there’s a lot of head scratching before you get it. Once you do though, they are very powerful. You’ll also likely wonder what was preventing you from understanding it in the first place - examples help.

See the attached Pics. So I’m not very worried about 2 direction. When the lead engine passes the 1st sensor (A0) if drops to red from green. that block is now occupied and no other train is allowed in it. when it trips the 2nd sensor (A2) it remains red. When the tail car clears 1st signal (A0). display a yellow only. When the train clears the 2nd sensor (A2). back to green. That block is now clear.

Thanks!

Images from Reply #5 so we don’t have to download them. See this Image Guide

871b0110e1685600706abfb176f5b33e2d2018ec.jpg

0a0c0f163abe3979cafdd359638c9ae0ab8e5943.jpg

…R

I don't see why you need to check for BOTH detectors being triggered. Sorry, I had not realized that the second detector was to identify when the train had exited the block.

And your code in Reply #3 has not taken account of the advice in Reply #1. If you follow that advice I suspect everything else will drop neatly into place.

...R

So it looks like you need a state machine that takes three states: EnteringBlock, LeavingBlock and BlockEmpty.

Start with state = BlockEmpty The only thing that can happen in this state is if A0 is broken, state =Entering block;

The only interesting thing that happens when in Entering Block state is when A2 is broken and we can transition to state=Leaving Block.

When in state LeavingBlock, we are looking for A2 to be clear again and we can set state to BlockEmpty again.

A switch statement will help hook this up and you'll need a state variable to check in that switch. The state tells you which light should be on.

One problem may be the gap between rolling stock - does the beam stay broken for the length of the train?

OK let me spend some time on state machines working on this.

As far as the beam, I'm really surprised how sensitive and accurate these sparkfun sensors are. I was thinking of angling them skewed across the track to prevent gaps from picking up. Because these would definitely pick up any momentary gap. I'm assuming the "debounce" can be applied if this is an issue?

Thanks!

I corrected an error I had in Reply #7. However I think I was still partly correct. A short train will not be able to trigger the detectors at both ends of a block at the same time so the logic should not rely on that.

I think @wildbill has it right in Reply #8

...R