Train layout control software examples

I have added extensive support for RemoteSigns in my private layout control software. If you are considering adding support to layout control software, you may find the examples below useful.

Please also see the SDK which describes the RemoteSign commands.

In the lines below, DC1 characters are displayed as <DC1>. .

On connecting, my program sends these commands:
{NAME}Betreibswerk
{VER}3.7.32
{CH?}


The RemoteSign responds with its name etc...
{NAME}RemoteSign
{VER}2.7.13
{ASPECT}625
{READY}

My program sets up the RemoteSign with 26 rows, analog clock and synchs the time, etc.
{ROWS} 26
{CLEAR}H
{STUCK}0
{CLOCK}F
{CLOCK}A 5
{SOUND}1
{TIME}20:40:43


It then sends the screen layout. This RemoteSign is in fact handling two separate stations.
For each station it displays the name of the station, and column headings
For each track it displays the track number, and if a train is present, the train name
In this example, in the first station (Wilsnack Hbf.), one train is present (SZ Wür) and does not yet have any destination so it displays "NICHT EINSTEIGEN" (= Do not board)
In the second station (Wilsnack Tief) there are trains in both tracks, both also without destinations.

I allocate 8 characters for train names.

{TEXT} 1<DC1>Wilsnack Hbf.<DC1> 5<DC1> 1<DC1> 20<DC1>L<DC1> 65535<DC1> 0<DC1> 200
{TEXT} 2<DC1>Gl<DC1> 6<DC1> 1<DC1> 3<DC1>L<DC1> 12632256<DC1> 0<DC1> 75
{TEXT} 3<DC1>Zug<DC1> 6<DC1> 4<DC1> 8<DC1>L<DC1> 12632256<DC1> 0<DC1> 75
{TEXT} 4<DC1>Von/Nach<DC1> 6<DC1> 13<DC1> 20<DC1>L<DC1> 12632256<DC1> 0<DC1> 75
{ROW} 7<DC1>01<DC1>22833333333833333333333333333333811822422<DC1>0<DC1>0
{ROW} 7<DC1>01 SZ Wür   NICHT EINSTEIGEN     <DC1>22833333333833333333333333333333811822422<DC1>1
{ROW} 8<DC1>02<DC1>22833333333833333333333333333333811822422<DC1>0<DC1>0
{ROW} 8<DC1>02<DC1>22833333333833333333333333333333811822422<DC1>1
{ROW} 9<DC1>03<DC1>22833333333833333333333333333333811822422<DC1>0<DC1>0
{ROW} 9<DC1>03<DC1>22833333333833333333333333333333811822422<DC1>1
{ROW} 10<DC1>04<DC1>22833333333833333333333333333333811822422<DC1>0<DC1>0
{ROW} 10<DC1>04<DC1>22833333333833333333333333333333811822422<DC1>1
{ROW} 11<DC1>05<DC1>22833333333833333333333333333333811822422<DC1>0<DC1>0
{ROW} 11<DC1>05<DC1>22833333333833333333333333333333811822422<DC1>1
{ROW} 12<DC1><DC1><DC1>0<DC1>1
{TEXT} 5<DC1>Wilsnack Tief<DC1> 13<DC1> 1<DC1> 20<DC1>L<DC1> 65535<DC1> 0<DC1> 200
{TEXT} 6<DC1>Gl<DC1> 14<DC1> 1<DC1> 3<DC1>L<DC1> 12632256<DC1> 0<DC1> 75
{TEXT} 7<DC1>Zug<DC1> 14<DC1> 4<DC1> 8<DC1>L<DC1> 12632256<DC1> 0<DC1> 75
{TEXT} 8<DC1>Von/Nach<DC1> 14<DC1> 13<DC1> 20<DC1>L<DC1> 12632256<DC1> 0<DC1> 75
{ROW} 15<DC1>01<DC1>22833333333833333333333333333333811822422<DC1>0<DC1>0
{ROW} 15<DC1>01 S1       NICHT EINSTEIGEN     <DC1>22833333333833333333333333333333811822422<DC1>1
{ROW} 16<DC1>02<DC1>22833333333833333333333333333333811822422<DC1>0<DC1>0
{ROW} 16<DC1>02 S3       NICHT EINSTEIGEN     <DC1>22833333333833333333333333333333811822422<DC1>1
{ROW} 17<DC1><DC1><DC1>0<DC1>1
{ROW} 18<DC1><DC1><DC1>0<DC1>1
{ROW} 19<DC1><DC1><DC1>0<DC1>1
{ROW} 20<DC1><DC1><DC1>0<DC1>1
{ROW} 21<DC1><DC1><DC1>0<DC1>1
{ROW} 22<DC1><DC1><DC1>0<DC1>1
{ROW} 23<DC1><DC1><DC1>0<DC1>1
{ROW} 24<DC1><DC1><DC1>0<DC1>1
{ROW} 25<DC1><DC1><DC1>0<DC1>1
{ROW} 26<DC1><DC1><DC1>0<DC1>1

