This module of the RaylFX system uses Arduino to control a level crossing. It consists of two boom barriers, two St. Andrew’s crosses and street lights for the night. The closing is triggered by two TCRT5000 light barriers.
For proper operation of the lighting, this module must be connected to the RaylFX control module.
Components
- 1x Arduino Nano*
- 1x Breadboard and jumper wires*
- 2x TCRT5000 photoelectric sensor*
- 2x Servo-motor* (I’d recommend buying better servos, because they need to be triggered relatively often..)
- 4x 220 Ohm resistor
- LEDs*
Wiring Diagram
This module also works with an Arduino Nano. The red LED in the wiring diagram is connected to the digital pin 5 on the Arduino board via a 220 Ohm resistor. You can simply connect more LEDs in parallel to the red LED here. However, they should still share one resistor together in series. LEDs can be connected to pin 3 and 6 as street lights, with the one on pin 6 flickering randomly.
The boom barrier servos are connected to pins 8 and 9. Pins 2 and 4 are connected to the digital pins (D0) of two TCRT5000 photoelectric sensor. Their sensitivity can be adjusted by the integrated potentiometers. As soon as one of the light barriers triggers, the St. Andrew’s crosses flash and after an adjustable time the barriers close. When both photoelectric sensor are unobstructed again, the boom barrier opens and the St. Andrew’s crosses stop flashing.
Attention: The control signal of the RaylFX control module is applied to pin A4. It controls the time of day and is required for the correct operation of the street lighting.
Code Settings
The following settings can be made in the code:
- Switch-on time of the street lamps (ms)
- Flickering of the street lamp at pin 6
- Flashing speed of the LEDs of the St. Andrew’s cross
- Minimum (closed) and maximum (open) of the servos
- Switching time of the boom barrier sequences (flashing, closing, opening, flashing)
- Speed of the boom barrier motion
In the following part of the program you can adjust the above parameters:
int streetlightTimeout = 100; // switch-on time of the street lights (ms)
int streetlightFlickering = 1000; // the higher the value, the slower the LED flickers at pin 6
int crossBlinkSpeed = 1000; // blinking speed of the St. Andrew's cross
int servo1Min = 40; // servo 1 minimum (closed)
int servo2Min = 40; // servo 2 minimum (closed)
int servo1Max = 160; // servo 1 maximum (opened)
int servo2Max = 160; // servo 2 maximum (opened)
int boombarrierTimeouts[4] = {1000, 2000, 2000, 1000}; // boom barrier {blink, close, open, blink}
int boombarrierSpeed = 5; // 5 = fast, 25 = medium, 50 = slow
The level crossing module uses the Servo library. This library must be installed first. For this go in the Arduino menu to Sketch>Include Library>Manage Libraries … and search for Servo in the search field. Install the current version of the Servo library from Michael Margois & Arduino.
When uploading you have to make sure that the correct board is selected in the Arduino menu. To do this, “ATmega328P (Old Bootlaoder)” must also be selected in the Processor subitem of the Tools menu.
The following program code can be easily copied with the above mentioned changes and loaded onto the Arduino nano.
Code for the RaylFX Level Crossing Module
#include <Servo.h>
/*
Rayl-FX Level Crossing Module
StartHardware.org/en
Permalink: https://starthardware.org/en/raylfx-level-crossing-with-arduino/
*/
/* ***** ***** Settings ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** ***** */
int streetlightTimeout = 100; // switch-on time of the street lights (ms)
int streetlightFlickering = 1000; // the higher the value, the slower the LED flickers at pin 6
int crossBlinkSpeed = 1000; // blinking speed of the St. Andrew's cross
int servo1Min = 40; // servo 1 minimum (closed)
int servo2Min = 40; // servo 2 minimum (closed)
int servo1Max = 160; // servo 1 maximum (opened)
int servo2Max = 160; // servo 2 maximum (opened)
int boombarrierTimeouts[4] = {1000, 2000, 2000, 1000}; // boom barrier {blink, close, open, blink}
int boombarrierSpeed = 5; // 5 = fast, 25 = medium, 50 = slow
/* ***** ***** From here begins the program code, which does not need to be adjusted ***** ***** ***** ***** */
int streetlightPin1 = 3; // this pin connects to streetlights
int streetlightPin2 = 6; // this pin connects to a flickering streetlights
int crossBlinkPin1 = 5; // this pin connects to the LEDs of the St. Andrew's cross
int crossBlinkPin2 = 7; // this pin connects to the LEDs of the St. Andrew's cross
int boombarrierServoPin1 = 8; // this pin connects to a boom barrier servo
int boombarrierServoPin2 = 9; // this pin connects to a boom barrier servo
int sensorPin1 = 2; // this pin connects to a photoelectric sensor
int sensorPin2 = 4; // this pin connects to a photoelectric sensor
/* Memory Variables */
int streetlightBrightness = 0; // stores how bright the street lights shine
int myState = 0; // stores the current stage of the level crossing: 0 = clear, 1 = blink, 2 = close boom barrier, 3 = boom barrier closed, 4 = open boom barrier
boolean drivesby = false; // stores if there currently is a train driving by
/* Timer Variables */
long streetlightTimer = 0; // timer for the street lights
long myTimer = 0; // timer
long servoTimer = 0; // timer for the servos
/* Servo Objects */
Servo myServo1; // servo object 1
Servo myServo2; // servo Object 2
int myServoPosition[2] = {80, 80}; // stores current servo positions
/* Variables from the control module to determine the time of day */
boolean receive = false;
boolean receiveStarted = false;
int receiveTimeout = 10;
long receiveTimer = 0;
int receivedTime = 0;
int receivePulse = 0;
int lastReceivePulse = 0;
int receivePin = 18;
int myTime = 23;
#define PAYLOAD_SIZE 2 // mandatory for communication with master-module
int timeOfDay = 0; // stores timeOfDay of master-module (0 and 255)
byte nodePayload[PAYLOAD_SIZE]; // temporarily stores data of master-module
void setup() {
Serial.begin(115200); // starts the serial communication
pinMode(receivePin, INPUT); // receiving pin from the control module
/* Servo Variablen */
myServo1.attach(boombarrierServoPin1);
myServo2.attach(boombarrierServoPin2);
myServo1.write(servo1Min);
myServo2.write(servo2Min);
myServoPosition[0] = servo1Min;
myServoPosition[1] = servo2Min;
delay(500); // servos are moved to initial position
pinMode(crossBlinkPin1, OUTPUT);
pinMode(crossBlinkPin2, OUTPUT);
pinMode(sensorPin1, INPUT);
pinMode(sensorPin2, INPUT);
streetlightOff();
}
void loop() {
receiveFunction(); // execute instructions for reception
if (receiveStarted == false) { // if no data is currently being received:
if (myTime > 22) { // ***** late evening *****
streetlightOn(); // turn on street lights
} else if (myTime > 18) { // ***** evening *****
streetlightOn(); // turn on street lights
} else if (myTime > 12) { // ***** noon *****
streetlightOff(); // turn off street lights
} else if (myTime > 9) { // ***** forenoon *****
streetlightOff(); // turn off street lights
} else if (myTime > 7) { // ***** morning *****
streetlightOn(); // turn on street lights
} else { // ***** night *****
streetlightOn(); // turn on street lights
}
if ((digitalRead(sensorPin1) == 0) || (digitalRead(sensorPin2) == 0)) {
if (myState == 0) {
myState = 1;
myTimer = millis();
}
else if ((myState == 4) || (myState == 5)) myState = 2;
} else {
if (myState == 3) {
myState = 4;
myTimer = millis(); // reset timer
}
}
Serial.println(myTime);
switch (myState) {
case 0: // idle
crossOff();
break;
case 1: // blink
crossOn(); // St. Andrew's cross blinking
if (myTimer + boombarrierTimeouts[0] < millis()) { // when time expired
myTimer = millis(); // reset timer
myState = 2; // go on to next state
}
break;
case 2: // close boom barrier
crossOn(); // St. Andrew's cross blinking
closeBoombarriers();
if (myTimer + boombarrierTimeouts[1] < millis()) { // when time expired
myTimer = millis(); // reset timer
myState = 3; // go on to next state
}
break;
case 3: // boom barrier closed
crossOn(); // St. Andrew's cross blinking
break;
case 4: // open boom barrier
crossOn(); // St. Andrew's cross blinking
openBoombarriers();
if (myTimer + boombarrierTimeouts[2] < millis()) { // when time expired
myTimer = millis(); // reset timer
myState = 5; // go on to next state
}
break;
case 5: // blink
crossOn(); // St. Andrew's cross blinking
if (myTimer + boombarrierTimeouts[3] < millis()) { // when time expired
myTimer = millis(); // reset timer
myState = 0; // go on to next state
}
break;
}
}
}
void crossOn() {
if (millis() % crossBlinkSpeed * 2 < crossBlinkSpeed) {
digitalWrite(crossBlinkPin1, LOW);
digitalWrite(crossBlinkPin2, HIGH);
} else {
digitalWrite(crossBlinkPin1, HIGH);
digitalWrite(crossBlinkPin2, LOW);
}
}
void crossOff() {
digitalWrite(crossBlinkPin1, HIGH);
digitalWrite(crossBlinkPin2, HIGH);
}
void closeBoombarriers() {
if (servoTimer + boombarrierSpeed < millis()) {
if (myServoPosition[0] > servo1Min) myServoPosition[0]--;
myServo1.write(myServoPosition[0]);
if (myServoPosition[1] > servo2Min) myServoPosition[1]--;
myServo2.write(myServoPosition[1]);
servoTimer = millis();
}
}
void openBoombarriers() {
if (servoTimer + boombarrierSpeed < millis()) {
if (myServoPosition[0] < servo1Max) myServoPosition[0]++;
myServo1.write(myServoPosition[0]);
if (myServoPosition[1] < servo2Max) myServoPosition[1]++;
myServo2.write(myServoPosition[1]);
servoTimer = millis();
}
}
void streetlightOn() {
if (streetlightTimer + streetlightTimeout < millis()) {
streetlightBrightness++;
if (streetlightBrightness > 255) streetlightBrightness = 255;
streetlightTimer = millis();
analogWrite(streetlightPin1, 255 - streetlightBrightness);
}
if (random(streetlightFlickering) == 1) analogWrite(streetlightPin2, random(255) - streetlightBrightness);
}
void streetlightOff() {
streetlightBrightness = 0;
analogWrite(streetlightPin1, 255);
analogWrite(streetlightPin2, 255);
streetlightTimer = millis();
}
void receiveFunction() { // receives time of day from control-module
receivePulse = digitalRead(receivePin); // read out the receive pin
if ((receiveTimer + receiveTimeout < millis()) && (receiveStarted == true)) {
// on timeout and active reception
receiveStarted = false; // end active reception
myTime = receivedTime - 1; // store received time
receivedTime = 0; // reset the auxiliary variable for time reception
Serial.println(myTime); // serial output
}
// if a pulse is detected at the receive pin that was not there before
if ((receivePulse == 0) && (lastReceivePulse == 1)) {
receiveTimer = millis(); // restart timer
if (receiveStarted == false) receiveStarted = true; // start active reception, if not already done
receivedTime++; // there was a pulse, so increase the auxiliary variable to receive time
}
lastReceivePulse = receivePulse; // remember current state at pin for next pass
}