WinCC Comfort VBScript: Building Dynamic HMI Tag References from Index Variables
Engineers commissioning parameter-driven HMI screens on S7-1200 and S7-1500 panels frequently need to map a single display field to one of N PLC tags based on a runtime index. The naive string-concatenation approach often returns a blank or stale value because the runtime collection is being indexed with a literal token rather than the constructed name. This reference covers the exact syntax fix, the UDT-element constraint that limits this pattern on WinCC Comfort, and the alternate architectures that scale beyond two or three indices.
Problem Statement
Consider a TIA Portal project with two machines, each owning two format strings exposed as DB elements:
DB_Machine_01.stFormat_1 : STRING[20] = "Vacuum"
DB_Machine_01.stFormat_2 : STRING[20] = "Sealer"
DB_Machine_02.stFormat_1 : STRING[20] = "Nozzle"
DB_Machine_02.stFormat_2 : STRING[20] = "Feeder"
The HMI side mirrors these as global tags DB_Machine_01_stFormat_1 ... DB_Machine_02_stFormat_2. The intended routine receives an index (1 or 2) and writes the resolved string into a read-only display tag value1. The original implementation looks correct at a glance but returns nothing on screen:
Sub Function_1(ByVal index)
Dim a, b, tag
Dim valueTag
a = "DB_Machine_0"
b = "_stFormat_2"
tag = a & index & b ' yields "DB_Machine_02_stFormat_2"
Set valueTag = HMIRuntime.SmartTags("tag")
value1 = valueTag.Value
End Sub
Symptom: the bound output field stays empty, even when index is a valid 1 or 2 and the underlying PLC tag is known to be populated. No runtime error is raised; the field just never updates.
Root Cause
The defect is a single pair of quotes. HMIRuntime.SmartTags accepts a String argument that names the tag to look up. The literal "tag" in the source above passes the four-character word tag to the collection, not the contents of the local VBScript variable tag. There is no HMI tag called tag, so the lookup returns an empty variant. The .Value property on an empty variant is also empty, which is what propagates to the display.
The corrected line removes the quotes so VBScript passes the variable's current string value:
value1 = HMIRuntime.SmartTags(tag)
Two additional defects in the same routine compound the issue and are worth correcting while you are in the file:
| Symptom | Defect | Fix |
|---|---|---|
| Display is always empty |
HMIRuntime.SmartTags("tag") passes the literal token tag
|
Use HMIRuntime.SmartTags(tag) with the variable unquoted |
| Runtime error "Object required" on some scripts |
Set valueTag = HMIRuntime.SmartTags(...) — the collection returns a value, not an object |
Drop Set and the .Value hop; assign directly |
| Index is interpreted as a digit character, not a number | String concatenation works only if the HMI tag is named with the leading zero, e.g. DB_Machine_01_stFormat_2, not DB_Machine_1_stFormat_2
|
Confirm the HMI tag is named byte-for-byte including leading zeros |
WinCC Comfort VBScript Tag Model
WinCC Comfort and WinCC Advanced use the same VBScript engine (cscript-compatible) but a reduced object model compared to WinCC Professional. The two collections the engineer needs are HMIRuntime.Tags and HMIRuntime.SmartTags.
| Collection | Returns | Use for |
|---|---|---|
HMIRuntime.Tags(name) |
Tag object (read with .Read + .Value, write with .Value + .Write) |
Explicit control over Read/Write cycle, access to .QualityCode, .LastError
|
HMIRuntime.SmartTags(name) |
Variant containing the current process value | Cheap inline read or write of an already-linked HMI tag |
The name argument is always a String matching the HMI tag name exactly. Tag-name lookup is case-insensitive on the HMI side, but the match is otherwise exact — including the PLC connection prefix if your tag is configured with one. Mismatches raise runtime error 13 (Type mismatch) or silently return Empty depending on the panel firmware.
SmartTags indirect access for primitive tags. Behaviour for UDT (user data type) element access by constructed name changed in V16; see the UDT section below.
Corrected Script
The minimal fix replaces the Set line and uses the variable directly. Two equivalent forms are shown; pick the one that matches the rest of your project style.
Form A — SmartTags read into a local
Sub Function_1(ByVal index)
Dim tag
tag = "DB_Machine_0" & CStr(index) & "_stFormat_2"
' Validate the constructed name before dereferencing.
If Not HMIRuntime.Tags(tag) Is Nothing Then
value1 = HMIRuntime.SmartTags(tag)
Else
value1 = "n/a"
End If
End Sub
Form B — Tags collection with explicit Read
Sub Function_1(ByVal index)
Dim t, tag
tag = "DB_Machine_0" & CStr(index) & "_stFormat_2"
Set t = HMIRuntime.Tags(tag)
t.Read
If t.QualityCode = 0 Then
value1 = t.Value
Else
value1 = "BAD (" & t.QualityCode & ")"
End If
End Sub
Form B is preferred for production code because it exposes the OPC UA quality code (good = 0, bad = &hC0000000&, uncertain = &h40000000&). On a Comfort Panel, quality code visibility is the cheapest way to catch a tag name typo, a disconnected PLC, or a non-existent DB element before it cascades into a "blank screen" complaint at FAT.
UDT Dynamic Access Constraints
If the HMI tags are not flat STRING globals but elements of an HMI-side UDT (PLC UDT mirrored to a WinCC UDT), there is a second defect class to check against the published Siemens support entry 109813306 — Current system behavior for dynamic access to elements of a user data type.
The relevant behaviours for TIA Portal V15.1 through V18 (WinCC Comfort/Advanced):
| TIA Portal version | SmartTags(name) on UDT element | Tags(name) on UDT element | Recommended pattern |
|---|---|---|---|
| V15.1 | Returns Empty silently for UDT elements | Returns object; .Value works |
Use HMIRuntime.Tags(...).Read
|
| V16 | Works if the UDT element is mapped to a flat HMI tag first | Works | Use the Tags collection with .Read
|
| V17 / V18 | Works for STRING, INT, REAL UDT members; not for nested UDTs | Works | Prefer Tags collection; avoid deeply nested UDTs |
SmartTags on every Comfort firmware as of V18. Flatten to two levels or pivot to the Tags collection with an explicit Read.
For the project in the source, the PLC exposes stFormat_1 and stFormat_2 as STRING members of a DB_Machine_xx block. If those blocks are themselves UDT instances on the HMI side, the V15.1 path will silently return Empty even after the string-construction fix. Move to the Tags collection form to clear the symptom.
Alternative Architectures
String concatenation works for two or three indices, but it scales poorly. Each new machine forces a new HMI tag, a new branch in the script, and a new HMI connection load. The following patterns replace concatenation with structured access and are the recommended approach once N exceeds 4.
2.1 Array tag with index-driven selection
WinCC Professional and Unified support array tags. Comfort does not. If your project is on a Comfort panel (TP700 / TP900 / TP1200 / KP variants), skip this option and use an indexed-DB block instead.
2.2 Indexed DB block with pointer indirection
On the PLC side, build a contiguous block of N format strings:
DATA_BLOCK "DB_Formats"
STRUCT
stFormat : ARRAY[1..16] OF STRING[20];
END_STRUCT
END_DATA_BLOCK
Copy the active machine's format into the array on each format change. On the HMI side, expose DB_Formats.stFormat[1..16] as 16 HMI tags, or as a single array tag if the panel supports it. The script reduces to a fixed offset:
Sub ShowFormat(ByVal index)
Dim t
Set t = HMIRuntime.Tags("DB_Formats_stFormat_" & CStr(index))
t.Read
value1 = t.Value
End Sub
This is the pattern the rest of the Siemens sample library uses for the same use case (format selection on a packaging line). It also makes the HMI connection list flat and audit-friendly.
2.3 Symbolic indirect addressing on the HMI
WinCC Comfort supports a limited form of symbolic indirection: any property or animation bound to a tag name can be redirected at runtime by writing the desired tag name into a configured multiplex tag tied to a multiplexed IO field. The benefit is that the script no longer needs to know the index — the IO field's Tag prefix is bound to the multiplex tag, and the operator selects a base index in a separate field.
Configuration steps (TIA Portal V17 example, TP700 Comfort):
- Create an internal INT tag
IndexMultiplex. - Add an I/O field on the screen and set its Tag prefix property to
DB_Machine_0{IndexMultiplex}_stFormat_. - Add a second I/O field bound to
IndexMultiplex; the operator types 1 or 2. - WinCC resolves the prefix + index + suffix on each refresh and binds the value automatically — no script required.
This is the most maintainable pattern for line HMI screens where the same display field shows the current format for the active machine, but it is limited to two index dimensions and does not work on TP1500 Basic panels.
Indirect Addressing Pattern (Recommended for the Source Project)
For the original two-machine × two-format case, the recommended code is below. It is defensive: it validates that the constructed name resolves to a real HMI tag, reads it through the Tags collection, and reports a quality code on the screen if the PLC link is down.
' VBScript — TP700 Comfort, TIA Portal V17
' Inputs : index (ByVal Integer, 1 or 2)
' Outputs: value1 (HMI internal tag, STRING[20])
Sub ShowActiveFormat(ByVal index)
Dim tagName, t, qc
tagName = "DB_Machine_0" & CStr(index) & "_stFormat_2"
' Defensive lookup — if the name does not exist, HMIRuntime.Tags
' raises error 13 (Type mismatch) on older firmwares.
On Error Resume Next
Set t = HMIRuntime.Tags(tagName)
If Err.Number <> 0 Then
value1 = "ERR tag=" & tagName
Err.Clear
On Error Goto 0
Exit Sub
End If
On Error Goto 0
t.Read
qc = t.QualityCode
If (qc And &HC0000000) = &H80000000 Then
' Bad quality
value1 = "BAD qc=" & Hex(qc)
Else
value1 = t.Value
End If
End Sub
Bind the script to the Change event of an I/O field bound to index, or to a "Show format" button's Click event if you prefer explicit operator action.
Verification
- Set a breakpoint on the
t.Readline. Step through and confirmtagNamecontains the expected HMI tag name byte-for-byte (Watch window). - Confirm the tag exists in the HMI tag table: HMI tags → filter by connection → confirm the DB element is linked, not just declared.
- Force the PLC tag to a known value with the Watch table (e.g. write
"TEST"toDB_Machine_02.stFormat_2) and confirm the HMI display shows the same string after the script runs. - Disconnect the PLC connection (Online → Accessible nodes → uncheck). Run the script with a valid index. Confirm
value1shows the BAD qc=... branch, not a stale value. - Trigger the script with
index = 3(out-of-range). Confirm the ERR tag=... branch fires rather than crashing the panel runtime.
Troubleshooting Matrix
| Symptom | Likely cause | Remedy |
|---|---|---|
| Display always empty, no runtime error | Quoted literal in SmartTags("tag")
|
Drop the quotes; pass the variable |
| Runtime error 13 "Type mismatch" on SmartTags | Tag name not found, or UDT element on V15.1 | Switch to Tags(name).Read; verify the HMI tag exists |
| Value present on first call, empty on second | Variable tag overwritten by a later routine in the same module |
Declare as local Dim inside the sub; do not reuse as module-level |
| Script runs in RT simulation, not on the panel | Tag is configured as "Simulated" only and not linked to a real PLC connection | Re-link the HMI tag to the S7-1200/1500 connection, not to the simulation |
| Display shows garbage characters | STRING length mismatch between PLC and HMI tag (e.g. PLC STRING[20], HMI STRING[10]) | Match lengths; the HMI tag adapts its STRING length to the PLC |
| Display freezes at last good value | Acquisition cycle on the HMI tag is too long (default 1 s, sometimes set to 5 s) | Reduce the HMI tag's acquisition cycle to 100 ms for active format display |
| Display updates in RT but stays empty on TP700 panel | TP700 firmware < V15.1 on the panel — UDT indirect access not supported | Update the panel image to V16 or later, or flatten the UDT to a single level |
Cross-Platform Notes
The exact fix is portable across the WinCC family, with these caveats:
- WinCC Comfort / Advanced (TIA Portal) — VBScript only, no full VBA. The corrected pattern works on every Comfort panel from TP700 to TP2200 running V15.1 or later.
-
WinCC Professional (TIA Portal) — Same VBScript syntax. Can also use the C / VB script IDE for compiled modules; the
HMIRuntime.SmartTagspattern is unchanged. -
WinCC Unified (TIA Portal V17+) — JavaScript replaces VBScript. The pattern is
HMIRuntime.Tags.Tags("name").Read()on the RT side; the collection name is plural and the method isRead(), not a property. The string-concatenation logic is identical. -
WinCC Classic (STEP 7 V5.x) — C scripting or VB scripting against a different runtime. The WinCC Classic
DMOpen/GetTagCharfamily is the equivalent, withGetTagChar(lpszTagName)replacingSmartTags(...).
FAQ
Why does HMIRuntime.SmartTags("tag") return Empty?
The string "tag" is interpreted as the literal name of a tag, not the variable. WinCC looks up a tag called tag, finds nothing, and returns an Empty variant. Pass the variable unquoted (HMIRuntime.SmartTags(tag)) so VBScript substitutes the constructed string before the lookup runs.
Can WinCC Comfort create a STRING array tag to avoid the concatenation script?
No. Comfort and Advanced do not support array tags. Use an indexed DB block of N STRING elements (ARRAY[1..N] OF STRING) on the PLC side and read element N with a fixed-offset script, or use symbolic indirect addressing on the IO field with a multiplex tag.
Does this pattern work for BOOL, INT, and REAL tags, or only STRING?
It works for every primitive type the HMI tag table supports: BOOL, INT, DINT, REAL, WORD, DWORD, and STRING. The VBScript variable receives the value as a Variant; assign or compare it directly without an explicit cast for BOOL, INT, and REAL. Use CInt, CLng, or CDbl only if you intend to perform arithmetic in the script.
What TIA Portal versions are affected by the UDT dynamic access restriction?
TIA Portal V15.1 silently returns Empty for UDT element access via SmartTags. V16 and later resolve correctly for flat UDTs. Nested UDTs (a UDT element that is itself a UDT) are not reliably resolvable on any Comfort firmware through V18. Use the HMIRuntime.Tags(name).Read form for all UDT element access on V15.1. See the Siemens support entry 109813306 for the current matrix.
How do I detect a stale or bad-quality value inside the script?
Read through the Tags collection, not SmartTags, and inspect .QualityCode. A good value returns 0; bad values are negative (bit 31 set, e.g. &hC0000000); uncertain values are positive and below &h80000000. Compare with a bitwise AND against &hC0000000 as shown in the recommended script.