While playing around in TIA Portal, I wanted to better understand how an HMI interacts with a PLC. To do this, I built a 4-way traffic light simulator using an S7-1200 PLC and a Siemens Simatic HMI, in which the PLC controls the state machine and the HMI acts as a user interface. This brief walks through how the simulator works and some of the implementation decisions behind it.
Key Takeaways:
- The PLC is responsible for controlling the logic for the entire state machine, while the HMI only sends requests and displays outputs
- UDTs can reduce manual tag creation when multiple objects share the same signal structure
- Having an array of UDT states centralizes output logic, reducing the amount of ladder logic needed per state
- Having a separate output network avoids potential double coil bugs while keeping transition networks as simple as possible
- HMI visuals may not always update fast enough to reflect rapidly changing PLC signals
Overview of the simulator
This simulator was made using a Siemens 1215FC PLC and a Siemens Simatic HMI in TIA Portal v20. The traffic light simulator represents a 4-way intersection in which neither road has priority. This means that the system will rotate between states automatically, and you do not need a user input in order for states to change. Pedestrians will have the ability to walk across the North-South (NS) road and the East-West (EW) road, which are defined as NS pedestrian requests and EW pedestrian requests. For this simulator, we assume that cars will not turn right on red. Traffic may go straight during a walk state, but only on the opposing road to where pedestrians are walking.
In total, there are 10 buttons that send information from the HMI to the PLC. These buttons include:
- 1 Start Simulator button, which will bring you to NS Green state.
- 1 Reset Simulator button, which will bring you back to the default flashing red state
- 8 Pedestrian buttons, with 2 buttons in each corner, which trigger either an NS pedestrian request or an EW pedestrian request
Figure 1: Completed Simulator
All the states in the simulator are defined as follows.
| State | Red NS | Yellow NS | Green NS | Cross NS | Red EW | Yellow EW | Green EW | Cross EW | Next State |
|---|---|---|---|---|---|---|---|---|---|
| Flash Red [0] | Flash | ╳ | ╳ | ╳ | Flash | ╳ | ╳ | ╳ | NS Green |
| NS Green [1] | ╳ | ╳ | ✓ | ╳ | ✓ | ╳ | ╳ | ╳ | NS Yellow |
| NS Yellow [2] | ╳ | ✓ | ╳ | ╳ | ✓ | ╳ | ╳ | ╳ | All Red after NS |
| All Red after NS [3] | ✓ | ╳ | ╳ | ╳ | ✓ | ╳ | ╳ | ╳ | NS Walk OR EW Green |
| NS Walk [4] | ✓ | ╳ | ╳ | ✓ | ╳ | ╳ | ✓ | ╳ | EW Green |
| EW Green [5] | ✓ | ╳ | ╳ | ╳ | ╳ | ╳ | ✓ | ╳ | EW Yellow |
| EW Yellow [6] | ✓ | ╳ | ╳ | ╳ | ╳ | ✓ | ╳ | ╳ | All Red after EW |
| All Red after EW [7] | ✓ | ╳ | ╳ | ╳ | ✓ | ╳ | ╳ | ╳ | EW Walk OR NS Green |
| EW Walk [8] | ╳ | ╳ | ✓ | ╳ | ✓ | ╳ | ╳ | ✓ | NS Green |
The NS Walk state signals that you can cross the NS road, while EW Walk state signals when you can cross the EW road. If a NS pedestrian request is received before the NS traffic lights go into the All Red after NS state, then the state machine will switch to the NS Walk state instead of directly to the EW Green state. This process is the same for the EW pedestrian requests; if a request is received before the All Red after EW state, then the machine will switch to EW Walk instead of NS Green. A request will not automatically make the machine switch to the next state, and the state transitions are instead determined by timers and the previous state up until the All Red after NS/EW states. Essentially, inputting a pedestrian request puts the request in a queue, but the request will not be carried out until all other states are visited first. The general sequence of the simulator goes Green → Yellow → Red → Walk OR Back to Green. Timing assumptions for each state are as follows:
NS/EW Greenstate: hold for 20s, then switch to next stateNS/EW Yellowstate: hold for 5s, then switch to next stateAll Red after NS/EWstate: hold for 1s, then switch to next stateNS/EW Walkstate: hold for 11s, then switch to next stateFlashing Redstate: no timer, holds until Start button is triggered
Creating a Traffic Light Data Type
Each traffic light used the same basic signals: red, yellow, green, crosswalk, and request clear. Instead of making four of the same tag, I made a User Defined Data Type (UDT) to describe the intersection signals. Since the pair of NS traffic lights or EW traffic lights always have the same outputs, my UDT consisted of two structs, NS and EW. These structs consisted of the same five signals:
RedLights: sends a 0 or 1 signal to all red lightsYellowLights: sends a 0 or 1 signal to all yellow lightsGreenLights: sends a 0 or 1 signal to all green lightsCrosswalkSymbols: signals 0 or 1 to all Crosswalk blocksRequest_Clear: signals 0 or 1 if pedestrian request is carried out, clearing the associated pedestrian request Latch
Figure 2: Traffic Light UDT
Creating a States Array
After creating the Traffic Light UDT, I made a data block containing a 9-element array of the "trafficLight" UDT, which allowed me to manually set the logical values of each output in one place. Since the array is in a separate data block, it can be dragged into the traffic light simulator function block, making the logic for all states readily available to be read. All array values were derived using the states table previously shown, with the only signal not mentioned on the table being Request_Clear.
Figure 3: States Array, with an example of defining the logic values of each property
Transition Logic
Since all states had a specific array index, switching between states is not difficult. Inside the function block, multiple integers were added to represent each state’s index, as well as a currentState variable. With that, every state network (9 total) could start with an Equal-to comparator, so that the entered network depends entirely on what state index your currentState value matches up to. Since all outputs are handled in a different network, the state networks typically only consist of a timer which triggers the state, and a move statement to change the currentState value. The All Red after NS/EW were the only states which had two options to go through, and that was where pedestrian requests would be considered.
Note: All States had the option to go back to the Flashing Red state using the reset button. This doesn’t affect the logic and was only used to return to the startup default.
Figure 4: All Red after NS network
Other Networks
In total, there were 4 other networks used in the function, but the only three of them had an effect on the general logic of the state machine. These include the output network, the pedestrian request network, and the flashing network.
Output Network
The output network is where the PLC writes the final HMI display values. An output coil should ideally only be used once throughout a PLC function. Trying to use the same output coil in two places creates the hazard of conflicting logic, and leads to unexpected behavior for that coil. For this network, each output signal gets its own branch, which avoids multiple coil issues and also gives you a place to debug if there seems to be an issue with the output logic. Each output branch references state[#currentState].(NS or EW).(insertProperty) to determine whether the associated light or crosswalk should be active. The rest of the logic is straightforward, with the only thing that had to be accounted for is the Flash signal. This signal is only true during the Flashing Red state, and will trigger the associated network to turn the Flash signal on and off. This is how we are able to get the lights to flash instead of continuously staying on while also keeping the one output coil rule.
Figure 5: Section of Output Logic Network
Pedestrian Request Network
The pedestrian request network is responsible for creating a latch when any of the pedestrian buttons are pressed, since the pedestrian request buttons on the HMI side act as a pushbutton and don’t latch themselves. The latch is cleared based on the Request_Clear signal, which is 0 in all states except for any NS/EW Walk state. When in a Walk state, the signal goes to 1 and breaks the associated latch. NS requests and EW requests each get their own latch, since EW pedestrian requests should not be cleared when a NS clear request is completed and vice versa. Since requests don’t clear until you go into the associated walk state, requests will be latched and stay in ‘queue’ until the simulator is able to switch to their associated walk state.
Figure 6: NS Pedestrian Request Latch
Flashing Network
Initially, instead of making a network, I wanted to use the predefined clock memory bits in the PLC. In my first attempt with a 10 Hz Clock bit, there was a slight delay in getting the PLC information to the HMI in time to keep up with the flashes. This delay resulted in the lights not flashing in the HMI display even though the ladder logic was technically sound. Since I was not sure at the time whether the clock bits were causing the issue, I opted to make my own flashing network with timer blocks. The network also had an Equal-to Comparator in the beginning, so the timers used will only run when in the Flashing Red state. If you do choose to implement a flashing network, keep the preset time of your timers to a minimum of 1 second to avoid the same problem as before. While I took the network option, you could also choose to use a clock bit with a lower frequency, as they will have a longer time gap in seconds than the 10 Hz I attempted to use.
Connecting the HMI to the PLC
While the state machine logic is all controlled by the PLC, if you want your state machine to work at all, you need to connect your HMI buttons and visuals to your function inputs and outputs. All accessible variables made in the state machine end up acting as signals that the HMI can respond to in a multitude of ways. A button or graphic added to an HMI does not start with any predetermined behavior, so you have to manually program how you want any inputs or outputs to behave. All of this can be done in the properties or events section of the selected graphic. As an example, if you wanted to create a push button on your HMI, you have to manipulate the Events section of the desired button graphic. One way to implement this is to use the defined SetTagValue function for the Press and Release events. Press sets the PLC tag to 1. Release sets it back to 0. In order for this to work, you would need a tag to write to. Note that any tag you connect from the PLC will automatically be added to the HMI’s default tag table.
Figure 7: Example of using SetTagValue on a button
PLC Tags could also control the appearance of your buttons or graphics, which is an ability that was utilized a lot in this simulator. In the properties section of an HMI variable, each property has a static value column and a dynamization column. If you select the dynamization section for a certain property, it will take you to a drop-down list of ways you can control your property, one of them being a Tag option. Once Tag is selected, TIA Portal will show you a new window in which you would input all your associated logic. First, you would connect the graphic to a specific PLC tag, and then you would choose the method you would like to program the graphic with. There are four options: None, Range, Multiple bits, and Single Bit. For this project, I only used the Range option. This option gives you a Condition column, where you could input any integer value, followed by columns where you can change the property’s value. This is where you would decide what color you would want the HMI to show when the tag condition is 0 or 1.
Figure 8: Example of using Range to change appearance values
Extra things added to the HMI interface
As a finishing touch to the project, I decided to add a pedestrian walking animation to the HMI interface which triggers during walk states. I use ‘animation’ very liberally here, since it’s more like a picture changing positions each second. While the Siemens TIA Portal supposedly does have their own “Animation” section based on what you can search online, I admittedly could not find this section at first glance, so I used position properties instead. To make the graphic seem like it was moving, you can use Range and manipulate the Position Left and Position Top properties, which act as X and Y coordinates respectively. Since Range only works with integer values, I converted the elapsed walk timer into an integer using another network in the simulator function block, then used that to update the pedestrian’s position.
Figure 9: Example of using Range with Integer values








