RaylFX – Level Crossing with Arduino

RaylFX model railway, model making Arduino level crossing with boom barrier and St. Andrew's cross

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

Wiring Diagram

RaylFX model railway, model making Arduino level crossing with boom barrier and St. Andrew's cross 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
}

Here you can find more RaylFX Modules

    Leave a Reply

    Your email address will not be published. Required fields are marked *