The Train Project

This Dezyne example is Terry Dennemans’s submission for the Dezyne Challenge 2016.

The Hardware


A small model HO train trajectory including:

  • 16 IR (IR = Infra‐Red) sensors to detect trains.

  • A Train using RF (RF = Radio Frequency) receiver to receive train commands.

  • One rail road crossing that needs to be protected when a train comes by.

  • Four switches to change track. The switches are controlled by servos.

  • A tunnel with warning lights (Green means Safe, Blinking Green means train is coming, Red means train in Tunnel).

The train has onboard Arduino with RF sender/receiver using RF24 (RF24 = Radio Frequency 2.4GHZ), a led at front and back. 12V power is taken from the track. A motor control for speed and direction is also build into the train.

The main controller is also Arduino based and uses I/O ports to readout IR sensors; Control the switches by using PWM outputs of a servo board to which the servos are connected. Also a servo is used to protect the railroad crossing using a barrier/beam. Some outputs are used to control the red blinking lights of the railroad crossing when it is not safe.

The RF protocol is not modeled in detail; the train commands need to be sent by the main control at the correct moments using RF, taking into account that messages send to the train could fail. The messages are asynchronous because of the RF transmission.

Based on IR detectors we can find out location of train; Based on the location of the train, the train should pause for a while, increase/decrease speed and/or reverse speed. Based on location of the train, the track switches need to be set, so the train will switch to another track; also the railway crossing will have to be (de)activated when train is coming (or passed).

The main control needs to keep track of the (de)activation of the sensors. Based on order of sensor (de)activation the train and its direction can be determined.

The figure below will show an overview of the railroad track, its IR sensors, Switch locations and tracks. The table below explain the IR Sensors and the track colors.


IR Sensors

1 GoingUp

2 InTunnelClimb

3 AfterClimb

4 EndLowTrack2 (= Station)

5 EndLowTrack3 (= Switch4TrackA)

6 GoingDown (= Switch1TrackA)

7 RailWayCrossing

8 Switch1

9 Switch2

10 Switch2TrackA

11 Switch2TrackB (= Switch1TrackB;= Station)

12 Switch3

13 Switch3TrackA

14 Switch3TrackB

15 Switch4

16 Switch4TrackB (= Station)


Blue Upper Track

Green Lower Track 1

Red Lower Track 2

Yellow Lower Track 3

The switches are able to switch to TrackA or TrackB; looking at the IR sensor description you can find the position of the switches. Several scenarios need to be supported by the main control:

  • Train driving on Upper track

  • Train driving on Lower track 1

  • Train driving on Upper track, switching to Lower track1, switching to Upper track and so on (The UpperLowerTrack scenario)

  • Special Lower track scenarios:

    • Train on Lower track 2 going to End Station (EndLowTrack2 IR Sensor to HIGH); Pause for a while, move backward and continue on Lower track 1

    • Train on Lower track 3 going to Park Station (EndLowTrack3 IR Sensor to HIGH); Pause for a while, move backward and continue on Lower track 1

Some further requirements:

  • When train is moving up (GoingUp IR Sensor to HIGH), the train needs to increase speed and the TunnelWarning light should blink green.

  • When train is in tunnel (InTunnelClimb IR sensor to HIGH), the TunnelWarning light should become red.

  • When climb is finished (AfterClimb IR Sensor to HIGH), the speed of the train can become normal again.

  • When train is fully passed the tunnel (AfterClimb IR Sensor to LOW) the TunnelWarning light can become green again.

  • When train is going down (GoingDown IR Sensor to HIGH, or at EndControl2 IR Sensor), speed need to be decreased.

  • The train can go forward and backward, but only for the special Lower track scenarios moving backward is required.

  • When train is nearing the RailRoad crossing (Switch 4 IR Sensor to HIGH), speed should become slow, the Warning lights of the crossing should start blinking and the protection beam needs to go down after a small delay.

  • When train is passing the RailRoad crossing, the speed should be slow.

  • When train is passed (RailwayCrossing IR sensor to LOW), the protection beam goes up and after a while the warning lights should switch off.

  • When running on LowerTrack 1, the train should stop at the railway stations (Switch4TrackB IR Sensor to HIGH, or Switch2TrackB IR Sensor to HIGH), and continue after 10 secs.

  • In UpperLowerTrack1 scenario, the train will switch from UpperTrack to LowerTrack1 by setting Switch3 to TrackB; Or to TrackA when going from LowerTrack1 to UpperTrack. The switch1 needs to be properly set according to the UpperTrack (TrackA) or LowerTrack (TrackB) mode.

  • In Special Lowertrack2 scenario, the train will move backward until it fully passed Switch 2 (Switch2 IR Sensor to LOW); Switch2 can move to TrackB so it will run in LowerTrack1 mode. The train can be moved forward again.

  • In Special Lowertrack23 scenario, the train will move backward until it fully passed Switch 4 (Switch4 IR Sensor to LOW); Switch2 can move to TrackB so it will run in LowerTrack1 mode. The train can be moved forward again.