The RemoteSign responds with the channel data requested earlier, stating that it is a Regular RemoteSign on Ch 1, and Ch 2 has 10 sensors. (A sign will always be channel 1 so you can probably ignore this data initially. People may find the sensor data very useful though for triggering all sorts of stuff very cheaply, but I suggest handling that later!)

{CH}1<DC1>R<DC1> 40<DC1> 48<DC1>RemoteSign 2.7.13
{CH}2<DC1>S<DC1>10

Now, a train gets scheduled to arrive in track 2 of the main station. My program sends one line:
The train name is "ICE 1355", and it is coming from Göppingen, arriving at 20:48
{ROW} 8<DC1>02 ICE 1355 Göppingen            An 20:48<DC1>22833333333833333333333333333333811822422<DC1>1

After it has arrived, and it is scheduled to depart, its destination is displayed:
{ROW} 8<DC1>02 ICE 1355 Köln Hbf.            Ab 20:54<DC1>22833333333833333333333333333333811822422<DC1>1

Once it has left, the row is cleared:
{ROW} 8<DC1>02<DC1>22833333333833333333333333333333811822422<DC1>1


Here is an example of a RemoteSign that is dedicated as a platform sign (Typically a small ESP8266 sign in the actual station)
In this example it is a platform sign for the same track as above, so you can see corresponding details:

This time, it sets up the screen with just 4 rows, and a digital clock:
{ROWS} 4
{CLEAR}H
{STUCK}0
{CLOCK}F
{CLOCK}D
{SOUND}1
{TIME}20:45:38


When the train "ICE 1355" is scheduled, it sends these commands
{CLEAR}
{ROWS}4
{CLOCK}D


It defines the platform number as 2 on reverse video, 200% size
{TEXT}1<DC1>2<DC1>2<DC1>1<DC1>5<DC1>C<DC1> 0<DC1> 16711680<DC1>200

Ensures no scrolling text is active
{SCROLL}0

Displays the arrival time
{TEXT}2<DC1>20:48<DC1>1<DC1>7<DC1>5<DC1>L<DC1> 16777215<DC1>0<DC1>100

Displays the train name
{TEXT}3<DC1>ICE 1355<DC1>2<DC1>7<DC1>9<DC1>L<DC1> 16777215<DC1>0<DC1>100

Displays "from" in German in small text
{TEXT}4<DC1>von<DC1>2<DC1>18<DC1>3<DC1>L<DC1> 16777215<DC1>0<DC1>50

Displays the origin city, centered
{TEXT}5<DC1>Göppingen<DC1>4<DC1>1<DC1>25<DC1>C<DC1> 16777215<DC1>0<DC1>100

