February 1, 2025
Description
Micro-controller: Any Arduino compatible micro-controller will work. The IR train sensors can be powered by 3.3V DC, but have a longer detection range when powered at 5V DC. The XIAO SAMD21 micro-controller used in this project is powered by 5V DC but uses 3.3V logic and analog inputs/outputs. This requires a 5V to 3.3V logic level shifter to protect the XIAO. Thus, in this project there are two high voltage levels present: 5V DC is indicated by red wires and 3.3V DC by orange.
-------------------------------------------------------------------------------------------------------------
IR Sensors: Use 3-wire IR reflective photoelectric sensor modules. Power these with 5V DC for longer detection range. The sensors have two outputs: digital (DO) and analog (AO). The code below uses the digital output. The trigger level for the digital output is set using a screw driver to turn the potentiometer on the back side of the circuit board. Use two sensors to keep the signal operating until the tail of the train has passed the crossing. Ideally these sensors should be mounted below the track, but they are too thick. Instead, mount them in rocks, trees, or out buildings along the track.
Logic Level Shifter: Us this to shift the 5V output of the IR sensors to a level compatible with the micro-controller. This is not required if the micro-controller accepts 5V signals.
Crossing Signal: Print with back on the print bed and no supports. Add filament change for accents.
Signal LEDs: Use diffuse (not clear) 10 mm red LED's. Press fit into signal. Wire in parallel with opposite polarities (long LED pin is + and short LED pin is -), and in series with a current limiting resistor (~220 ohms). In this project the LED leads were oriented vertically (long lead on top for one LED and on the bottom for the other LED). The short leads were bent horizontally and each one was soldered to its corresponding long LED. The long leads were adjusted horizontally so their spacing was inside the servo motor cover, then soldered to jumper wires. Power with the digital outputs of the micro-controller.
Signal Servo: Use a SG90 servo to raise and lower the arm. The micro-controller can easily source the voltage necessary to set the position of the servo, but may not be able to source enough current to turn servo motor. This project uses a USB type C serial breakout board to power the motor at 5V DC. A 2-in-1 USB C Splitter Cable can power both the USB breakout board and the micro-controller from a single USB wall charger adapter. Place the servo motor inside its shield with the leads pointing downward. Insert in the signal and mount using two #2 x ½" sheet metal screws. Snap the cover on the servo motor shield. Use the single crank that comes with the servo to connect the signal arm to the servo motor. You may have to cut/sand the front side of the crank to make it flat. Mount the arm using a piece of double-sided tape sandwiched between the arm and crank lever (this connection needs to break easily in case the servo spins too far) and a #2 x 3/8" sheet metal screw. You might need to ream out the holes for the screws: #43 size bit for the hole in the crossing arm and #48 size bit for the hole in the front cover of the servo box.
Signal Bell: Use a DFPlayer Mini with 8 ohm speaker. Download mp3 or wav audio files onto a micro SD card and insert into DFPlayer Mini. Power the DFPlayer with 5V DC from the USB breakout board. You can vary the delay in code to synchronize the lights and the bell.
Level Crossing: Designed to fit Bachmann EZ Track. Print two of Part 1 on their side with supports everywhere. This makes it easy to add stripes on roadbed using filament changes. Part 2 is used to connect these to a piece of straight track. You will need to notch the edges of the track. I found that a table saw works best. Set the blade depth to 5 mm. Part 2 presses into the underside of the track. Friction will hold it in place. Use #2 x 3/8" sheet metal screws to fasten the level crossings to Part 2. Then mount the crossing signals using screws or double-sided tape.
Micro-controller: Any Arduino-compatible micro-controller is fine. I used the XIAO SAMD21 from Seeed Studio (see photos), because it is small and inexpensive. The SAMD21 also has a USB type C connector. Here is the code:
/* Railroad crossing with lights, gate, and bell
by Everett Ramer 1/24/2023
*/
/* Railroad crossing bell
by https://hackaday.io/project/194056-digital-bicycle-horn-with-customizable-sound/details
*/
/* Railroad crossing lights
by DAVID https://projecthub.arduino.cc/eldo85/railroad-crossing-lights-3c1252
*/
/* Sweep
by BARRAGAN <http://barraganstudio.com>
This example code is in the public domain.
modified 8 Nov 2013
by Scott Fitzgerald
https://www.arduino.cc/en/Tutorial/LibraryExamples/Sweep
*/
#include <Servo.h>
Servo myservo; // create servo object to control a servo
// twelve servo objects can be created on most boards
#include <SoftwareSerial.h>
#define RX D6 // RX pin of DFPlayer Mini connected to digital pin 6
#define TX D7 // TX pin of DFPlayer Mini connected to digital pin 7
SoftwareSerial mySoftwareSerial(RX, TX); // Create a SoftwareSerial object
#include <DFRobotDFPlayerMini.h>
DFRobotDFPlayerMini myDFPlayer; // Create a DFPlayerMini object
// set pin numbers
const int ledPin1 = 1;
const int ledPin2 = 2;
const int sensorPin1 = 3;
const int sensorPin2 = 4;
const int armPin = 5;
bool sensorState1 = false;
bool sensorState2 = false;
bool signalOn = false;
void setup() {
pinMode(ledPin1, OUTPUT); // initialize the LED pin as an output:
pinMode(ledPin2, OUTPUT);
pinMode(sensorPin1, INPUT); // initialize the sensor pin as an input:
pinMode(sensorPin2, INPUT);
myservo.attach(armPin); // attaches the servo to the servo object
myservo.write(0); //make sure the gate is raised
mySoftwareSerial.begin(9600); // Start the software serial communication
myDFPlayer.begin(mySoftwareSerial); // Initialize DFPlayer Mini
myDFPlayer.volume(30); // Set volume (0 to 30)
}
void loop() {
sensorState1 = digitalRead(sensorPin1) == LOW;
sensorState2 = digitalRead(sensorPin2) == LOW;
// lower gate and flash leds while either sensor is LOW
if (sensorState1 || sensorState2) {
if (!signalOn) {
signalOn = true;
myDFPlayer.play(1); // Function to play song 1
myservo.write(90); //lower gate
}
digitalWrite(ledPin1, HIGH); //red1 LED on
digitalWrite(ledPin2, LOW); //red2 LED off
delay(400); //wait 400ms
digitalWrite(ledPin1, LOW); //red1 LED off
digitalWrite(ledPin2, HIGH); //red2 LED on
delay(400); //wait 400ms
} else {
delay(400); // allow for gaps between cars
sensorState1 = digitalRead(sensorPin1) == HIGH;
sensorState2 = digitalRead(sensorPin2) == HIGH;
// raise gate and shut off leds if both sensors are HIGH
if (sensorState1 && sensorState2 && signalOn) {
signalOn = false;
myDFPlayer.stop();
myservo.write(0);
digitalWrite(ledPin1, LOW); //red1 LED off
digitalWrite(ledPin2, LOW); //red2 LED off
}
}
}
License:
Creative Commons — Attribution