The system consists of the MainControl that decides what to do based on the client requests (selection of track scenarios) received via the IMainControl interface and the input events coming from the IrSensorsDecoder.

The MainControl will decide when to signal the TrackControlSystem, The RailWayCrossingSystem the TunnelSignalLightSystem and the TrainControl. For this, it also needs a timer for being able to let the train wait for a number of seconds at a certain point of the track (End points or Railway stations).



The MainControl component is, like the name suggests, the main component that controls all actions of the Train System. It uses the IrSensorDecoder via the IIrSensorsDecoder (requires interface) to retrieve IR Sensor triggers to be able to decide what to control based on position of the train. The IrSensor triggers to HIGH when a train is detected. It triggers to LOW when the train is not detected anymore. Together with the selected track scenario the received IR Triggers determine how to control the other parts of the system. Each track scenario has its own state, and for each scenario, the trigger of the IrSensors could have a different meaning or has no meaning at all and can be seen as an trigger to ignore (e.g. when train cannot be at that sensor).

Within this design model, you can find examples of using functions to reuse logic. This way you can avoid duplication of logic. If the reusable logic changes, you only have to modify at one location.

The following picture shows a simulation sequence of the MainControl component for the (start of the) UpperLowerTrack1 scenario. A train is moving, and the simulated IR triggers follow the movement of the train.

As you can imagine, a lot of other scenarios could be simulated, including situations in which IR sensors gets triggered while no train is on that part of the track. A full model should be able to deal with this, the current model could be improved on this (will be done in future extensions when a second train will be introduced).



The IrSensorsDecoder combines 16 IR inputs to 32 output messages used to indicate which sensor got triggered and whether a train was detected (to HIGH trigger) or passed again (to LOW trigger); These messages will be used to decide in the MainControl what to do based on the track scenario selected. The IR sensors are modeled using the

Combining 16 of these inputs in one model results in a very large state space. This means it takes a long time to get the model verified.


The TrackControl knows about the different track scenarios; it provides an interface (ITrackControl) to activate the correct switches.


The four track switches are controlled by the TrackControl; they can be switched to TrackA or TrackB. The output of a TrackSwitch will control a servo. The figure below shows a simulation of switching to lower track1, switching to upper track and switching to lower track 2. You can see that all switches are set to the correct TrackA or TrackB state.

This system shows the usage of multiple instances of the same component.



The railway crossing system takes care of controlling all parts of the railway crossing; the protection beam and the warning lights. The figure below shows the components of the RailWayCrossing system.


The figure below shows a simulation of the RailWayCrossing; it shows the initialization in which the warning lights and protection beam are deactivated. When a train is detected, the warning lights are activated. After a delay (triggered by timeout) the protection beam gets activated;

Now, when train is passed, the protection beam gets deactivated; After a delay (triggered by timeout), the warning lights are deactivated.



The TunnelSignalLight system is shown in the figure below.


First it initializes the system to the safe state in which green led will be active and red led is off.

When a Train is coming, the green led starts blinking; frequency of blinking is determined by timer and simulated by a few timeouts.

When the train is in the tunnel (SetTunnelStop), the red led is activated and blinking stops and green led switches off.

