Beckhoff TwinCAT HMI provides a responsive engineering design environment for building industrial web-based user interfaces. Because it is built on standard HTML5 technologies, TwinCAT HMI applications can run in any modern web browser. Its flexible architecture and extensive built‑in feature set make it well-suited for everything from simple operator panels to complex, scalable HMI systems. In this article, we will go over basic project setup for TwinCAT HMI by walking through a simple project.
Prerequisites
- TwinCAT XAE installed
- TwinCAT HMI installed
- PLC Project built and running (see bonus information for the PLC Code used in this example)
Project Setup
Creating the Project
With the prerequisites checked off, we can start the basic project setup for the HMI. Begin by right-clicking the solution in the Solution Explorer project tree and selecting Add > New Project. When the Add New Project window launches, navigate to TwinCAT HMI > TwinCAT HMI Project and name it whatever you like.


Linking Tags
The first step in setting up a TwinCAT HMI project is linking PLC symbols to the user interface. This is done by exposing symbols to the TwinCAT HMI Server and binding them directly to HMI control properties.
Navigate to TwinCAT HMI > Windows > TwinCAT HMI Configuration.
From TwinCAT HMI Configuration, navigate to All symbols > ADS > PLC, and wherever you have your variables defined, in my case ‘MAIN’.
Note: If ADS is not appearing, check your ADS Route (review the bonus information to learn how).
From here, you can right-click on a variable and hit ‘Map Symbol’. You can also simply double-click on a symbol to map it. We have now successfully pulled the PLC data into the HMI Server.
Now, we can bind these tags to controls in the HMI screen. Navigate to ‘Desktop.view’. This is the main screen on which we will develop.


Creating Controls
On the right pane, we have properties and the toolbox. These are used to add and edit controls within the project. We’ll start by dragging a Button into the project.
After your component is added, you can then select it to edit the Properties tab. For a basic button, we will want to create a data binding for the state symbol. Click the square to the right of the text field to link tags and select ‘Create data binding’.

This opens the symbol browser where you can select mapped PLC variables. On my screen, some variable groups include ‘ADS’, ‘PLC1’, ‘Global_Version’, and ‘MAIN’. Within the ‘MAIN’ category, we’ll select the bRun variable. It is worth noting that you can also map symbols from this screen using the same method as in HMI configuration.
You should now see the name for that symbol populated in the ‘StateSymbol’ box. You can also hover over property names to see a description of what they do. Now, we will drag in an Ellipse to show how the Events tab works.

With your ellipse selected, navigate over to the Events tab indicated by the lightning bolt symbol. From here, you can see that there are many built-in events to configure, like .onMouseDoubleClick, .onMouseEnter, .onMouseLeave, etc. You also have the option to configure a custom event.
Click the small square next to the Custom field to map a symbol to this control. In our case, we will select ‘bState’. Once that symbol is mapped, click the pencil icon to open and edit the custom event associated with the control.
In the Events tab, you will see a drag-and-drop interface that allows you to define logic for the controller’s behavior. This visual workflow makes it easy to configure interactions without writing traditional code. For this example, we will use a Condition block. Drag the condition into the steps section, then begin populating the auto-generated fields.

Here, we want to check whether the boolean variable bRun is set to ‘True’. If the condition evaluates to true, we will update the ellipse’s appearance to indicate an active state. To do this, drag the FillColor property into both the ‘then’ and ‘else’ branches. Set the ‘then’ color to green to represent a running state, and the ‘else’ color to grey to indicate that the system is not running.

Testing in Live View
After configuring this logic, it’s also important to establish your screen’s behavior. Return to the Desktop.View, where you will see a set of controls on the right-hand side. Click the ‘L’ button to launch the live view.
As you interact with the interface, observe the ellipse once again. When the button is pressed and held, the condition should evaluate to true, and the circle should toggle from red to green in real time. This confirms that your event logic is working as expected and that the control is properly responding to changes in the mapped variable.
User Controls
As your HMI projects grow in size and complexity, maintaining reusability and consistency becomes increasingly important. TwinCAT HMI addresses this need through User Controls, which are reusable templates that combine multiple HMI elements into configurable, modular components.
To get started, create a new folder in your project called ‘UserControls’. This helps keep your project organized as your library of reusable components expands. Within this folder, add a new item and select ‘User Control’. From here, you can begin building a custom control that can be reused across screens, reducing development time and ensuring a consistent user interface.


Once again, on the right‑hand side of the screen, you’ll see a set of small buttons. Selecting the ‘P’ button opens the User Control parameters. These parameters are configurable values that are passed into each instance of the User Control, allowing multiple instances to be created without modifying the control’s internal layout, grouping, or logic.

We are going to create two parameters:
- One for the HMI_PbLight struct
- One String for the name of the component
Note: If your struct does not show up, try closing parameters, adding a button to your user control, and linking HMI_PbLight. Return to the parameters and the datatype should now appear as PLC1.HMI_PbLight, with String for sName.

With the User Control parameters defined, we can now build the control in much the same way as before, with one key difference: instead of binding directly to PLC symbols, we bind each element to properties exposed by the User Control. The pushbutton is configured so its state and behavior reference the bRun member of the HMI_PbLight parameter, allowing each instance of the control to respond to its own data source.
In the same way, the ellipse used as a status indicator is bound to the bState variable within the same struct, and simple logic is applied to drive its fill color, green when the run condition is true and red when it is false. Finally, we’ll add a text element and bind its Text property to the sName string parameter, enabling each instance to display a unique label.