If no train is present, (say it just departed), it clears the screen, keeps the platform number in place, and displays an advert in scrolling text:
(The scrolling text is placed on row 3 or 4 randomly)
{CLEAR}
{ROWS}4
{CLOCK}D
{TEXT}1<DC1>2<DC1>2<DC1>1<DC1>5<DC1>C<DC1> 0<DC1> 16711680<DC1>200
{SCROLL} 3<DC1>City-Ticket: Nahverkehr inklusive - jetzt bei Sparpreis und Flexpreis im Fernverkehr.


When my program decides to disconnect from a RemoteSign it sends
{BYE}

Items to note:

  • The states I handle for a train in my layout software are: (Other states may be more appropriate for your software)
  1. no train present
  2. train scheduled to arrive from some origin
  3. train present and scheduled to depart to some destination
  4. train present but not scheduled to go anywhere (route cancelled, or none yet set)
  • I maintain an array of RemoteSigns, sending different commands to each sign as needed. (I initially coded for just one, but found one can have a bunch of them!)
  • For animated rows (with the flap boards) use the mask data to tune the characters that get flipped through, or blank columns.
  • Monitor the events for connections and disconnections so that when a sign connects, you can set up its screen for its intended purpose once. (rows, clock type, etc)
  • I also send a {PING} command periodically in case a RemoteSign disconnected in such a manner that I never got a disconnect event. If it has disconnected, this typically generates an error event that I can use to clean up the disconnection.
  • For RemoteSigns that have been set up, but are not connected, I periodically (60s) try to connect. Sometimes the machine running the RemoteSign has not been powered up.
  • For each RemoteSign, I allow the user to define the IP/host name, a name for that RemoteSign (used in logging, and displaying {MSG} or {ERROR} content), pick lists etc., how many rows to use, the type of clock, (digital/analog/none) and if analog, the clock colors, etc.
  • When adding a new RemoteSign, I also offer a way to locate them on the network, by sending connection request on port 50601 to all the IP addresses on the LAN, when RemoteSigns respond, I put their IP address (and host name) into a list in the UI for the user to pick. (Initially I just provided a place to enter the IP address or hostname!)

Typical structure in (Visual Basic syntax)  I use for each RemoteSign:

Name As String
IPaddress As String
Port As Long ' TCPIP port number
ScreenRows As Byte ' current number of rows in the RemoteSign
Clock As ClockSettingsType
Sensors() As Integer ' index 0 = count.  Sensors available via this RemoteSign
 
' data from other end
HostAppName As String
HostVersion As String
' these fields will be populated with data {CH} the remote device
CH() As RemoteSignChannelType
LastError As String ' last winsock error
 
Dirty As Boolean ' true if this RemoteSign has been edited in the UI and needs to be saved


Typical structure of clock settings:

Analog As Boolean ' analog or digital?
    H24 As Boolean ' 24 hour or not
    FaceColor As Long
    HandColor As Long
    TickColor As Long
    SecondHandColor As Long
    SecondHand As Boolean ' show second hand or not
    Size As Integer ' screen rows
    Speed As Byte ' fast speed. zero or 1 = real time.   2 = 2x etc.
    StartTime As Date ' a time value to force into the clock

Hopefully the layout software provides the events and hooks needed to know when to update the status of a track in a station.

Events

My layout software also has what I call events - these can be almost any sort of action that can be triggered by various things. For example I can play a sound effect, start a route, etc. I also allow RemoteSign events to be defined, so the user can enter any RemoteSign commands they like, for example, if someone wants to display a welcome message for their club they can enter a {SCROLL} command with their desired row number and text. I provide buttons to insert DC1 and DC2 characters. I also allow multiple lines to be defined in a single event, using \r to indicate the line breaks. Before sending any commands to a RemoteSign I replace " \r" with carriage returns. This could be an unsophisticated way of allowing people to define screens that get triggered by other events. This feature allows people to create sound players allowing sound effects to be played on demand (using {PLAY} command)!