Stage 6 - Putting it all Together
Stage 6 of our RMFT layout is a simple loop including a single siding for a station, which allows for automated running of up to three trains including automated switching for entering and exiting the station siding.
To accomplish this, the layout will be broken up into four virtual blocks with five sensors in use to detect the position of our trains as they enter and exit the blocks, and three signals that will be controlled by automation to indicate when it is safe to proceed.
Below, we’ll cover off the various aspects required to get up and running with stage 1 including object definitions, the various hardware options you can use, and how you can apply automation techniques to the layout.
What to expect to learn from stage 6
At the end of this stage, we expect you will have learnt the following:
How to define the different types of turnout/point and signal objects.
How to reference sensors.
How, why, and when to effectively use aliases.
How an object ID is different to the pin or configuration used by the physical object it represents.
How to enable layout automation while still manually controlling the trains.
How to enable a fully automated layout.
What hardware can be used, and how to connect the components (Fritzing diagrams for this will be published soon).
Aliases
As mentioned already, we will be defining aliases throughout these pages to put human-friendly labels on our various objects.
By doing so, it becomes easier to refer to things in various different sequences or contexts when referring to a name than it is to remember a specific pin or turnout/point ID.
This also means if there is a need to change a pin or object ID, you can simply update the single alias reference, meaning you don’t need to dig through all your sequences, routes, and so forth to edit them individually.
Further more, you can make radical changes such as moving from pin turnouts/points to servo turnouts/points, and only need to edit the defined objects and alias. Again, all your existing sequences, routes, etc. should remain unchanged.
For more information on aliases, refer to Aliases.
Turnouts/Points
Two turnouts/points are used in this first stage of our RMFT layout to allow trains to enter and exit the station siding, or continue along the main track.
For further reading on turnouts/points, you can refer to the Turnout/Point Objects - Definition and Control section of the EX-RAIL reference and Turnouts/Points (Configuring the EX-CommandStation) in the DCC-EX Command reference.
Turnout/Point definitions
We will define turnout/point 1 with an ID of 100, and turnout/point 2 with an ID of 101.
DCC accessory turnouts/points
To define these as DCC accessory turnouts/points, turnout/points 1 will be at linear address 101, and turnout/points 2 at 102. These translate to address 26 with sub address 0 for linear address 101, and address 26 with sub address 1 for linear address 102.
Therefore, the DCC-EX command to define these in the serial console is as follows:
<T 100 DCC 26 0>
<T 101 DCC 26 1>
In EX-RAIL, we would add these lines to myAutomation.h, with aliases defined:
ALIAS(TRN1, 100)
ALIAS(TRN2, 101)
TURNOUT(TRN1, 26, 0, "Station entry")
TURNOUT(TRN2, 26, 1, "Station exit")
Pin turnouts/Points
To define these same turnout/point IDs as pin turnouts/points instead, and using I/O pins that are directly on our EX‑CommandStation Mega2560, we will use pins 22 and 23.
To define these in the serial console:
<T 100 VPIN 22>
<T 101 VPIN 23>
In EX-RAIL, we would add these lines to myAutomation.h:
ALIAS(TRN1, 100)
ALIAS(TRN2, 101)
PIN_TURNOUT(TRN1, 22, "Station entry")
PIN_TURNOUT(TRN2, 23, "Station exit")
If we were instead to use an MCP23017 I/O expander, we would use Vpins instead of direct pins on the Mega2560, and we would start these at the first I/O expander’s 164 Vpin ID.
To define these in the serial console:
<T 100 VPIN 164>
<T 101 VPIN 165>
And again, in myAutomation.h for EX-RAIL:
ALIAS(TRN1, 100)
ALIAS(TRN2, 101)
PIN_TURNOUT(TRN1, 164, "Station entry")
PIN_TURNOUT(TRN2, 165, "Station exit")
Servo turnouts/points
Finally, to define these same turnouts/points as servo based turnouts/points, these would be connected to a PCA9685 servo module, and our first module starts at Vpin ID 100.
Tip
Remember! Servo angles will be unique to your layout, and probably even unique to individual turnouts/points, so be sure you read the blurb on Tuning servo positions and the Connecting a Servo Module page.
Please don’t blindly copy/paste the servo angles listed here and expect them to “just work”.
Throughout these pages, we will assume that the thrown servo position is 400, the closed servo position is 100, and we will use the “Slow” profile.
Note
You will note below that our turnout ID 100 matches the first PCA9685 module’s first Vpin which happens to also be 100. While these numbers are the same, they do not represent the same item.
You can see within this section on defining the turnouts/points that a turnout/point with an object ID of 100 could represent any variation of turnout object, with any support hardware implementation.
Defining these in the serial console therefore would be:
<T 100 SERVO 100 400 100 3>
<T 101 SERVO 101 400 100 3>
Again, in myAutomation.h this becomes:
ALIAS(TRN1, 100)
ALIAS(TRN2, 101)
SERVO_TURNOUT(TRN1, 100, 400, 100, Slow, "Station entry")
SERVO_TURNOUT(TRN2, 101, 400, 100, Slow, "Station exit")
Sensors
Five sensors are used in this first stage, which allows us to have up to three trains controlled by EX-RAIL automation. The sensors are placed within each virtual block to ensure we know when the front of the train enters a block, and when the rear of the train has exited a block.
We don’t need to explicitly define any sensor objects to work with EX-RAIL, so we will simply map these by defining EX-RAIL aliases.
To use pins directly on our Mega2560, we would start at pin 24 (we used pins 22/23 for turnouts):
ALIAS(SNS1_TRN1_APP, 24) // Sensor 1, approaching turnout/point 1
ALIAS(SNS2_MAIN_TRN1_EX, 25) // Sensor 2, on the main track exiting turnout/point 1
ALIAS(SNS3_STN, 26) // Sensor 3, at our station stop
ALIAS(SNS4_MAIN_TRN2_APP, 27) // Sensor 4, on the main track approaching turnout/point 2
ALIAS(SNS5_STN_TRN2_APP, 28) // Sensor 5, on the station siding approaching turnout/point 2
Moving these to our first MCP23017 I/O expander, these would start at Vpin 166 (we used Vpins 164/165 for turnouts/points):
ALIAS(SNS1_TRN1_APP, 166) // Sensor 1, approaching turnout/point 1
ALIAS(SNS2_MAIN_TRN1_EX, 167) // Sensor 2, on the main track exiting turnout/point 1
ALIAS(SNS3_STN, 168) // Sensor 3, at our station stop
ALIAS(SNS4_MAIN_TRN2_APP, 169) // Sensor 4, on the main track approaching turnout/point 2
ALIAS(SNS5_STN_TRN2_APP, 170) // Sensor 5, on the station siding approaching turnout/point 2
Signals
Three signals have been used in this first stage to indicate whether or not a train can enter either the station siding or proceed beyond turnout/point 1 on the main track, to indicate whether a train can exit the station siding, or if a train can proceed beyond turnout/point 2 on the main track.
Pin based signals
To use pin based signals, we require three pins per signal, and therefore nine pins in total, but we will only define an alias for the red pin given that it is the “control” pin for each signal. The other pins are used in the background by DCC-EX and are not referenced anywhere else outside the object definition.
To define pin based signals directly on the Mega2560 with aliases for the control pins:
ALIAS(SIG1_TRN1_APP, 30) // Signal 1, approaching turnout/point 1
ALIAS(SIG2_TRN2_GO, 33) // Signal 2, proceed beyond turnout/point 2
ALIAS(SIG3_STN_EX, 36) // Signal 3, exit the station siding
SIGNAL(SIG1_TRN1_APP, 31, 32)
SIGNAL(SIG2_TRN2_GO, 34, 35)
SIGNAL(SIG3_STN_EX, 37, 38)
Moving these again to an MCP23017 I/O expander, these would start at Vpin 172, however this also overlaps to a second I/O expander by one pin:
ALIAS(SIG1_TRN1_APP, 172) // Signal 1, approaching turnout/point 1
ALIAS(SIG2_TRN2_GO, 175) // Signal 2, proceed beyond turnout/point 2
ALIAS(SIG3_STN_EX, 178) // Signal 3, exit the station siding
SIGNAL(SIG1_TRN1_APP, 173, 174)
SIGNAL(SIG2_TRN2_GO, 176, 177)
SIGNAL(SIG3_STN_EX, 179, 180)
Servo based signals
To define servo based signals, these only require one Vpin per signal along with specifying the servo angle for the red, amber, and green positions.
Tip
Remember! Servo angles will be unique to your layout, and probably even unique to individual signals, so be sure you read the blurb on Tuning servo positions and the Connecting a Servo Module page.
Please don’t blindly copy/paste the servo angles listed here and expect them to “just work”.
Allowing for servo based turnouts/points being used, we will start our signals from the third available Vpin on our PCA9685 servo module (we used the first two for servo turnouts/points). We will make the assumption that red requires a servo angle of 100, amber 250, and green 400:
Virtual blocks
We’ve divided the layout into four virtual blocks, allowing for up to three trains to coexist safely on the layout. You will need at least one more block than you have trains in order to fully automate a layout, otherwise there will be nowhere for a train to move to in order to start the automation sequences. This is outlined in further detail in the fully automated layout section.
Block 1
Block 1 is the approach to turnout/point 1, and can be used to prevent a train from entering either the station siding or the main track between turnouts/points 1 and 2 if they are occupied.
We will use ID 1 for this, with an alias:
ALIAS(BLK1_TRN1_APP, 1)
Block 2
Block 2 consists of the section of the main track between turnouts/points 1 and 2, providing for a section to hold one train, allow a train on the station siding to exit safely, and also prevent a train running around the main track from entering this block.
We will use ID 2 for this, with an alias:
ALIAS(BLK2_MAIN_HOLD, 2)
Block 3
Block 3 is for our station siding, ensuring no other trains can enter this block while it is occupied.
We will use ID 3 for this, with an alias:
ALIAS(BLK3_STN, 3)
Block 4
Block 4 is the exit beyond turnout/point 2, and can hold a train while block 1 is occupied. Once block 1 is free, a train can run uninterrupted from block 4 back to block 1.
Note that block 4 on the diagram continues all the way to the beginning of block 1
We will use ID 4 for this, with an alias:
ALIAS(BLK4_TRN2_EX, 4)
Station
In this particular stage, there’s nothing specific for the station, however some advanced concepts might be to trigger an automated sound recording of arrivals and departures based on triggering sensor 3 or even sensor 2 if turnout/point 1 is in the thrown position.
This would likely make use of the EX-RAIL AT()
or AFTER()
commands.
Manual train control with automated routes
If you still wish to be the driver of the trains but have some automation related to the turnouts/points and signals, then we can make use of EX-RAIL’s ROUTE()
directive. In this scenario, we don’t need to implement our virtual blocks, as it will be up to you as the driver to ensure your trains don’t collide. We also don’t need to use the sensors, and will set our signals based on the choice of routes.
The two routes below will be advertised to wiThrottle Protocol applications such as Engine Driver, so you can simply select them from the ROUTE menu.
Note that you can mix and match all the above I/O methods together, so you can use direct I/O pins on the Mega2560 while using MCP23017 I/O expanders, PCA9685 servo modules, and any other supported I/O options, which provides a myriad of possibilities to expand the I/O capabilities of your CommandStation.
For simplicity, we will outline the stage 1 options using consistent hardware types otherwise we’ll wear out the scroll button on your mouse.
Once you understand the logic of our routes below and the various turnout/point, sensor, signal, and virtual block concepts above, you can view some complete myautomation.h examples at the end of this page.
Startup sequence
When the CommandStation and EX-RAIL starts, everything defined before the first DONE
command executes automatically.
For safe running and a known starting state, we ensure both our turnouts/points are closed and set all our signals to red, followed by the first DONE
to stop EX-RAIL executing any further automatically.
If we omit that first DONE
, EX-RAIL would automatically execute ROUTE(1, "Main Track")
at every startup. Note, of course, that if you want that first route executed at every startup, then you can simply omit that same DONE
, which will have the same effect as manually selecting the “Main track” route.
Route 1 - main track running
The first route we publish for use is ROUTE(1, "Main track")
which will appear in wiThrottle Protocol based apps (like Engine Driver and wiThrottle) with the description “Main track”.
Given we have closed our turnouts/points and set all our signals red in the startup sequence above, when selecting this route the first time, it will simply set signals 1 and 2 green, as the IFTHROWN()
statements will evaluate as false and not execute the associated commands.
On subsequent selections of this route, after selecting route 2 below, the turnouts/points will be thrown, resulting in these IFTHROWN()
statements evaluating as true, and therefore executing the associated commands.
When this happens, signal 3 to exit the station siding is set to red RED(SIG3_STN_EX)
as it is no longer safe to exit.
Next, in order to safely be able to close turnout/point 1, signal 1 is set to amber for 2 seconds to warn of the impending change AMBER(SIG1_TRN1_APP)
, and then red for a further 2 seconds RED(SIG1_TRN1_APP)
to allow time for the turnout/point to close fully CLOSE(TRN1)
.
This same logic is applied for turnout/point 2, setting signal 2 to amber AMBER(SIG2_TRN2_GO)
, then red RED(SIG2_TRN2_GO)
to allow turnout/point 2 to close fully CLOSE(TRN2)
.
Once both turnouts are closed, both signals 1 and 2 are set to green to indicate trains are safe to run through both turnouts with GREEN(SIG1_TRN1_APP)
and GREEN(SIG2_TRN2_GO)
.
The route is completed with a DONE
to tell EX-RAIL not to proceed any further.
Route 2 - enter and exit the station siding
The second route we publish for use is ROUTE(2, "Stating siding")
which will appear in wiThrottle Protocol based apps (like Engine Driver and wiThrottle) with the description “Station siding”.
Counter to the main track route above, we use IFCLOSED()
statements to evaluate if turnouts/points need to change or not from their current position. Therefore, if the first route we choose after startup is this one, both statements will evaluate true. The same will occur if we select our main track route.
When this happens, signal 2 to proceed beyond turnout/point 2 on the main track is set to red RED(SIG2_TRN2_GO)
as it is no longer safe to exit that section of track.
Next, in order to safely be able to throw turnout/point 1, signal 1 is set to amber for 2 seconds to warn of the impending change AMBER(SIG1_TRN1_APP)
, and then red for a further 2 seconds RED(SIG1_TRN1_APP)
to allow time for the turnout/point to throw fully THROW(TRN1)
.
This same logic is applied for turnout/point 2, setting signal 3 to amber AMBER(SIG3_STN_EX)
, then red RED(SIG3_STN_EX)
to allow turnout/point 2 to be thrown THROW(TRN2)
.
Once both turnouts/points are thrown, both signals 1 and 3 are set to green to indicate trains are safe to enter and exit the station siding with GREEN(SIG1_TRN1_APP)
and GREEN(SIG3_STN_EX)
.
The route is completed with a DONE
to tell EX-RAIL not to proceed any further.
Fully automated layout
Now it’s time to display the full automation capabilities by setting our layout up for fully automated control of your trains.
You will note that these are somewhat similar to Multiple inter-connected trains, updated to suit the specifics of the RMFT layout.
To setup for these fully automated sequences, we need to ensure our trains are placed in the below positions, noting that EX-RAIL has no way of knowing where a train is on the layout when first starting.
Train 1 in block 1, between sensor 1 and turnout/point 1.
Train 2 in block 2, between sensors 2 and 4.
Train 3 in block 4, after turnout/point 2.
Once you understand the logic below and the various turnout/point, sensor, signal, and virtual block concepts above, you can view some complete myautomation.h examples at the end of this page.
Virtual block logic
As mentioned in the introduction, we can enable fully automated running of up to three trains on this layout by breaking it into four virtual blocks.
Note
Remember, these are virtual blocks, and do not necessarily need to be electrically isolated from each other. Don’t confuse isolated blocks of track or block occupancy detection with these virtual blocks. For further background, refer to Virtual Block Control.
When reading through the sections below on the logic, it helps to keep in mind the perspective of the engineer driving the train, rather than thinking of the complete layout. As the engineer, you need to ask yourself the question “what needs to be in place for me to safely drive this train to the desired destination?”
The automation is accomplished by defining six separate sequences that map out how trains can move safely from one block to the next, and we also use LATCH()
as a technique to alternate between trains stopping at the station or continuing on the main track.
Full automation startup sequence
As outlined above in the startup sequence section, everything before the first DONE
in myAutomation.h is executed on start up.
Given we are starting with three trains on the layout occupying virtual blocks 1, 2, and 4, we need to ensure our layout starts up in a manner that is safe for the automation to commence running these trains correctly.
Therefore, once again we ensure both our turnouts/points are closed and our signals are set to red so these objects are all in a known state to start with.
Next, we place a RESERVE() on each block a train occupies, which will prevent any sequence from driving another train into that block.
Once these activities have been done, we can tell our trains to start following the appropriate sequence, which will let them start on their fully automated journey safely.
Exiting block 1 - station entry or main track?
In order to safely exit block 1, the first decision to be made is if the train will go straight through to continue on the main track, or if it will switch on to the station siding.
Using the LATCH()
command gives us a way to automatically alternate between the main track and the station siding. LATCH()
simply sets the state of a pin (either real or virtual) which can then be tested by an IF()
statement. In this particular case, we have defined pin 60 (alias “CHOOSE_BLK2”) to be latched and unlatched, as this pin does not exist on the Mega2560, nor does it exist on any of our I/O expander boards. Further reading on LATCH()
and UNLATCH()
can be found in the Sensors/Inputs - Reading and Responding section of the EX-RAIL reference.
When our CommandStation starts up, virtual pin 60 will not be set, and therefore evaluating the IF() statement IF(CHOOSE_BLK2)
will return false, with our sequence then latching this virtual pin, meaning the next time this sequence is called, IF(CHOOSE_BLK2)
will return true.
This logic allows us to follow our block 1 to block 3 sequence (if false) to switch onto the station siding, or follow our block 1 to block 2 sequence (if true) to continue on the main track.
On startup, we are sending train 1 on this sequence, which means with our IF(CHOOSE_BLK2)
returning false on startup, train 1 will first attempt to move from block 1 to block 3, which means switching to our station siding. Control at this point is handed over to the moving from block 1 to block 3 - entering the station sequence.
As a result of executing the LATCH(CHOOSE_BLK2)
, the next train navigating this block will instead have control handed over to the moving from block 1 to block 2 - continue on the main track sequence.
Moving from block 1 to block 2 - continue on the main track
To move from block 1 to block 2, the first thing we need to know is if it’s safe to do so.
In EX-RAIL, this is accomplished by using the RESERVE()
command which says if the block is free, we can reserve it. If it is not free, the reserve cannot be completed, and the train will stop until the block is freed, in which case the sequence can continue.
So, providing a reserve can be placed on block 2, we can then test to see if turnout/point 1 is thrown. If so, the turnout/point needs to be closed, but in order to do so safely we set the approach signal amber for 2 seconds AMBER(SIG1_TRN1_APP)
, then set the signal red RED(SIG1_TRN1_APP)
before closing the turnout/point CLOSE(TRN2)
, and waiting a further 2 seconds to ensure the turnout/point is fully closed.
Once the turnout/point is closed, or if it already was, we set our signal green GREEN(SIG1_TRN1_APP)
and tell the train to proceed at speed 20 FWD(20)
. The speed is relatively slow given the train is likely to need to stop again before being able to move beyond turnout/point 2.
Then, after the train has not only activated sensor 2, but has passed over it completely and allowed it to deactivate for 0.5 seconds AFTER(SNS2_MAIN_TRN1_EX)
, the reservation on block 1 can be released FREE(BLK1_TRN1_APP)
, meaning the next train needing to enter block 1 can do so.
At this point, control of the train is handed over to the moving from block 2 to block 4 - continue on the main track sequence.
Moving from block 1 to block 3 - entering the station
To move from block 1 to block 3, we again use RESERVE()
.
As per our move to block 2 above, we ensure turnout/point 1 is set correctly, however this time we need it to be thrown rather than closed, and so again set our signals if it needs to change.
Once the turnout/point is thrown, or if it already was, we set our signal green and tell the train to proceed at a very leisurely speed of 10 FWD(10)
as we need to be cautious approaching the station.
The train needs to STOP
at the appropriate point on the station AT(SNS3_STN)
, at which point the reservation on block 1 can be released as we no longer occupy it FREE(BLK1_TRN1_APP)
and it’s safe for another train to enter that block.
There is now a delay of 10 to 15 seconds while our passengers embark or disembark DELAYRANDOM(10000, 15000)
, before moving off again at low speed FWD(10)
until sensor 5 is reached AT(SNS5_STN_TRN2_APP)
, at which point the control of the train is over to the moving from block 3 to block 4 - exiting the station siding sequence.
Moving from block 2 to block 4 - continue on the main track
There are no new concepts here compared with our previous virtual block sequences, and we again need to ensure block 4 is free prior to entering it, then ensure our signals and turnout/point are set correctly, and once again after leaving block 2 AFTER(SNS4_MAIN_TRN2_APP)
we free block 2 in order for the next train to be able to safely enter it.
Once done, train control is over to the moving from block 4 to block 1 - the speed run sequence.
Note
Note that we start with train 2 occupying block 2, and train 3 occupying block 4 (both with a RESERVE()
in place as part of our startup sequence) and therefore train 2 cannot proceed until train 3 has exited block 4.
Also on startup, train 1 is occupying block 1 (with a RESERVE()
in place as part of our startup sequence), and therefore until train 1 exits block 1, train 3 cannot enter block 1.
This creates a domino effect whereby train 1’s exit of block 1 is needed in order for train 3 to enter block 1, and likewise for train 3 to exit block 4 in order for train 2 to enter block 4. The trains will then follow the sequences around the layout accordingly.
Moving from block 3 to block 4 - exiting the station siding
Leaving the station siding is another repeat of the same logic, ensuring block 4 is free to enter, our signals and turnout/point are set correctly, and again freeing block 3 after we leave it AFTER(SNS5_STN_TRN2_APP)
.
Control is then handed over to the moving from block 4 to block 1 - the speed run sequence.
Moving from block 4 to block 1 - the speed run
The final sequence is the simplest of all, and allows for a higher speed run through block 4 FWD(40)
(providing block 1 is free to enter), again freeing up block 4 once we’ve exited it AFTER(SNS1_TRN1_APP)
, and then finally handing control back to our original exiting block 1 - station entry or main track? decision sequence.
Again, we start up with train 3 occupying block 4, and once train 1 has exited block 1, the sequence below will execute, with train 3 moving to block 1, and train 2 being able to exit block 2.
Learnings from stage 1
No doubt, as you’ve read through this fairly lengthy stage 1 page, you’ve already noted a number of commonalities between all variations of myAutomation.h, regardless of the way we have defined the various objects, and hopefully you’ve picked up a few tips and techniques to help you on your DCC-EX and EX‑RAIL journey.
The main things at this point that we’d like to call to your attention are:
Using aliases helps your brain along. Most of us aren’t geared to remember that turnout/point ID 100 is the station siding entrance turnout/point, so defining aliases makes these numbers easier to digest and work with when referring to them in myAutomation.h.
You can expand your I/O devices as you need. The Mega2560 provides easily for 42 available I/O pins (A2 to A15, and 22 to 49), but when you exceed this limit, you can very easily expand this using I/O expanders such as the MCP23017. This means you don’t need to have all these devices up front and can start with just the Mega2560.
Use virtual blocks to safely control automation of your layout. With appropriate use of
RESERVE()
andFREE()
along with appropriate location of sensors, you can safely have a number of different trains traversing all sorts of layouts without colliding.EX-RAIL is an incredibly powerful piece of software that can automate the most basic, simple layout functions as well as provide fully automated, prototypical operation of an entire layout which is limited only by your imagination.
Complete myAutomation.h examples
ROUTEs with DCC accessory turnouts/points and signals on Mega2560 direct I/O pins
ROUTEs with turnouts/points/signals on Mega2560 direct I/O pins
ROUTEs with turnouts/points/signals on MCP23017 I/O expander Vpins
ROUTEs with servo based turnouts/points/signals on a PCA9685 servo module
Full automation with pin based turnouts/points and signals on Mega2560 direct I/O pins
Full automation with pin based turnouts/points and signals on MCP23017 I/O expander Vpins
Full automation with servo based turnouts/points and signals with a PCA9685 servo module