In post & video 20 we made a railway crossing, but it was just one way. There have been requests for a two way railway crossing and also for two tracks. Well … why limit ourselves to two tracks … here’s a software download for a multi track two way crossing.
For every track two sensors need to be placed, one left and one right from the sensor. The distance of the sensors to the gate should be such that there is enough time to close the gate before the train is there.
Some types of sensor can trigger multiple times with one train, like optical sensors that see every wagon. To deal with this an ‘end of train’ detection is built in. If the departure sensor is triggered, a timer is started. If the sensor triggers again before the timer was at zero, the timer resets again. When the timer reaches zero, the software decides this was the end of train.
Such timer is not 100% fail safe. When a train stands still while the optical sensor beam is right in between two wagons, ‘end of train’ may be given falsely. Chances of this happening are very slim though and at the moment I don’t know of another way to detect ‘end of train’ without using additional sensors. A workaround is to place the sensor beam diagonal.
Of course with multiple tracks it is possible another train is coming while the cycle that blinks the LEDs and closes the gate is already running. To handle this, a train counter is introduced, which is incremented each time a new train is detected and is decremented when an ‘end_of_train’ is detected. When the counter reaches zero the gates open and the cycle stops.
#define GATE_SPEED 120 // [ms] lower number is higher servo speed
#define BLINK_SPEED 400 // [ms] smaller number is faster blinking
#define GATE_DELAY 2000 // [ms] time between start blinking and gate closing
#define END_OF_TRAIN_DELAY 2000 // [ms] time to wait before deciding this was the end of the train
#define GATE_OPEN_ANGLE 90
#define GATE_CLOSED_ANGLE 10
#define SERVO_PIN 12
#define LED1_PIN 2
#define LED2_PIN 3
#define NUM_SENSORS 4 // two sensors per track, one left and one right of the gate
byte sensor_pin[NUM_SENSORS] = {8,9,10,11}; // sensor pin numbers
byte state = 1, train_counter, n;
byte led1, led2, blink_enabled;
byte angle = GATE_OPEN_ANGLE;
byte setpoint = GATE_OPEN_ANGLE;
byte sensor_state[NUM_SENSORS]; // 0 idle, 1 detect arrival, 2 detect departure, 3 detect end of train
byte end_of_train[NUM_SENSORS]; // 0 idle, 1 end of train detected
unsigned long time_to_blink;
unsigned long time_to_close_gate;
unsigned long time_for_servo_step;
unsigned long time_end_of_train[NUM_SENSORS];
#include <Servo.h>
Servo gate_servo;
void setup() {
pinMode(LED1_PIN, OUTPUT);
pinMode(LED2_PIN, OUTPUT);
for (byte i = 0; i < NUM_SENSORS; i++) pinMode(sensor_pin[i], INPUT_PULLUP);
gate_servo.attach(SERVO_PIN);
gate_servo.write(angle);
Serial.begin(9600);
Serial.println("Railway Crossing Control Ready");
Serial.println();
Serial.println("Waiting for train");
for (byte i = 0; i < NUM_SENSORS; i++) sensor_state[i] = 1; // enable sensors for train detection
}
void loop() {
for (byte i = 0; i < NUM_SENSORS; i++) {
if(sensor_state[i] == 1) { // detect arrival of new train
if(!digitalRead(sensor_pin[i])) { // train detected
train_counter++;
sensor_state[i] = 0;
if(i%2) n = i - 1; else n = i + 1;
sensor_state[n] = 2; // buddy sensor departure detection enabled
Serial.print("Arrival: ");
Serial.println(i);
Serial.print("Trains: ");
Serial.println(train_counter);
}
}
else if(sensor_state[i] > 1) {
if(!digitalRead(sensor_pin[i])) { // departure detected
time_end_of_train[i] = millis() + (unsigned long)END_OF_TRAIN_DELAY;
if(i%2) n = i - 1; else n = i + 1;
sensor_state[n] = 1; // buddy sensor enabled again
if(sensor_state[i] == 2) {
Serial.print("Departure: ");
Serial.println(i);
}
sensor_state[i] = 3;
}
if(sensor_state[i] == 3) // decide if end of train has passed based on a timer
if(millis() > time_end_of_train[i]) end_of_train[i] = 1;
if(end_of_train[i]) { // this takes care train_counter-- is executed only once
train_counter--;
end_of_train[i] = 0;
sensor_state[i] = 1;
Serial.print("Trains: ");
Serial.println(train_counter);
}
}
}
switch (state) {
case 1: // gate open, not blinking, waiting for train arrival
if(train_counter) state = 12;
break;
case 12: // train arrival detected, blinking
Serial.println("Binking started");
blink_enabled = 1;
time_to_close_gate = millis() + (unsigned long)GATE_DELAY;
state = 2;
break;
case 2: // blinking, wait until it's time to close the gate
if (millis() > time_to_close_gate) state = 23; // gate delay time has passed
break;
case 23: // close the gate
Serial.println("Gate closing");
gate_servo.attach(SERVO_PIN);
setpoint = GATE_CLOSED_ANGLE;
state = 3;
break;
case 3: // gate is closing, blinking
if(angle == setpoint) {
Serial.println("Gate closed");
state = 4;
}
break;
case 4: // gate fullly closed, blinking, waiting for train departure
if(train_counter == 0) state = 45;
break;
case 45: // train departure detected, open the gate, blinking
Serial.println("Gate opening");
gate_servo.attach(SERVO_PIN);
setpoint = GATE_OPEN_ANGLE;
state = 5;
break;
case 5: // wait until gate is fully opened, blinking
if(train_counter) state = 23;
if (angle == setpoint) state = 51;
break;
case 51: // gate is fully opened, stop blinking
// for (byte i = 0; i < NUM_SENSORS; i++) sensor_state[i] = 1;
blink_enabled = 0;
led1 = 0;
led2 = 0;
gate_servo.detach(); // to avoid servo flutter
state = 1;
Serial.println("Gate open, blinking stopped");
Serial.println();
Serial.println("Waiting for train");
break;
}
if (millis() > time_for_servo_step) {
time_for_servo_step = millis() + (unsigned long)GATE_SPEED;
if (angle < setpoint) angle++;
if (angle > setpoint) angle--;
gate_servo.write(angle);
}
if(blink_enabled == 1) {
if(millis() > time_to_blink) {
time_to_blink = millis() + (unsigned long)BLINK_SPEED;
led1 = !led1;
led2 = !led1;
}
}
digitalWrite(LED1_PIN, led1);
digitalWrite(LED2_PIN, led2);
}
— 0 —
Hello, and a very huge thank you for this arduino grade crossing update that you are so wonderfully giving to the model railroading community. It is people like you with such great talent and knowledge who are willing to share with us for free. On behalf of myself, my friends and the model railroading community, thank you and stay safe.
LikeLiked by 1 person
Well, thanks for your feedback, it’s appreciated.
LikeLike
Hallo Rudy,
auch von mir ein herzliches Dankeschön für diese tolle Arbeit. Gibt es ev. auch ein Video davon?
Gruß
Peter
LikeLike
There’s no video yet. But then, such video would look similar to the video of the single track crossing.
LikeLike
This is ok but what if you have no gates but still need two tracks for crossing?
LikeLike
I’m not sure what you mean, or try to accomplish?
LikeLike
Thanks Rudy for the sketch as this is what I was looking for.
I slightly modified it for the use of 2 sensors. Why? I am using 2 tracks in a railroad crossing situation with 2 servo and a MP3 playing some bell sound and singing birds every time a loco passes. To avoid false triggering I 3D printed some supports that allow me to install 2 modified sensors: an IR emitting LED at 45° of the IR receiver.
I tested the whole bunch with Hall sensors and also with not modified IR sensors and everything works exactly as I expected but … when the modified IR sensors are in use I am in trouble.
Due to the modification (IR emitting LED in front of the IR receiver) the whole sketch is started at power up. So somewhere something has to be inverted. But what?
LikeLike
Hi. At several places in the code you’ll find digital read statements. The have an exclamation mark ! in front of them. The ! means ‘NOT’. If you take them out you’ll have your signal reads inverted.
LikeLike
Would it be possible to use photoresistors in place of the IR sensors? Or, possible use phototransistors?
LikeLike
Any type of sensor is possible as long as you offer a clear LOW (below 1V) and HIGH (above 4V) signal to the input of the Arduino.
LikeLike
got this working great put the extra servo and 2 more leds into the code and just delete 2 sensors and adjusted the code for them. a brilliant piece of work Rudy.
LikeLike
Rudy
Can you explain how this bit of the code works please
void loop() {
for (byte i = 0; i < NUM_SENSORS; i++) {
if(sensor_state[i] == 1) { // detect arrival of new train
if(!digitalRead(sensor_pin[i])) { // train detected
train_counter++;
sensor_state[i] = 0;
if(i%2) n = i – 1; else n = i + 1;
sensor_state[n] = 2; // buddy sensor departure detection
i think
it takes the number of the sensor ie 0,1,2, or 3
and it works out that n = its buddy number using " if(i%2) n = i – 1; else n = i + 1;"
using modulo operator i can understand if sensor 0 then buddy would be sensor 1 and the other way around. but i cant work out how we get the sensor 2 then buddy = 3 and the other way around. is it to do with the else statment?
regards
john
LikeLike
Sensor numbers are in pairs: 0,1 then 2,3 then 4,5. The modulo 2 statement tells us it’s odd or even, for any number the result is 0 (even) or 1 (odd). If the number is odd, then one is subtracted to find its buddy (if the triggered sensor was 3, then its buddy is 2). Else, the triggered sensor was even and then we add one to get to the buddy.
LikeLiked by 1 person
Rudy
Thank you so much i am fairly new to coding so some of the code structure is new to me. i think this code is amazing. i did enjoy the previous versions you did which i must admit i found easier to understand and follow and liked the use of the transition() between the states.
it would be great for newer models and arduino users if you could do a video for this new code as it would teach us all a lot more. i know for me i like to learn from others and not just put the code to use without understanding it.
The parts are now being 3d printed to house the servo’s and led housing on the way as well, i am looking forward to seeing this on my layout.
LikeLike
Why are my leds keep blinking? When the gate is open ?
LikeLike
Hello, Rudy, Excellent videos and better yet are your explanations of how the code works. I have been able to get almost all of the Fun with Arduino projects to work as you described. How ever I am having some trouble with #20 and #42 Railway crossing. First I down loaded RBO_Railway_Crossing_Multi_Track_Two_Way.ino and I loaded it to an arduino nano and it seemed to work OK however. When I had a long train activate the Departure sensor while the Arrival sensor is still activated, lights flash and never stop even after train is long gone. The message that prints out seems to indicate that the Trains count just goes from 1 to 255 and then repeats. See below.
I noticed in the video with #20 you tested with a train that did not cover both sensors at the same time. So maybe you would not have seen this problem.
I also noticed that the first line in Case 51 is commented out. I tried removing the comment “//” but the result was the same.
Short train works OK;
Waiting for train
Arrival: 1
Trains: 1
Binking started
Gate closing
Gate closed
Departure: 0
Trains: 0
Gate opening
Gate open, blinking stopped
Long train does not work correctly;
Waiting for train
Arrival: 1
Trains: 1
Binking started
Gate closing
Gate closed
Departure: 0
Arrival: 1
Trains: 2
Departure: 0
Arrival: 1
Trains: 3
Departure: 0
Arrival: 1
Trains: 4
Departure: 0
Arrival: 1
Trains: 5
Departure: 0
Arrival: 1
Trains: 6
I want to thank you for the most exultant work and for sharing so we all can learn.
Milton
LikeLike
Hi Milton. It seems like you ran into a situation which I never tested. I never thought trains would be that long. 🙂 Looks like we start looking at the entry sensor too soon, we have to wait looking at that sensor until the train has departed. I’ll look into it.
LikeLike
Hi Milton,
I have no means of testing available right now. Could you try the following:
Find the line that says:
sensor_state[n] = 1; // buddy sensor enabled again
With me this is line 71, but your line numbers may differ some.
Cut this line away there and paste it between the lines that read:
if(end_of_train[i]) { // this takes care train_counter– is executed only once
train_counter–;
With me these are lines 80 and 81.
LikeLike
Hi Rudy. Thank you for you help. I have made the changes that you recommended and every thing seems to work as it should. Have a good day.
LikeLike