JMRI: WiFi Throttle Communications Protocol
This page describes the protocol provided by the jmri.jmrit.withrottle package for controlling trains, turnouts, and more through JMRI via a TCP/IP socket.
JMRI uses a fairly simple text-based client-server protocol over TCP/IP sockets. Throttles (or other applications) can locate JMRI by sending a multi-cast DNS request, using ZeroConf/Bonjour, for the address "_withrottle._tcp.local". JMRI won't always reply (it depends on your operating system, firewall settings, and network environment), so your application should also allow entering an IP address and port.
When you first open the connection, JMRI responds with a number of lines of text that provide information about the JMRI instance to which you've connected. Your application then sends commands to JMRI. Each of these commands is plain text, with a newline character at the end (0A hex). Similarly, lines sent back from JMRI are terminated by a newline. Other implementations of the WiThrottle protocol will terminate the command with a carriage return (0D hex) followed by a newline.
You can see these messages by enabling DEBUG for WiThrottle. Add the line "log4j.category.jmri.jmrit.withrottle=DEBUG" to your default.lcf file and restart JMRI. All messages (both directions) will be added to your JMRI System Console and the session.log file.
String Parsing
JMRI commands and results are very often arrays of values, and sometimes arrays of arrays. The array elements are separated by three-character delimiters. For example, the following roster entry contains two engines, and each engine has three values:
RL2]\[RGS 41}|{41}|{L]\[Test Loco}|{1234}|{L
Here the delimiters are color-coded to make it easier to
see the two different delimiters. The ]\[
delimiter is used to divide
the different roster entries (there are two), while the
}|{
delimiter is used to
divide the parts of each roster entry. The details are
described in sections below for each command and
response.
Initial Connection
When you first open a socket to JMRI, you may get a response that will look something like the following:
VN2.0 RL1]\[SP 2101}|{2102}|{L PPA2 PTT]\[Turnouts}|{Turnout]\[Closed}|{2]\[Thrown}|{4 PRT]\[Routes}|{Route]\[Active}|{2]\[Inactive}|{4 RCC0 PW12080 *10
Each line is actually followed by two end-of-line characters (newline/newline or carriage return/newline). For brevity, the extra blank line is not shown.
Some implementations will not send these initial
lines until a command has been sent by the client. The Digitrax LnWI
will send VN2.0
in response to an N
command, while a more complete block is sent in response to
the H
command.
Each line contains information for part of JMRI, as described below, so your application can show things like engines, turnouts, etc.
WiThrottle Version
The first string, VN2.0
, provides the version
number of the WiThrottle protocol. In this case the version
number is 2.0.
Roster List
The roster string always begins with RL
(Roster List) followed by the number of entries in the
roster. Here are three different examples of roster strings
that might be returned by JMRI:
RL0 RL2]\[RGS 41}|{41}|{L]\[Test Loco}|{1234}|{L RL3]\[D&RGW 341}|{3}|{S]\[RGS 41}|{41}|{L]\[Test Loco}|{1234}|{L
with zero, two, and three roster entries, respectively. Each roster entry contains three pieces of information, always in the same order:
Segment | Example | Description |
---|---|---|
Name | RGS 41 | The name to show in your application |
Address | 41 | The DCC address of the locomotive |
Length | L | S or L to indicate if this is a Short or Long DCC address |
Track Power
Returns information about the curent state of track power. This can be one of the following:
PPA0 | Off |
PPA1 | On |
PPA2 | Unknown |
Turnout List
JMRI returns either zero, one, or two strings for this section. The first string provides the labels for the different turnout states. For example:
PTT]\[Turnouts}|{Turnout]\[Closed}|{2]\[Thrown}|{4
This string contains an array with two elements, which provide the labels to use for each turnout state (except for Unknown):
Segment | Example | Description |
---|---|---|
Caption | Closed | The string to show for the state of turnout |
Value | 2 | The corresponding value for the turnout state |
There is also a third value, Unknown, represented with the value 1, that is not explicitly given a caption in the reply.
The second string appears when you have turnouts defined in JMRI, and contains an array of turnouts, and will look something like this:
PTL]\[LT12}|{Rico Station N}|{1]\[LT324}|{Rico Station S}|{2
Each turnout contains the following segments:
Segment | Example | Description |
---|---|---|
System Name | LT12 | The internal name used for this turnout |
User Name | Rico Station N | The name entered into JMRI for this turnout |
State | 1 | The current state of the turnout (see above) |
Route List
Like for turnouts, JMRI will reply with zero, one or two strings. The first string is likewise a list of labels for the route values. Again, Unknown (1) is not explicitly listed. For example:
PRT]\[Routes}|{Route]\[Active}|{2]\[Inactive}|{4
The second string contains an array of routes. For example:
PRL]\[IR:AUTO:0001}|{Rico Main}|{
Segment | Example | Description |
---|---|---|
System Name | IR:AUTO:0001 | The internal name used for this route. The name in this example was auto-generated |
User Name | Rico Main | The name entered into JMRI for this turnout |
Consist List
The consist list is somewhat similiar in concept to the roster list. However, there is a separate line for each consist entry. Here is an example of a consist list that contains two entries:
RCC2 RCD}|{74(S)}|{74(S)]\[3374(L)}|{true]\[346(L)}|{true RCD}|{123(S)}|{123(S)]\[3374(L)}|{true
The first line marks the start of the consist list, with the number of entries after the "RCC". Subsequent lines start with "RCD}|{" and are followed by information about a single consist entry.
For whatever reason, consist entries don't follow the same pattern as roster entries. Here is a summary of the Format for each consist entry (with spaces added for readability):
RCD }|{ consistAddress }|{ consistId Array: ]\[ loco DCC Address }|{ locoDirection
Addresses have the format address(S/L). For example, 3374(L) is a long address and 74(S) is a short addresses.
In the example above, here is the information about the first consist entry:
Data | Description |
---|---|
74(S) | Consist has short DCC address 74 |
74(S) | The ID of the consist |
3374(L)}|{true | Loco with long address 3374 in the forward direction |
346(L)}|{true | Loco with long address 346 in the forward direction |
JMRI Web Port
The final string from the initial reply provides the port set for the JMRI web server. For example:
PW12080
indicates that the web server port is set to 12080.
Property Change Notification
There are cases where property changes in JMRI cause a message to be sent. These messages are not a response to a command, but rather are sent at any time (from the perspective of your WiThrottle client). For example, if you have a Throttle window open in JMRI and you turn on the light, a property change message will be sent with the function information:
MTAL341<;>F10
All property change messages begin with MTA (where 'T' could be a different throttle letter) and then the DCC address of the locomotive. The part after the <;> separator uses the format described below, under the 'F' Function.
Here is a list of property notifications that can be sent
- Function state changed
- Speed
- Direction
- Speed step mode
Commands
Once you've established a connection as outlined above, you can start sending commands to JMRI. The first letter of each command is interpreted by the DeviceServer class to determine where to send the rest of the command. Here are the letters currently supported:
- 'T' sends a throttle command, deprecated, use 'M'
- 'S' sends a throttle command to a second throttle, deprecated, use 'M'
- 'M' sends a command to a "multi" throttle
- 'D' sends a hex packet to the command station
- '*' sends a heartbeat, or sets heartbeat on or off
- 'C' is not used anymore and forwards to the throttle controller.
- 'N' sets the device name
- 'H' sets the device ID.
- 'P' sends a panel command
- 'R' sends a roster command
- 'Q' indicates the device has quit, close its window.
'H' Device ID
Sets the device ID for the client. This could, for example, be the MAC address of the caller, as a string. Engine Driver uses sends device name (see 'N'), so it may or may not be unique.
'N' Device Name
Sets the name that will appear in the WiThrottle window.
In Engine Driver, this is the throttle name that you can set.
The syntax is Nname
, where name
can be any text, except for a newline, as the newline
terminates the command. For example:
NJohn's Throttle
Reply: *
StopSeconds
*10
The number after '*' indicates how often your throttle will need to send a command or heartbeat (0 means a heartbeat is not required). JMRI will send an EStop to the locomotive if it hasn't received a command or heartbeat within the number of seconds provided.
'*' Heartbeat
There are three versions of this command:
Command | Description |
* | Send heartbeat |
*+ | Turn heartbeat monitoring on |
*- | Turn heartbeat monitoring off |
The idea of the heartbeat is that JMRI will issue an emergency stop if it has not received any commands from your throttle within the heartbeat period. When you set your throttle name, using the N command, JMRI returns the emergency stop delay, in seconds, but it doesn't turn heartbeat monitoring on until you explicitly tell it to do so. Once you've turned it on, your throttle will need to ensure it sends periodic heartbeat commands (or any other command) to keep your engine(s) alive.
M - MultiThrottle Controller
The 'M' command is for a "multi" throttle, and is handled by the ThrottleController class.
The 'M' multithrottle can handle more than one locomotive. Engine Driver uses this feature, for example, to make it super easy to create a consist on the fly with any set of engines (without using DCC consisting). You can have one or more multithrottles, and each multithrottle can have more than one locomotives attached to it. The first character after the 'M' is used as a key for the instance of MultiThrottle to use. Engine Driver uses '0' through '6' as its keys for multithrottle instances. However, you can use other characters for the key, 'T' is used in the examples below.
MultiThrottle Elements
The command string after the first two characters is passed to the MultiThrottle for further processing. As in other cases, the commands are arrays, where the "<;>" string is used as the delimiter. For example:
MT+L341<;>ED&RGW 341
Contains the array elements "+L341" and "ED&RGW 341" (after removing the first two letters of the command).
The first character of the first element describes the MultiThrottle command, and is one of the following:
3rd Char | Description |
---|---|
A | Action. The following characters provide more details |
+ | Add a locomotive to the throttle |
- | Remove a locomotive from the throttle |
S | Request steal locomotive (see more below) |
Each of these operations is described in sections below.
The second element will be passed onto the ThrottleController instance for further processing, and the details are described below in the Throttle Controller section.
'+' -- Add Locomotive
The following command is a request to add a new locomotive to the multithrottle:MT+S3<;>ED&RGW 341
The first element is "+S3" has the '+' to indicate that this is a request to add a locomotive to the multithrottle. The rest of the string is another key, formatted like an address (e.g., "S3" or "L341"), which will be used in future references by MultiThrottle commands.
The second element is the actual address to select, using either the address directly ("S79" or "L3002") or by referencing a roster entry using the "E" command (such as "ED&RGW 341").
The key address MUST match the address chosen via the third element of the string (after the <;>). Any mismatch results in undefined behavior (and is known to vary between implementations).
The reply from this command is quite verbose, and contains details about this locomotive as known by JMRI. If the engine number is in JMRI's roster, it will return information like the following:
MT+L41<;> MTLL41<;>]\[Headlight]\[Bell]\[Whistle]\[Short Whistle]\[Steam Release]\[FX5 Light]\[FX6 Light]\[Dimmer]\[Mute]\[Water Stop]\[Injectors]\[Brake Squeal]\[Coupler]\[]\[]\[]\[]\[]\[]\[]\[]\[]\[]\[]\[]\[]\[]\[]\[ MTAL41<;>F00 MTAL41<;>F01 MTAL41<;>F02 ... MTAL41<;>F027 MTAL41<;>F028 MTAL41<;>V0 MTAL41<;>R1 MTAL41<;>s1
If, on the other hand, JMRI does not have the engine number in it's roster, it returns information like the following:
MT+L341<;> MTAL341<;>F00 MTAL341<;>F01 ... MTAL341<;>F027 MTAL341<;>F028 MTAL341<;>V0 MTAL341<;>R1 MTAL341<;>s1
the examples above are all replies from the multithrottle.
The reply consists of the following sections:
- Function labels
- Function states (pressed or released)
- Current speed
- Current direction
- Current speed step mode
For all except the function labels, see the appropriate throttle commands, such as 'V' below, for details.
The function labels are an array of strings, with ]\[ used as the array delimiter. So in the example above, F0 is called Headlight, F1 Bell, etc. Note that, unlike with other arrays, the array delimiter appears at the start and end of the array, as well as between elements.
'-' -- Remove Locomotive
The '-' command removes an engine from a multithrottle. Engine Driver, for example, sends the following command to release all locomotives from the 'T' throttle:
MT-*<;>r
The first element after the '-' character is the key of the locomotive to remove, or '*' to remove all locomotives from the multithrottle instance. The multithrottle instance will loop through all of it's throttle instances and send the 'r' command to each one. The 'r' command is a release command.
'A' -- Locomotive Action
This command passes the second element in the array to either a specific throttle (if you specify a key), or to all of the throttles in the multithrottle instance if you provide '*' as the key. For example:
MTA*<;>qR
The characters after the 'A' in the first element are the key to a locomotive in the multithrottle, or '*' to send the command to all of the locomotives. In the example above, Engine Driver is sending a "qR" command to all of the locomotives on the 'T' throttle.
Throttle Controller Commands
This section describes throttle commands, which are the second element of 'Mx' commands (as described above). After the command prefix, the next letter is one of the following:
- 'C' consist
- 'c' consist lead from roster entry
- 'd' dispatch.
- 'E' sets an address from a roster entry. Format EID.
- 'F' function key. Format F{0|1}Function.
- 'f' force function
- 'I' idle, which sets the speed to 0
- 'L' sets a long DCC address.
- 'm' momentary
- 'q' ask for current settings, such as speed or direction
- 'Q' quit
- 'R' set direction. Format R{0|1}.
- 'r' release.
- 'S' sets a short DCC address.
- 's' set speed step mode
- 'V' sets the speed (velocity). Format Vspeed.
- 'X' emergency stop.
Each of the commands is described in a section below when it takes additional information. Some commands, such as 'X' require no additional information.
'C' and 'c' Set lead loco for consist
These two commands both allow you to set the lead loco to which function commands will be sent. This is used in cases where you're not using CV21 and CV22 to determine how locos in an advanced consist should respond to functions.
MTAL341<;>CL346
'd' Dispatch and 'r' Release
For most DCC systems, these are the same thing; if in doubt, use release. Device server will then send a remove address command to the mobile device.
MT-L341<;>r MT-L341<;>d
You can also release or dispatch all of the locomotives under control of the current throttle:
MT-*<;>r
After sending this command, the WiThrottle server will typically reply with a removed message
MT-L341<;>
'F' Function
Function keys have two states. Either they're pushed down (when you're pushing with your finger), or they're up (when you release them). Therefore, when you send function commands, you'll send a pair--first when the button is pushed down and second when the button is released. Here is an example of a pair of messages (usually with some time in between them):
MTA*<;>F112 MTA*<;>F012
The first command is when you push F12, and the second is when you release the F12 button. Although this example sends to all of the 'T' throttles, you can send a function to just one by providing the key you used when you added an engine.
Some functions, such as whistle, remain active only while you have the button down, while others, like lights, are toggle. In both cases, your code should send both the push (1) and release (0) and JMRI will map that into the correct DCC command or commands, as appropriate.
'R' Set Direction
Used to change between forward and reverse:
R0 | Reverse |
R1 | Forward. Actually, any value other than 0 after the R is considered forward |
'V' Set Speed
Set the speed to a value between 0 and 126. The format looks something like this:
MTA*<;>V30
If the value received from the WiThrottle server is negative (e.g., during the initialization information when an address is selected), that indicates the address is in Emergency Stop mode.
'S' Steal Locomotive
Requests stealing a locomotive. This is a feature that is of most interest when working with Digitrax command stations and allows WiThrottle to support the same steal scenario supported by Digitrax throttles.
Stealing a loco takes a few steps. Here is the sequence
- Send a normal MT+ command to acquire a locomotive
- WiThrottle sends back an MTS reply, indicating that a steal request is required
- Send a steal request for the locomotive
- Send a release for the locomotive you're stealing
- Send an add locomotive request
Here is an example
From WiThrottle Server (JMRI) | From Client |
---|---|
MT+L41<;>L41 | |
MTSL41<;>L41 | |
MT-L41<;>r | |
MT+L41<;>L41 |
Note: Steal requires JMRI 4.10 or later
Consist Controller Commands
The commands in this section are used to work with consists. All the consist commands start with "RC", which stands for Roster Consist.
- 'P' Change order of locos in the consist
- 'R' Delete (remove) a consist
- '+' Add loco to a consist and/or set the relative direction
- '-' Remove a loco from the consist
- 'F' Program CV21 and CV22 function behavior
'+' Add to Consist
Either creates a consist or adds a locomotive to an existing consist. As an example, let's say that consist S123 does not exist. The following commands will create a consist with named "My consist" with two locos:
RC+<;>S123<;>My consist<:>L40<;>true RC+<;>S123<;>My consist<:>L41<;>true
The parts of this command are as follows:
S123 | The DCC address of the consist |
My consist | The JMRI id (name) of the consist |
L40 | DCC address of the loco to add to the consist |
true | Direction is normal (false for reversed) |
'-' Remove Loco from Consist
Removes a single locomotive from the consist. For example:
RC-<;>S74<:>L40
Removes DCC address L40 from the consist with the DCC address of S74.
'P' Change Position in Consist
This command is used to change the order of locos in a consist. These commands have the following format:
RCP<;>consistAddress<:>leadLoco<;>nextLoco<;>...* ...<;>nextLoco<;>trailLoco
Notice that this has no direction infromation--it just has a list of locomotive DCC addresses. For example:
RCP<;>S74<:>L346<;>L3374
The example sets the position of the locos in the consist with the DCC address of S74 so that DCC L346 is the lead and L3374 is the trailing loco.
'R' Remove Consist
Deletes a consist. For example, the following command deletes the consist with DCC address S122.
RCR<;>S122
Fast Clock Commands
The 'PFT' command is used to synchronize the fast clock time. It is sent periodically from the server, indicating the "now" time and (optionally) the current time ratio. Examples include:
PFT65871<;>4 PFT1550686525<;>4.0 PFT1550686860
The first number is an integer number of seconds since 12:00 midnight, January 1st, 1970. This start date is the Unix time epoch, and makes it easy to convert this value using the standard time libraries. Only the hours and minutes are useful. JMRI sends a value based on the system time when the fast clock was started, so will return a value near the calendar date. The Digitrax LnWI returns values within the range 0-86400 (all the values for January 1, 1970).
The second number, if present, is the current fast time ratio. It may be an integer value (4) or a floating point value (4.0). If this value is 0 (or 0.0), the clock is stopped.
JMRI will update the current fast time every 5 real-time minutes, while the LnWI does so much more frequently.
Unknown commands
Any command that is not recognized by a client should be ignored, with processing continuing after the next newlines.
Observations
The Digitrax LnWI sends multiple instances of the H command as part of the initial information sent after the first command the client sends:
HTDigitrax HtDigitrax LnWi 1.0 Unit=7,Wifi Chnl= 1,Command Station= DCS100
and after a selection attempt when using a long address for what
would be a short address values (say MT+L23<;>L23
) the
response is:
HM error - long address < 127 S23
The LnWI also sends a PFC
command. Its purpose is
unknown at this time.
This is the package/jmri/jmrit/withrottle/Protocol help page