When train passed tunnel, the SetTunnelSafe is called and Green led is activated again (Red is switched off). The figure below shows a simulation of the TunnelSignalLight.



Using TrainControl you can control the Model Train by sending commands to the train using RF.

The TrainControl translates the ITrainControl interface into messages being sent using RF24. This object uses an external ‘helper’ component to make decisions based on the data. This ‘helper’ component implements the IRf24Helper interface; it has a method to check whether a messageId is the same or not;

The idea of this helper interface is simple. A message id can be the same or not; it either returns true or false; based on these possible results, the model checker will verify all possible situations (good and bad weather scenarios). The IRf24Helper interface will be implemented in an external component.

The TrainControl component has implemented a retry mechanism in case no acknowledge of the message is received; since the TrainControl also has the ability to switch of the power of the railway tracks, it can be used to reset the Arduino board in the model train by switching off its power. This is used as a last resort to try to recover communication to the train (sending a reset command would in this case most probably also fail).


The sequence diagram above shows a few attempts to send a message using RF24. After two timeouts, when no message acknowledge has been received, a power switch of/on retry mechanism is used. This should reset the receiving part (The train on the track that uses the power from the track and powers onboard Arduino with RF24 receiver). After power cycle, another SendMessage is send and a corresponding acknowledge is received this time.

The TrainControl design model also shows how to send different messages with different MessageIds and MessageData. It uses constants defined externally.

The TrainControl design model makes use of the blocking keyword. This is used to make the call blocking (will not return) until another event (in this case a messageAck) is received that will result in a reply to the blocking call; You need to use it when you go from synchronous to a‐synchronous communication like done in this model.

The receiver part of the RF24 messages is a model Train that uses power of the track to power its onboard Arduino with RF Sender/receiver. The System is shown below and has the IRF24Node as input (provides).


The TrainRf24Node translates/decodes the RF24 messages into usable commands for the ModelTrainControl which controls the motor of the train and its front and back lights.

The TrainRf24Node again uses a ‘helper’ component for decoding the messages based on the data received. The helpers is used to compare message_id’s and compare message_data.


The TrainRf24Node uses the ITrainControl interface; so this can serve as an example for remoting the ITrainControl interface using RF24 (or another a‐synchronous communication protocol): ITrainControl ⇒ RF24 messages ⇒ ITrainControl. The ModelTrainControl is able to use the ITrainLightsControl interface to control the lights of the train. (Rf24 message ⇒ ITrainLightsControl)

The output of the ModelTrainControl is the motor control (requires IMotorControl) and two digital outputs (requires two IDigitalOutput) for the front and back light.


Use of Dezyne designed models

Although I was not able to use the generated code from the models for my Arduino boards, I still was able to use the knowledge gained, by creating the models and performing the simulations.

Since the Train Track project was also part of a ‘Christmas Winter Village’ displayed during Christmas, part of the mainControl logic was manually programmed in the Arduino to follow the UpperLowerTrack scenario. Unfortunately I had issues with the RF hardware, so I had no possibilities to send commands to the train.

The 2016 Christmas setup was without TrainControl, but RailWayCrossing and TunnelSignalLight and TrackControl where working correctly based on IR Sensor triggers caused by the Train passing. The Train was always driving the same speed. Switches were set automatically so the train would drive the upper track, lower track and upper track again…

While implementing the TrainSystem, you see that reality is always different than theory. In this case the sensors also triggered, while the silver wheels/axels of the model train pass the IR sensors. This means that current model is not sufficient to detect a train correctly; you also have to take into account that multiple low‐to‐high triggers in a short amount of time of the same sensor need to be seen as one train passing by. The Arduino code got modified to deal with this situation. The Dezyne models are not updated for this.

Having the Dezyne models helped me to get the Arduino code right. Using the generated code would have been easier and would have worked when I had used a more powerful hardware platform.


You can download the example in a zip-file from here.

Note that all of the diagrams included in this document have been produced in and exported from Dezyne. The System View and State Charts are part of the Dezyne editor. The Sequence Trace has been produced using the Dezyne simulation engine.

Enjoy this article? Don't forget to share.