Last Updated:03/04/2023
Projects - Isle of Mudd Model Railway Traverser
Projects >> Projects - Isle of Mudd Model Railway Traverser
Isle of Mudd Railway Traverser
This is the second traverser I have built for the fiddle yard of my layout. Like all projects the 2nd version works much better due to lessons learnt in the first version.
as I work in 0-16.5 (7mm narrow gauge) this project will asily transfer over to OO/HO gauges and will work with other gauges although the smaller the gauge the more care will need to be taken in aligning tracks.
For this project to maintain alignment and rigidity I use Linear rails
You will notice I used quite a bit of aluminium angle as the main structural side membesrs but also small gauge angle along both sides of the traverser bridge to prevent any warping.
The base of the bridge is 9mm plywood.
This next photo shows the underside of the traverser. The drive is froom a Nema 17 stepper Motor that is indexed using a photo interrupter
This photo shows the photointerrupter and how it is blocked as the traverser moves across. Once completed I put a cover over the photointerrupter just to neaten it up.
The photointerrupter is very accurate leading to very good track indexing.
Problems
Although it worked perfectly when built the cold weather has caused alignment problems as the cold has caused the drive rod to contract a small amount. Hard to see but it's about 0.5mm out of alignment so I had to update the programming code and add a couple of adjuster buttons so that I can update the settings as temperatures change.
As can be seen in the image they sit under the baseboard so that they cannot be accidentaly knocked. I used capacitive touch switches as they have no moving parts to go wrong and are low profile.
Board Control
The electronics side is fairly simple.An Arduino UNO is used as a DCC decoder using the circuit found at https://www.digitaltown.co.uk/79DCCDecoderCircuit.php
This through the NMRA DCC library controls the stepper motor with each track being a seperate address.
I use DCC-EX as a DCC bas station linked to my own touch panel controllers for track control but because it is NMRA DCC compliant it will run just as happily from my old NCE PowerCab or any other DCC control system.
Current Code : fiddleYardTraverserV4.ino
Click to Download code: fiddleYardTraverserV4.ino
/* fiddleYardTraverserV4
24/11/23
steppers rest after 1 minute of no movement
baud rate changed
Adjuster switches added on pins 6 & 7
18/10/23
Addresses change to 11-15
05/10/2023
This version adds the DCC control
DCC addresses are Acc numbers 11 - 15 left or right will trigger the move.
Nema 17 stepper Motor
Photo interrupter
pin 2 connected to minabay optoisolator set up to get DCC signal
Seems to work at about 100 steps per mm using 1/4 steps
Pins Used:
A0 Photointerrupter
2 dcc pin
6 adjuster pin
7 adjuster pin
8 enable pin
9 Step Pin (stepper)
10 Direction Pin (stepper)
*/
//EEPROM
//system will use built in eeprom as it should not be written to very much...limited lifecycle.
#include < EEPROM.h >
const byte eepromCheck = 11;//check value
unsigned long eepromDelay = 20000;
unsigned long eepromTimer;
unsigned long currentMillis;
unsigned long lastStepperMoveTimer;
unsigned long stepperRestTime = 60000;//1 minute
byte eepromUpdateFlag;
//stored for emergency
int defaulttrackPositions[5][2] = { //these are the defaults in case eeprom error or Arduino has been swapped.
{11, 900}, //acc number step offset
{12, 6300},
{13, 11700},
{14, 17100},
{15, 22500}
};
//5400 step intervals
int trackPositions[5][2] = { //these are the defaults in case eeprom error or Arduino has been swapped.
{11, 900}, //acc number step offset
{12, 6300},
{13, 11700},
{14, 17100},
{15, 22500}
};
//adjusters
const int adjustPlusPin = 6;
const int adjustMinusPin = 7;
//Stepper Motor... 800 steps per revolution
const int enablePin = 8; //taken high to disable stepper
const int stepPin = 9; // change tp 3 for your set up
const int dirPin = 10; //change t 4 for your set up
const int photoInterrupterPin = 0;
int testArrayPos = 1;
byte lastStep;
int travCounter = 0; //used to count the number of movements to reset auto homing
int travReindexCounter = 20; // Number of movements before reindex, if losing index lower number
const byte travForwards = 1;
const byte travBackwards = 0;
const byte stepperPowerOn = 0;
const byte stepperPowerOff = 1;
//int stepDelay = 100;
int stepDelay = 120;
long stepsTarget; //Target postion of stepper motor
long stepperPosition = 100;
int stepperArrayPos; //position in the array
byte indexFound = 0; //1 = found...zero searching
//NMRA DCC library
#include < NmraDcc.h >
NmraDcc Dcc ;
DCC_MSG Packet ;
//function used for controlling points
// This function is called whenever a normal DCC Turnout Packet is received and we're in Output Addressing Mode
void notifyDccAccTurnoutOutput( uint16_t Addr, uint8_t Direction, uint8_t OutputPower)
{
int q;
//Print out the DCC command to make sure it's working
Serial.print("notifyDccAccTurnoutOutput: ") ;
Serial.print(Addr, DEC) ;
Serial.print(',');
Serial.print(Direction, DEC) ;
Serial.print(',');
Serial.println(OutputPower, HEX) ;
switch (Addr) {
case 11://front track..nearest photo interrupter
indexFound = 0;//force system to reindex everytime the nearest track is selected
stepperArrayPos = 0;
moveToPosition(trackPositions[0][1]);
break;
case 12:
stepperArrayPos = 1;
moveToPosition(trackPositions[1][1]);
break;
case 13:
stepperArrayPos = 2;
moveToPosition(trackPositions[2][1]);
break;
case 14:
stepperArrayPos = 3;
moveToPosition(trackPositions[3][1]);
break;
stepperArrayPos = 4;
case 15://rear track
moveToPosition(trackPositions[4][1]);
break;
default://not for this device
break;
}
}
//Moves the stepper to the selected position.
//will auto index if not already indexed.
void moveToPosition(int trackPos) {
Serial.print("Target: ");
Serial.println(trackPos);
int q;
int travelDirection;
int stepsToMove;
travCounter++;
if (travCounter >= travReindexCounter) {
travCounter = 0;
indexFound = 0;
Serial.print("Re Index");
}
if (indexFound < 1) {
findIndex();//finds the index point
}
digitalWrite(enablePin, stepperPowerOn); //Enable stepper driver
if (stepperPosition > trackPos) {
digitalWrite(dirPin, travForwards);//move towards photointerrupter
travelDirection = travForwards;
stepsToMove = stepperPosition - trackPos;
} else {
digitalWrite(dirPin, travBackwards);//move away from photointerrupter
travelDirection = travBackwards;
stepsToMove = trackPos - stepperPosition;
}
Serial.print("Current Steps: ");
Serial.println(stepperPosition);
Serial.print("Target Steps: ");
Serial.println(trackPos);
Serial.print("stepper to move: ");
Serial.println(stepsToMove);
for (int q = 0; q < stepsToMove; q++) {
digitalWrite(stepPin, HIGH);
delayMicroseconds(stepDelay);
digitalWrite(stepPin, LOW);
delayMicroseconds(stepDelay);
}
stepperPosition = trackPos;
//digitalWrite(enablePin, stepperPowerOff); //disable stepper driver
lastStepperMoveTimer = currentMillis;
Serial.println(stepperPosition);
Serial.println("stepperArrayPos: " + String(stepperArrayPos));
}
void checkInterrupter() {
int photointerrupterState = 0;
photointerrupterState = analogRead(photoInterrupterPin);//Read photointerrupter
//Serial.println(photointerrupterState);
if (photointerrupterState < 100) {
indexFound = 1;
} else {
indexFound = 0;
}
}
//finds the index point
void findIndex() {
digitalWrite(enablePin, stepperPowerOn); //Enable stepper driver
checkInterrupter();
if (indexFound > 0) { //if the system starts with the interrupter blocked
indexFound = 0;//restest as traverser will move away 1st
Serial.println("initial move away from photointerrupter");
digitalWrite(dirPin, travBackwards);//move backwards away from the photointerrupter
//for (int q = 0; q < 21700; q++) {//move 3 rotations
for (int q = 0; q < 4000; q++) {//move away 4000 steps
digitalWrite(stepPin, HIGH);
delayMicroseconds(stepDelay);
digitalWrite(stepPin, LOW);
delayMicroseconds(stepDelay);
}
}
Serial.println("moving towards photointerrupter");//towards photointerrupter is forwards?
do {
digitalWrite(dirPin, travForwards);//move towards photointerrupter
digitalWrite(stepPin, HIGH);
checkInterrupter();
delayMicroseconds(stepDelay);
if (indexFound < 1) {
digitalWrite(stepPin, LOW);
checkInterrupter();
delayMicroseconds(stepDelay);
}
}
while (indexFound < 1); //keep moving forward until index is set
stepperPosition = 1000;//a1lows stepper to pass beyond photointerupter
Serial.println("Indexed :-)");
digitalWrite(enablePin, stepperPowerOff); //Enable stepper driver
Serial.println(stepperPosition);
delay(500);
}
//deals with adjuster switches
void adjusterSwitches(){
if (indexFound > 0) {//only runs when system has been indexed
if (digitalRead(adjustMinusPin) > 0) {
digitalWrite(enablePin, stepperPowerOn);
digitalWrite(dirPin, travForwards);//move towards photointerrupter
digitalWrite(stepPin, HIGH);
delayMicroseconds(stepDelay * 20);//slow speed
digitalWrite(stepPin, LOW);
delayMicroseconds(stepDelay * 20);
stepperPosition--;//update stepper position
trackPositions[stepperArrayPos][1]--;//update trackPositionArray
eepromUpdateFlag = 1;//set flag to update eeprom
eepromTimer = currentMillis;
lastStepperMoveTimer = currentMillis;
Serial.println(trackPositions[stepperArrayPos][1]);
}
if (digitalRead(adjustPlusPin) > 0) {
digitalWrite(enablePin, stepperPowerOn);
digitalWrite(dirPin, travBackwards);//move towards photointerrupter
digitalWrite(stepPin, HIGH);
delayMicroseconds(stepDelay * 20);//slow speed
digitalWrite(stepPin, LOW);
delayMicroseconds(stepDelay * 20);
stepperPosition++;//update stepper position
trackPositions[stepperArrayPos][1]++;//update trackPositionArray
eepromUpdateFlag = 1;//set flag to update eeprom
eepromTimer = currentMillis;
lastStepperMoveTimer = currentMillis;
Serial.println(trackPositions[stepperArrayPos][1]);
}
}
}
//gets the data from EEPROM
void getEEPROMData() {
int q;
byte testEEPROM = EEPROM.read(0); //this should have a value of 16
Serial.println("Read EEPROM");
Serial.println("EEEPROM TEST: " + String(testEEPROM));
if (testEEPROM != eepromCheck) {//check valid eeprom in case Arduino has been changed
Serial.println("EEPROM Fail...writing new EEPROM data");
updateEEPROM();//will write default addresses from original positionArray[q]
} else { //good set so read away
Serial.println("Good EEPROM data");
for (q = 0; q < 5; q++) {
trackPositions[q][1] = EEPROMReadint((q + 1) * 10);
}
for (q = 0; q < 5; q++) {
Serial.println(trackPositions[q][1]);
}
}
}
//writes the corrected data set to EEPROM
void updateEEPROM() {
int q;
EEPROM.write(0, eepromCheck); //set up marker
for (q = 0; q < 5; q++) {
EEPROMWritelong((q + 1) * 10, trackPositions[q][1]);
}
Serial.println("EE");
}
int EEPROMReadint(long address){
int two = EEPROM.read(address);
int one = EEPROM.read(address + 1);
return two + (one * 256);
}
void EEPROMWritelong(int address, int value) {
byte two = (value & 255);
byte one = ((value >> 8) & 255);
EEPROM.update(address, two);
EEPROM.update(address + 1, one);
}
void eepromCheckForUpdate(){
if(eepromUpdateFlag > 0){
if(currentMillis - eepromTimer >= eepromDelay){
updateEEPROM();
eepromUpdateFlag = 0;
}
}
}
void setup() {
Serial.begin(115200);
Serial.println("fiddleYardTraverserV4");
pinMode(stepPin, OUTPUT);
pinMode(dirPin, OUTPUT);
pinMode(adjustMinusPin, INPUT);
pinMode(adjustPlusPin, INPUT);
//DCC
// Setup which External Interrupt, the Pin it's associated with that we're using and enable the Pull-Up
Dcc.pin(0, 2, 1);
// Call the main DCC Init function to enable the DCC Receiver
Dcc.init( MAN_ID_DIY, 10, CV29_ACCESSORY_DECODER | CV29_OUTPUT_ADDRESS_MODE, 0 );
Serial.println("DCC Init Done");
getEEPROMData();//read the info from EEPROM
}
void loop() {
Dcc.process();//checks for DCC connads being received
adjusterSwitches();
currentMillis = millis();
eepromCheckForUpdate();
restStepper();
}
//turns steppers off after an interval....talking at exhibition
void restStepper(){
if(currentMillis - lastStepperMoveTimer >= stepperRestTime){
digitalWrite(enablePin, stepperPowerOff);
Serial.println("Step rest");
}
}
Additional Resource Links
Arduino Indexing DCC Model Railway Traverser 20/03/23
DCC Modular Layouts and Control Panels 02/02/2022
DCC Accessory Decoder, sounds and lights Arduino UNO 12/10/2021
Comments
This site has been designed to be child friendly, this means that comments cannot be added to videos or directly to the site. To add a comment or ask a question please email the address in this image: and use Projects - Isle of Mudd Model Railway Traverser as a reference.