In my last post, I described the operation of my Automated Block Signalling ("ABS") on my model railroad. In this post, I shall describe my method of building my own signal towers and I will share the complete Arduino code which runs the system.
I knew I was going to need a considerable number of operating signal towers, 10 for the upper level, 20 for the middle level (because it is double-track) and a few more for the lowest level. Searching online turned up a handful of vendors that sell operating signal towers and even fewer that sell the modern "Darth Vader" style which is becoming commonplace in the US and is being installed over time by Canadian Pacific which I model. Vendors typically have a small number in stock and pricing per-tower runs between $40 and $70 (Canadian dollars per head). I decided that I would set to work building my own.
To build the signal "target" (the black contraption which contains the green, yellow and red signal lights) I used two thicknesses of sheet styrene 0.02 inches (for the flat shield which surrounds the lights) and 0.01 inches for the "Darth Vader" shroud.
I shall use pictures to show the steps I follow to make each signal head. I built a jig to make sure all of the parts are identical and to speed up production.
I built several other jigs with which to make the signal tower. They are all mounted on one board (please note that there are some aspects of this board which are designed to build two-headed signal towers which will be the subject of a future blog post).
int Sensor = 10; //Sensor is setup on Pin 10
int GreenLeft = 2; //Green Signal Facing Left on Pin 2 (used to be Green1)
int YellowLeft = 3;
int RedLeft = 4;
int GreenRight = 5; //Green Signal Facing Right on Pin 5 (used to be Green2)
int YellowRight = 6;
int RedRight = 7;//Sets up all of our separate signals on their pins
int BlockLeftSend = 8;//Sets up signal to be sent to previous block (used to be BlockASend)
int BlockRightSend = 9 ;//Sets up Signal to be sent to next block (used to be BlockBSend)
int BlockLeftRec = A0; //Sets up A0 as pin for receiving signal from previous block (used to be BlockARec)
int BlockRightRec = A1; //Sets up A1 as pin for receiving signal from next block(used to be BlockBRec)
int valA1 = 0;
int valA2 = 0;
int valA3 = 0;
int clearcount;
void setup() {
Serial.begin(9600);
//Establish all of our pins as inputs or outputs
pinMode(Sensor, INPUT);
pinMode(GreenLeft, OUTPUT);
pinMode(YellowLeft, OUTPUT);
pinMode(RedLeft, OUTPUT);
pinMode(GreenRight, OUTPUT);
pinMode(YellowRight, OUTPUT);
pinMode(RedRight, OUTPUT);
pinMode(BlockLeftSend, OUTPUT);
pinMode(BlockRightSend, OUTPUT);
pinMode(BlockLeftRec, INPUT);
pinMode(BlockRightRec, INPUT);
}
enum SIGNALSTATES
{
ST_GG,
ST_GY,
ST_YG,
ST_YY,
ST_RR,
ST_YY1,
};//Sets up different signal states to switch to
SIGNALSTATES signalState = ST_GG;//GG is the default signal state
void loop() {
valA1 = digitalRead(Sensor);//Reads Sensor
valA2 = analogRead(BlockLeftRec);//Reads Block to left
valA3 = analogRead(BlockRightRec);//Reads Bock to right
delay(200);
Serial.println("");
Serial.print("valA1= ");
Serial.println(valA1);
Serial.print("valA2= ");
Serial.println(valA2);
Serial.print("valA3= ");
Serial.println(valA3); //Allows for values to be read on Serial Monitor
delay(1);
switch (signalState)
{
case ST_GG:
signalgg(valA1, valA2, valA3);
break;
case ST_GY:
signalgy(valA1, valA2, valA3);
break;
case ST_YG:
signalyg(valA1, valA2, valA3);
break;
case ST_YY:
signalyy(valA1, valA2, valA3);
break;
case ST_RR:
signalrr(valA1, valA2, valA3);
break;
case ST_YY1:
signalyy1(valA1, valA2, valA3);
break;
//This sets up our different loops within the main loop to switch between signal states
}
}
void signalgg(int valA1, int valA2, int valA3) {
digitalWrite(GreenLeft, LOW); //Turns Green facing Left On
digitalWrite(YellowLeft, HIGH);
digitalWrite(RedLeft, HIGH);
digitalWrite(GreenRight, LOW); //Turns Green facing Right On
digitalWrite(YellowRight, HIGH);
digitalWrite(RedRight, HIGH);
digitalWrite(BlockLeftSend, LOW); //No signal from this block
digitalWrite(BlockRightSend, LOW); //No signal from this block
Serial.println("No Train");//Allows text to be seen in serial monitor
delay(1);
clearcount = 0;
if ((valA1 > 0) && (valA2 < 750) && (valA3 > 750)) {
signalState = ST_GY;//Block clear but Block to Right occupied
}
if ((valA1 > 0) && (valA2 > 750) && (valA3 < 750)) {
signalState = ST_YG;//Block clear but Block to Right occupied
}
if ((valA1 > 0) && (valA2 > 750) && (valA3 > 750)) {
signalState = ST_YY;//Block clear but Blocks to Left and Right are occupied
}
if (valA1 < 1) {
signalState = ST_RR;//Block occupied
}
}
void signalgy(int valA1, int valA2, int valA3) {
digitalWrite(GreenLeft, LOW); //Turns Green facing Left On
digitalWrite(YellowLeft, HIGH);
digitalWrite(RedLeft, HIGH);
digitalWrite(GreenRight, HIGH);
digitalWrite(YellowRight, LOW);// Turns Yellow facing Righ On
digitalWrite(RedRight, HIGH);
digitalWrite(BlockLeftSend, LOW); //No signal from this block
digitalWrite(BlockRightSend, LOW); //No signal from this block
Serial.println("Approach with Caution");//Allows text to be seen in serial monitor
delay(1);
if ((valA1 > 0) && (valA2 < 750) && (valA3 < 750)) {
signalState = ST_GG;//Block clear
}
if ((valA1 > 0) && (valA2 > 750) && (valA3 < 750)) {
signalState = ST_YG;//Block clear but Block to Right occupied
}
if ((valA1 > 0) && (valA2 > 750) && (valA3 > 750)) {
signalState = ST_YY;//Block clear but Blocks to Left and Right are occupied
}
if (valA1 < 1) {
signalState = ST_RR;//Block occupied
}
}
void signalyg(int valA1, int valA2, int valA3) {
digitalWrite(GreenLeft, HIGH);
digitalWrite(YellowLeft, LOW);//Turns Yellow facing Left On
digitalWrite(RedLeft, HIGH);
digitalWrite(GreenRight, LOW); //Turns Green facing Right On
digitalWrite(YellowRight, HIGH);
digitalWrite(RedRight, HIGH);
digitalWrite(BlockLeftSend, LOW); //No signal from this block
digitalWrite(BlockRightSend, LOW); //No signal from this block
Serial.println("No Train Ahead");//Allows text to be seen in serial monitor
delay(1);
if ((valA1 > 0) && (valA2 < 750) && (valA3 > 750)) {
signalState = ST_GY;//Block clear but Block to Left occupied
}
if ((valA1 > 0) && (valA2 < 750) && (valA3 < 750)) {
signalState = ST_GG;//Block clear
}
if ((valA1 > 0) && (valA2 > 750) && (valA3 > 750)) {
signalState = ST_YY;//Block clear but Blocks to Left and Right are occupied
}
if (valA1 < 1) {
signalState = ST_RR;//Block occupied
}
}
void signalyy(int valA1, int valA2, int valA3) {
digitalWrite(GreenLeft, HIGH);
digitalWrite(YellowLeft, LOW);//Turns Yellow facing Left On
digitalWrite(RedLeft, HIGH);
digitalWrite(GreenRight, HIGH);
digitalWrite(YellowRight, LOW);//Turns Yellow facing Right On
digitalWrite(RedRight, HIGH);
digitalWrite(BlockLeftSend, LOW); //No signal from this block
digitalWrite(BlockRightSend, LOW); //No signal from this block
Serial.println("No Train");//Allows text to be seen in serial monitor
delay(1);
if ((valA1 > 0) && (valA2 < 750) && (valA3 > 750)) {
signalState = ST_GY;//Block clear but Block to Left occupied
}
if ((valA1 > 0) && (valA2 > 750) && (valA3 < 750)) {
signalState = ST_YG;//Block clear but Block to Right occupied
}
if ((valA1 > 0) && (valA2 < 750) && (valA3 < 750)) {
signalState = ST_GG;//Block clear but Blocks to Left and Right are occupied
}
if (valA1 < 1) {
signalState = ST_RR;//Block occupied
}
}
void signalrr(int valA1, int valA2, int valA3) {
digitalWrite(GreenLeft, HIGH);
digitalWrite(YellowLeft, HIGH);
digitalWrite(RedLeft, LOW);//Turns Red facing Left On
digitalWrite(GreenRight, HIGH);
digitalWrite(YellowRight, HIGH);
digitalWrite(RedRight, LOW);//Turns Red facing Right On
digitalWrite(BlockLeftSend, HIGH); //signal from this block
digitalWrite(BlockRightSend, HIGH); //signal from this block
Serial.println("Block is Occupied");//Allows text to be seen in serial monitor
delay(1);
if (valA1 > 0) {
signalState = ST_YY1; //switches over to the transition yellow signal state
}
}
void signalyy1(int valA1, int valA2, int valA3) {
digitalWrite(GreenLeft, HIGH);
digitalWrite(YellowLeft, LOW);//Turns Yellow facing Left On
digitalWrite(RedLeft, HIGH);
digitalWrite(GreenRight, HIGH);
digitalWrite(YellowRight, LOW);//Turns Yellow facing Right On
digitalWrite(RedRight, HIGH);
digitalWrite(BlockLeftSend, HIGH); //signal from this block
digitalWrite(BlockRightSend, HIGH); //signal from this block
Serial.println("Train just left block");//Allows text to be seen in serial monitor
delay(1);
if ((valA1 > 0) && (clearcount < 5)) {
clearcount++;
}
else if ((valA1 < 1) && (clearcount < 5)) {
clearcount = 0; //checks for false positives of clear track
}
else if ((valA1 > 0) && (clearcount > 4)) {
signalState = ST_YY;//goes to signal state YY where occupancy status is checked
}
}