Notice that none of the elements inside the User Control are bound directly to PLC symbols. Instead, each element references the variables exposed by the HMI_PbLight struct parameter. This indirection is what makes the control reusable.

After saving the User Control, switch back to Desktop.View and you will see it appear in the toolbox alongside the standard HMI elements. From there, drag the control onto a screen, bind the HMI_PbLight parameter to a specific instance of the struct, and assign a name. Boom! With that single step, the control is fully configured, and you can now drop in as many instances as needed while maintaining consistent behavior, appearance, and logic across your HMI.
Bonus Information
Adding a Combobox
This section introduces a Combobox that allows the operator to change the light’s color behavior within the User Control. Rather than hard-coding the color logic, the Combobox provides a simple, configurable way to select between predefined color states at runtime.

To add the Combobox, navigate to SrcData, click the three dots on the right-hand side, and create a new data source that will hold the selected value. This data source is then bound to the Combobox and referenced by the User Control logic, allowing the selected option to drive how the indicator is displayed. By handling the color selection this way, the control remains flexible and reusable while giving each instance the ability to behave differently based on user input.
Next, define the selectable color options for the Combobox. Add three entries labeled Green, Yellow, and Red, and assign each one a corresponding hex color value in the Value category. These values will be used directly by the User Control logic to drive the appearance of the status indicator.
Once the options are in place, navigate back to the ellipse inside the User Control and update its custom action. For the bRun true condition, set the fill color using a function binding that references the Combobox selection: ({ color: %ctrl%TcHmiCombobox_1::SelectedValue%/ctrl% })
This binding allows the ellipse color to be determined dynamically based on the selected Combobox value rather than fixed logic. With that complete, you can add additional instances of the User Control to your screen, bind each one to a different HMI_PbLight [HO16.1] struct instance, and select the desired color from the Combobox. Each control now behaves independently, while still leveraging the same reusable design.



Basic PLC Project
This section walks through setting up a simple PLC project to support a basic pushbutton and light, which will later be consumed by the TwinCAT HMI. Start by creating a new solution and selecting ‘TwinCAT XAE Project’. This initializes the TwinCAT environment and provides the framework needed to add PLC logic and HMI communication.

Once the project is created, navigate to the Solution Explorer, right-click on PLC, and add a new PLC project. This creates the runtime environment where the control logic will live. With the PLC project in place, we can define the shared data structure used by the HMI. Right-click on DUTs, select Add > DUT, and choose Structure. Name this structure HMI_PbLight and add two boolean members: bRun to represent the pushbutton command, and bState to represent the resulting status of the light.
Next, navigate to POUs > MAIN (PRG) and declare an instance of the HMI_PbLight struct along with any supporting variables. Add simple logic that drives bState based on the value of bRun, mimicking the behavior of a pushbutton controlling a status light. This keeps the PLC logic intentionally straightforward while clearly demonstrating the data flow between the PLC and HMI.
With the code in place, activate the configuration and log in to the PLC. If you are running TwinCAT on a local host, be sure to execute the required .bat script before logging in. At this point, the PLC is ready, and the HMI_PbLight structure can be bound to the HMI elements and User Controls created in the previous sections.
Updating to User Mode Runtime ADS Route

This final step ensures that the TwinCAT HMI Server is communicating with the correct runtime by updating the ADS route to point to the User Mode Runtime. Begin by opening the TwinCAT HMI Server configuration by going to TwinCAT HMI > Windows > TwinCAT Server Configuration. From there, navigate to ADS > Runtimes > AMSNetId. This setting defines which TwinCAT runtime the HMI will connect to for symbol access and data exchange.
Verify that the AMSNetId is set to the AmsNetId of your User Mode runtime, if that is the runtime you are using.

This is especially important when running TwinCAT locally or when multiple runtimes are present, as an incorrect AMSNetId will prevent the HMI from resolving PLC symbols and binding data correctly.
Once the AMSNetId is confirmed, the HMI and PLC are fully aligned, allowing the User Controls and bindings configured in the previous sections to function as expected at runtime.
Conclusion
This tutorial walked through the foundational steps required to build a simple, reusable TwinCAT HMI project, starting with PLC structure design and basic HMI bindings, then expanding into User Controls and runtime configuration. By introducing the HMI_PbLight struct and gradually abstracting symbol bindings into User Control parameters, we demonstrated how to move from a single hard-coded screen toward a scalable, modular HMI architecture. The use of events, visual logic blocks, and configurable parameters highlights how TwinCAT HMI enables powerful interaction design without requiring custom scripting.
By the end of this project, you should have a working HMI that connects cleanly to a PLC, supports multiple instances of a reusable control, and allows runtime configuration via simple UI elements such as a Combobox. These same concepts scale directly to larger applications, where consistency, maintainability, and development speed become critical. Whether you are building a small operator interface or a plant-wide visualization system, following these patterns will help you create HMIs that are flexible, readable, and easy to extend.
Have an upcoming project? DMC can help you take the next step.
Take your project to the next level with engineering solutions from DMC. Learn more about our Beckhoff programming expertise and contact us to get started today!







