Sequences - an Introduction
This page is a limited introduction to the EX‑RAIL automation sequences. For more comprehensive information refer to the EX-RAIL Command Reference.
Before You start, generally you will need to have created some Key Objects (e.g. Turnouts/Points, Sensors, Signals) before you start writing sequences. Refer to the previous page (Objects - an Introduction) for creating and adding those Objects. Note that these objects don’t have to be listed in the myAutomation.h file before the sequence in which you use it, but it is good practice to do so.
For a full list of keywords, see EX-RAIL Command Reference. Only a subset are described on this page.
Types of Sequence
There are four types of Sequence:
SEQUENCE
ROUTE
AUTOMATION
ONevent based sequences
SEQUENCE
Simply a list of things to be done in order. These things might be to actually drive a train around, or merely to set some turnouts or flash some scene or panel lights. Actions can be made to wait for conditions to be met, like a sensor detecting a train, a button being pushed, or a period of time elapsing.
A generic SEQUENCE (unlike the variations below) can only be started by another SEQUENCE, or automatically at the start-up of the Command Station.
ROUTE
A special type of SEQUENCE that is made visible to a throttle with a readable name, so the user can press a button to get the sequence executed. This might be best used to set a series of turnouts and signals to create a route through the layout.
AUTOMATION
A special type of SEQUENCE that is made visible to a throttle with a readable name, so that a user can hand over a loco and instruct that specific loco to follow each step listed in the sequence.
ONevent sequences
Special types of SEQUENCE that are activated when certain ‘events’ occur. Including when signals change, when turnouts/points change, when a turntable rotates, at a specific time, etc.
These include:
ONGREEN( signal_id)
ONAMBER( signal_id)
ONRED( signal_id)
ONCLOSE( turnout_id )
ONTHROW( turnout_id )
ONCHANGE( vpin )
ONROTATE( id )
ONACTIVATE( addr, sub_addr )
ONACTIVATEL( linear )
ONDEACTIVATE( addr, sub_addr )
ONDEACTIVATEL( linear )
ONCLOCKTIME( hours, mins )
ONCLOCKMINS( mins )
ONOVERLOAD( track )
Most people wanting to do animations or run trains through an automated route will use a SEQUENCE, but those with throttles that support it (Engine Driver, EX‑WebThrottle) can add routes and automations. Both of these terms are just tags that let throttles with this feature automatically assign sequences to control buttons. “Routes” go into route buttons/screen. Automation sequences will also appear with the routes but with a ‘handoff’ button that will supply or handoff the Loco ID to EX‑RAIL where it can take over and run the train autonomously.
e.g. A ROUTE could be just changing a series of turnouts/points, or could be driving a specific loco on a complex journey around the layout. An AUTOMATION could be reading id of the loco currently controlled by user, and taking it on a complex journey around the layout.
The Automation Process
Once started, each ‘sequence’ will step through a list of simple keyword commands, in order, until they reach a DONE
keyword.
Multiple concurrent sequences are supported. i.e. More than one sequence can be running at the same time.
Sequential execution of sequences is supported. i.e. One sequence can invoke another sequence, including itself using the FOLLOW(id)
or CALL(id)
commands.
Note
COMMANDS are case sensitive. i.e. they must be in uppercase. Text parameters you provide (aliases, descriptions) are not.
Structure of a ‘Sequence’
In general, sequences follow the basic structure:
<sequence-type>( sequence-id, parameter-2, ...)
<command 1>
<command 2>
...
<command n>
DONE or RETURN or FOLLOW ( id )
For example:
// Example
ROUTE(1,"Coal Yard exit") // unique sequence-id = 1
RED(77) // signal 77 to Red
THROW(1) // throw turnout/point 1
CLOSE(7) // close turnout/point 7
DELAY(5000) // 5 second wait
GREEN(92) // signal 92 to Green
DONE
Invoking/Trigging Sequences
Sequences types fall in the following broad groups:
Manually triggered
Triggered by another sequence
Triggered as a result of an event that has occurred on one of the turnouts/points, sensors, signals.
Manually Triggered Sequence Types
Manually triggered sequences are advertised to WiThrottles so you can activate them on your throttles (e.g. Engine Driver or wiThrottle). They are one of:
|
Start a Automation Sequence and creates a WiThrottles {Handoff} button to automatically send a train along. |
|
Start of a Route Sequence and creates a WiThrottles {Set} button to manual drive the train along |
Note that these can also be invoked by other sequences.
Invoked Sequence Types
Sequences that can only be triggered by other sequences have the following form:
|
A general purpose Sequence for scenic animations, etc. |
Event Triggered Sequence Types
// Example
ONTHROW(8) // When turnout 8 is thrown,
THROW(9) // throw the facing turnout
RED(24) // signal 24 to red
DELAY(2000) // wait 2 seconds
GREEN(27) // signal 27 to green
DONE
Sequences that are triggered when ‘events’ occur, include:
ONCLOSE( turnout_id ) |
Event handler for turnout close |
ONTHROW( turnout_id ) |
Event handler for turnout thrown |
See the EX-RAIL Command Reference for additional Event Triggered Sequence types, and additional information on these types.
Automatically Running a Sequence at Power Up
Note
There is no longer an implied AUTOSTART whereby everything in myAutomation.h prior to the first DONE
keyword is executed on startup. If you wish to reinstate this behaviour, simply add the keyword AUTOSTART
as the first line.
If you want a sequence to start immediate the system powers up, add the AUTOSTART
command to the content of the sequence.
This is useful for sequences where you want to constantly monitor the state of sensors and switches.
Contents of a ‘Sequence’
A sequence is made up of ‘Commands’. Commands are usually written one per line for ease of reading, but you can put multiple commands on a single line.
The commands fall into some basic categories:
Actions - Commands that ‘do’ something
-
Conditionals & Branching - Commands that change the flow/order in which the commands are executed
Delays & Waits - Commands that change the timing of the execution of the commands
Action Commands - Getting EX-RAIL to ‘do’ something
This type of command will somehow change Objects of the system you have created and defined, like turnouts/points, signals, servos, turntables, blocks and locos.
There are a substantial number of commands that you can explore in the EX-RAIL Command Reference. We will look at just a few here.
// Example
ONTHROW(8) // When turnout 8 is thrown,
THROW(9) // throw the facing turnout
RED(24) // signal 24 to red
DELAY(2000) // wait 2 seconds
GREEN(27) // signal 27 to green
DONE
Turnout/Point commands include:
THROW( id ) |
Throw a defined turnout |
CLOSE( id) |
Close a defined turnout |
Signal related commands include:
RED( signal_id ) |
Set defined signal to Red (See SIGNAL) |
AMBER( signal_id ) |
Set a defined signal to Amber. (See SIGNAL) |
GREEN( signal_id ) |
Set a defined signal to GREEN (see SIGNAL) |
// Example
AUTOMATION(4,"Back and Forward")
AUTOSTART // start this immediately the system powers up
FWD(50) // move forward at DCC speed 50
DELAY(5000) // run for 5 seconds
STOP // stop the train
REV(30) // move backwards at DCC speed 50
DELAY(5000) // run for 5 seconds
STOP // stop the train
FOLLOW(4) // repeat forever
Loco related commands include:
FWD( speed ) |
Drive loco forward at DCC speed 0-127 (1=ESTOP) |
REV( speed ) |
Drive logo in reverse at DCC speed 0-127 (1=ESTOP) |
SPEED( speed ) |
Drive loco in current direction at DCC speed (0-127) |
STOP |
Set loco speed to 0 (same as SPEED(0) ) |
Turntable related commands include:
MOVETT( vpin, steps, activity ) |
Move a turntable the number of steps relative to home, and perform the activity (refer EX-Turntable documentation) |
See the EX-RAIL Command Reference for additional commands and additional information on these commands.
Sequence Flow / Flow Control Commands
For a simple sequence, once triggered, the system steps though each and every instruction as quickly as possible until it hits DONE
at the end of the sequence.
However there are a number of ways that the processing of a sequence can be changed:
The timing of the execution of the commands can be altered as well with ‘Delay’ and ‘Wait’ type commands.
Conditionals
If a conditional is encountered, the following (enclosed) commands are only executed if the specified conditions are met.
Conditionals have the structure:
// Example
// - Toggle a turnout/point based on a push button
SEQUENCE(85)
DELAY(100) // check every 0.1 of a second
AT(35) // monitor push button 35
IFCLOSED(105) // check the state of turnout/point 105
THROW(105) // if closed THROW Turnout/Point 105
ELSE
CLOSE(105) // if closed CLOSE Turnout/Point 105
ENDIF
FOLLOW(85) // repeat forever
...
IFxxx( id_or_condition, ... ) // where xxx is the type of 'IF' command (see below)
<commands to execute if the conditions are met>
...
ENDIF
...
or
...
IFxxx( id_or_condition, ...) // where xxx is the type of 'IF' command (see below)
<commands to execute if the conditions are met>
...
ELSE
<commands to execute if the conditions are NOT met>
...
ENDIF
...
Types of Conditionals
Sensor Related Conditional:
IF( sensor_id ) |
If sensor activated or latched, continue, otherwise skip to ELSE/ENDIF, use negative values for active HIGH sensors |
IFNOT( sensor_id ) |
If sensor NOT activated and NOT latched, continue, otherwise skip to ELSE/ENDIF, use negative values for active HIGH sensors |
IFGTE( sensor_id, value ) |
Test if analog pin reading is greater than or equal to value (>=) |
IFLT( sensor_id, value ) |
Test if analog pin reading is less than value (<) |
Turnout/Point Related Conditionals:
IFTHROWN( turnout_id ) |
Test if turnout is thrown |
IFCLOSED( turnout_id ) |
Check if turnout is closed |
Signal Related Conditionals:
IFRED( signal_id ) |
Tests if signal is red |
IFAMBER( signal_id ) |
Tests if signal is amber |
IFGREEN( signal_id ) |
Tests if signal is green |
Other Conditionals:
IFRANDOM( percent ) |
Runs commands in IF block a random percentage of the time |
IFRESERVE( block ) |
If block is NOT reserved, reserves it and run commands in IF block. Otherwise, skip to matching ENDIF |
IFTIMEOUT |
Tests if “timed out” flag has been set by an ATTIMEOUT sensor reading attempt |
see the EX-RAIL Command Reference for additional information.
Branching
Sequences can invoke other sequences. There are two ways this can be done.
CALL and RETURN
To invoke another sequence, and return and execute the next command in the current sequence you can use the combination of the CALL( route_id )
command in the main sequence, and in the called sequence, use the RETURN
command to return.
CALL( route_id ) |
Branch to a separate sequence, which will need to RETURN when complete |
RETURN |
Return to the calling sequence when completed (no DONE required). |
FOLLOW
To invoke another sequence, with no wish to return and execute any further commands in the current sequence you can use the FOLLOW( route_id )
command in the main sequence.
FOLLOW( route_id )
Branch or Follow a numbered sequence. (The ‘followed’ sequence does not return to the sequence that invoked it.)
Delays & Waits
The timing of the execution of the commands in a sequence can be altered with ‘Delay’ or ‘Wait’ type commands. i.e. they don’t happen immediately on completion of the previous command.
There are a number of delay type commands that you can explore in the EX-RAIL Command Reference. We will look at just a few here.
// Example
AUTOMATION(5,"Back and Forward - Random")
FWD(50) // move forward at DCC speed 50
DELAY(5000, 20000) // run for between 5 to 20 seconds (random)
STOP // stop the train
REV(30) // move backwards at DCC speed 50
DELAY(5000, 20000) // run for between 5 to 20 seconds (random)
STOP // stop the train
FOLLOW(5) // repeat forever
DELAY( delay ) |
Delay a number of milliseconds |
DELAYMINS( delay ) |
Delay a number of minutes |
DELAYRANDOM( min_delay, max_delay ) |
Delay a random time between min and max milliseconds |
AFTER( sensor_id ) |
Waits for sensor to trigger and then go off for 0.5 seconds, use negative values for active HIGH sensors |
WAITFOR( pin ) |
Wait for servo to complete movement |
AT( sensor_id ) |
Wait until sensor is active/triggered, use negative values for active HIGH sensors |
ATTIMEOUT( sensor_id, timeout_ms ) |
Wait until sensor is active/triggered, or if the timer runs out, then continue and set a testable “timed out” flag, use negative values for active HIGH sensors |
AFTER( sensor_id ) |
Waits for sensor to trigger and then go off for 0.5 seconds, use negative values for active HIGH sensors |
Command Station Commands
There are a substantial number of commands that you can explore in the EX-RAIL Command Reference. We will look at just a few here.
// use the drive away feature to recginse the loco on the
// programming track and drive it onto the main track
SEQUENCE(99)
READ_LOCO // identify the loco on the programming track
JOIN // connect programming track to main
WAIT(30000) // wait 30 seconds
UNJOIN // disconnect the programming track form the main
// see the 'Drive-Away Feature' on this page for more infomation
JOIN |
Joins PROG and MAIN track outputs to send the same MAIN DCC signal on both tracks |
UNJOIN |
Disconnect Prog track from Main DCC signal |
READ_LOCO |
Read loco ID from Prog track |
See the EX-RAIL Command Reference for additional Command Station Commands and additional information on the commands shown here.
Referencing Key Objects in Sequences
Referencing Turnouts/Points
EX‑CommandStation supports a number of different turnout/point hardware configurations, but your automation treats them all as simple ID numbers. Turnouts may be defined using <T>
commands from JMRI, or in SETUP("<T ...>")
commands placed in your mySetup.h file, or C++ code in mySetup.h, just like earlier versions.
You may, however, find it more convenient to define turnouts/points using EX-RAIL commands, which may appear anywhere in the ‘myAutomation.h’ file, even after they are referenced in an ONTHROW
, ONCLOSE
, THROW
or CLOSE
command. (EXRAIL extracts the turnout definitions just once from your script at Command Station startup.)
Turnouts/Points defined in ‘myAutomation.h’ will still be visible to WiThrottle and JMRI in the normal way.
A TURNOUT command sends DCC signals to a decoder attached to the track, a PIN_TURNOUT sends a “throw” or “close” (5V or 0V signal) to a pin on the Arduino, and a SERVO_TURNOUT sends an I2C serial command to a servo board connected to your servos.
See the EX-RAIL Command Reference for TURNOUT, PIN_TURNOUT and SERVO_TURNOUT definitions.
Referencing Signals
Signals can now simply be a decoration to be switched by the route process; they don’t need to control anything.
GREEN(55)
would turn signal 55 green, and RED(55)
would turn it red. Somewhere in the script there must be a SIGNAL command like this: SIGNAL(55,56,57)
. This defines a signal with ID 55, where the Red/Stop lamp is connected to pin 55, the Amber/Caution lamp to pin 56, and the Green/Proceed lamp to pin 57. The pin allocations do not need to be contiguous, and the red pin number is also used as the signal ID. Thus you can change the signal by RED(55)
, AMBER(55)
, or GREEN(55)
. This means you don’t have to manually turn off the other lamps. A RED/GREEN only signal may be created with a zero amber pin.
Referencing Locos
//Example
SEQUENCE(98)
SETLOCO(9999) // select loco 9999
SPEED(0) // set the speed to 0.
// This will turn the track power on
// if it is not on already.
DONE
To reference a loco in a sequence you only need to know it’s DCC Address. i.e. It does not need to be in the roster.
Use the SETLOCO( loco_dcc_address )
command to set the loco address for the sequence. Use SENDLOCO( ( loco_dcc_address, route_id )
to activate a new route/sequence send a given loco along it.
Following commands (e.g. SPEED (50)
) will be directed at the loco chosen.
Referencing Loco Functions
You can use FON( function_no )
and FOFF( function_no )
to activate and deactivate loco functions… eg sound horn. The loco that the command will be directed to will be the one previously chosen using SETLOCO( loco_dcc_address )
or SENDLOCO( ( loco_dcc_address, route_id )
.
Referencing Sensors
Sensor numbers are direct references to VPINs (virtual pin numbers) in the Hardware Abstraction Layer. For a Mega onboard GPIO pin, this is the same as the digital pin number. Other pin ranges refer to I/O expanders etc.
Sensors with ID’s 0 to 255 may be LATCHED/UNLATCHED in your script. If a sensor is latched on by the script, it can only be set off by the script… so AT(5) LATCH(5)
for example effectively latches the sensor 5 on when detected once.
Sensor polling by JMRI is independent of this, and may continue if <S>
commands are used.
Drive-Away feature
EX-RAIL can switch a track section between programming and mainline.
Here for example is a launch sequence that has no predefined locos but allows locos to be added at station 1 while the system is in motion. Let’s assume that the track section at Station1 is isolated and connected to the programming track power supply. Also that we have a “launch” button connected where sensor 17 would be and an optional signal (i.e. 3 LEDs) on the control panel connected where signal 27 would be.
SEQUENCE(99)
SIGNAL(27,28,29)
RED(27) // indicate launch not ready
AFTER(17) // user presses and releases launch button
UNJOIN // separate the programming track from main
DELAY(2000)
AMBER(27) // Show amber, user may place loco
AFTER(17) // user places loco on track and presses “launch” again
READ_LOCO // identify the loco
GREEN(27) // show green light to user
JOIN // connect prog track to main
START(12) // send loco off along route 12
FOLLOW(99) // keep doing this for another launch
The READ_LOCO reads the loco address from the PROG track and the current route takes on that loco. By altering the script slightly and adding another sensor, it’s possible to detect which way the loco sets off and switch the code logic to send it in the correct direction by using the INVERT_DIRECTION
instruction so that this locos FWD and REV commands are reversed. (easily done with diesels!)
Next Steps - Examples
See the Example Objects and Sequences page or click the ‘Next’ button to see some concrete examples of automation sequences.