Resolving VBScript SmartTags Access to Struct Array Elements

David Krause10 min read
HMI / SCADASiemensTroubleshooting
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

Overview

WinCC Professional and WinCC Runtime Advanced (TIA Portal) expose the entire PLC tag namespace to VBScript through the SmartTags object. When the underlying tag is a structured PLC data type (UDT / DB of type STRUCT) and that structure is part of an array, the string passed to SmartTags() must follow a strict delimiter rule: no whitespace is allowed between the tag prefix, the bracket block, the dot operator, and the member name. A single space inside the resolved string is enough to make WinCC return runtime error 14h / "Invalid procedure call or argument" or "Tag not found".

This reference documents the exact syntax, the supported iteration pattern, the WinCC versions where it applies, the configuration prerequisites on the PLC side, and a verification procedure that catches the failure before going to production.

Scope: This article targets WinCC Professional V13/V14/V15/V15.1/V16/V17/V18 scripting (TIA Portal) and WinCC Runtime Advanced V13-V18 on Comfort/Comfort Plus panels and PC Runtime. WinCC Unified (V16+) uses the JavaScript-based Global JS API (Tags(), HMIRuntime) and does not use SmartTags.

Problem Definition: SmartTags Struct Array Access Failure

A standard WinCC HMI tag is bound to a PLC tag of type ARRAY[0..n] OF "UDT_Segment" where UDT_Segment contains at least the Boolean field IsEnabled. The following naive VBScript returns a runtime error when i = 0 and silently returns wrong values (or Empty) at other indices:

' ❌ INCORRECT - spaces are tolerated by the editor but rejected by the runtime parser
Dim bValue
bValue = SmartTags("WorkingData.Segment [" & i &"] . IsEnabled")

The same tag accessed with a hard-coded index works, which proves the binding, the PLC datatype, and the HMI tag are configured correctly:

' ✔ Works, but cannot be parameterized
Dim bValue
bValue = SmartTags("WorkingData.Segment[0].IsEnabled")

The discrepancy is the whitespace introduced by the string concatenation. The WinCC tag resolver performs a strict string match against the tag name collected from the configured connections; any inserted space (" ", tab, line break) causes the lookup to fail because the configured tag name in the project tree never contains spaces around the brackets or the dot.

Root Cause: WinCC Tag Name String Parsing Rules

When the HMI engineering station generates the tag database from the PLC, the qualified name of a struct-member-array element is built from the concatenation:

<DB name>.<STRUCT name>[<index>].<MEMBER name>

The parser tokenises on:

  • the dot character . — only ONE dot character between identifiers, no leading or trailing dot
  • square brackets [ and ] — must be adjacent to the identifier (no space before the open bracket, no space after the close bracket)
  • no whitespace anywhere in the resolved string

The variant in the question that failed — SmartTags("WorkingData.Segment [" & i &"] . IsEnabled") — produces the literal string WorkingData.Segment [0] . IsEnabled after concatenation. WinCC cannot find a tag whose name contains "Segment [0]" with surrounding spaces and therefore throws the lookup error.

Correct VBScript Syntax for Struct Array Access

The accepted form removes every space that does not belong to an identifier. Brackets, dots, and the dynamic index are concatenated directly to the surrounding strings:

' ✔ CORRECT - no whitespace adjacent to brackets or dots
Dim i, bValue
i = 3
bValue = SmartTags("WorkingData.Segment[" & i & "].IsEnabled")

' ✔ Equivalent using a local variable for the tag prefix
Dim sTag, sPrefix
sPrefix = "WorkingData.Segment["
sTag = sPrefix & i & "].IsEnabled"
bValue = SmartTags(sTag)

For multi-character member names the same rule applies:

SmartTags("RecipeData.Ingredients[" & idx & "].ConcentrationPct")
SmartTags("AxisData.Position[" & axisNo & "].Actual_mm")

If a string variable is assembled from user input, validate it against the configured array bounds before calling SmartTags — the runtime does not bounds-check, and an out-of-range index will raise Error 424h ("Object required") rather than Nothing:

If idx >= 0 And idx <= UBound_Segment Then
    bValue = SmartTags("WorkingData.Segment[" & idx & "].IsEnabled")
End If

Iterating Struct Arrays in a For Loop

The canonical pattern is a For loop with the array lower/upper bound read once into local variables. Two boundary strategies are common:

  1. Hard-coded bounds (preferred for fixed-size recipe / segment arrays)
  2. Dynamic bounds read from a separate configuration tag (preferred when the array is resized at runtime)
' Strategy 1: fixed array [0..9] of UDT_Segment
Dim i, bEnabled, sName
For i = 0 To 9
    bEnabled = SmartTags("WorkingData.Segment[" & i & "].IsEnabled")
    If bEnabled = True Then
        sName = SmartTags("WorkingData.Segment[" & i & "].Label")
        ' ... process element i ...
    End If
Next

' Strategy 2: dynamic bounds
Dim iLow, iHigh, k
iLow  = SmartTags("WorkingData.LoIndex")
iHigh = SmartTags("WorkingData.HiIndex")
For k = iLow To iHigh
    SmartTags("WorkingData.Segment[" & k & "].Processed") = True
Next
Cache the tag string template in a variable when the loop iterates more than ~50 times. Each SmartTags() call performs a tag database lookup; the cost of string concatenation is negligible, but the lookup is not. For very large arrays (1000+ elements) batch through a configured raw byte buffer or use SmartTags("MyArray") as a Variant() and read UBound from the returned array.

Reading the Whole Array as a Variant

If the HMI connection supports array-of-struct read, a single call returns the entire array:

Dim vArray, i
vArray = SmartTags("WorkingData.Segment")
' vArray is a VBScript array of Variants; each element is a Dictionary-like
' object (or a struct wrapper) whose members can be read with member access.
For i = LBound(vArray) To UBound(vArray)
    If vArray(i).IsEnabled = True Then
        Trace "Element " & i & " label = " & vArray(i).Label
    End If
Next

Array-of-struct read is supported on S7-1200/1500 connections in WinCC Professional V14+ and on PC Runtime with the appropriate HMI tag configuration. See the TIA Portal Help on "Reading array tags" and "Structured data types".

Multi-Dimensional and Nested Struct Arrays

For a 2-D array, brackets chain with no spaces:

SmartTags("Matrix[" & row & "][" & col & "].Value")
SmartTags("Grid[" & x & "," & y & "].State")   ' also valid

For nested UDTs (a UDT that contains another UDT as a member), the dot chain extends naturally and every dot must be free of whitespace:

SmartTags("JobData.Header[" & i & "].Operator.Name")
SmartTags("JobData.Header[" & i & "].Operator.Role")

Maximum nesting depth in WinCC is platform-dependent:

Runtime Max UDT nesting Max array rank
WinCC RT Advanced (Comfort Panel) 6 levels 6 dimensions
WinCC RT Professional (PC) 8 levels 6 dimensions
WinCC Unified (V17+) 8 levels (JS API) 6 dimensions

PLC Data Type (UDT) Configuration Requirements

Before the HMI can address a struct-array element, the PLC project must expose a data block whose declaration matches the WinCC tag binding. The minimum S7-1500 declaration for the example:

TYPE UDT_Segment
    STRUCT
        IsEnabled : Bool;
        Label     : String[32];
        Processed : Bool;
    END_STRUCT;
END_TYPE

DATA_BLOCK WorkingData
    STRUCT
        Segment : ARRAY[0..9] OF UDT_Segment;
        LoIndex : Int;
        HiIndex : Int;
    END_STRUCT;
BEGIN
END_DATA_BLOCK

On the HMI side, configure the connection to "HMI tags of the S7-1500". Add a tag named WorkingData of data type Struct with sub-tags Segment[0..9] (also Struct) and members IsEnabled (Bool), Label (WString), Processed (Bool). The HMI tag browser must show the fully qualified name WorkingData.Segment[0].IsEnabled — if the dot is missing, the PLC connection was not rebuilt after the UDT changed.

Common configuration mistakes that look like the same error

  • The DB is non-optimised on S7-300/400 but the connection expects symbolic names (or vice-versa for S7-1200/1500, which require optimised block access).
  • The HMI connection is configured for "absolute addressing" while the script uses the symbolic name.
  • The UDT was modified in STEP 7 but the HMI was not recompiled — the tag browser shows stale members.
  • "Permit access with PUT/GET communication" is disabled on the S7-1200/1500 CPU properties, blocking the HMI from reading the array.

Common Error Patterns and Anti-Patterns

Pattern Result Why
"Segment [" & i & "]" Error Spaces around brackets
"Segment[" & i & "]. IsEnabled" Error Space after dot
"Segment(" & i & ")" Error Round brackets, not square
"segment[" & i & "].isenabled" Error Case mismatch on S7-1500 (case-sensitive)
"Segment[" & i & "]." & sMem Error if sMem is empty Trailing dot fails the parser
"Segment[" & i & "].IsEnabled" OK Reference implementation
Case sensitivity: S7-1200/1500 symbolic tag names are case-sensitive in WinCC V14+. Confirm the case in the HMI tag browser exactly matches the PLC declaration.

WinCC Version Compatibility Matrix

TIA Portal WinCC Script SmartTags struct-array support Notes
V13 / V13 SP1 VBScript Yes Original behaviour, struct-array access via dot+bracket
V14 / V14 SP1 VBScript Yes Optimised block access required for S7-1500
V15 / V15.1 VBScript Yes Performance improvement for array read
V16 VBScript + JS (Unified) Yes (Classic) / separate API (Unified) First Unified release
V17 VBScript + JS Yes Unified ODK improvements
V18 VBScript + JS Yes Last VBScript-centric release line

For projects on WinCC Unified (Comfort panels v18+ and Unified PC Runtime), switch to the Tags() / HMIRuntime JavaScript API; SmartTags is not available in Unified.

Verification and Testing Procedure

  1. Open the HMI tag browser in TIA Portal and confirm the fully qualified name WorkingData.Segment[0].IsEnabled is listed (no spaces).
  2. Add a temporary "Diagnostics" screen with a 10-element table bound to WorkingData.Segment[0..9].IsEnabled. The table should populate from the live PLC before any script runs.
  3. Create a test script that reads Segment[0].IsEnabled with the static string and again with the dynamic string for i = 0..9. Compare to the table values; both must match for every index.
  4. Force one PLC value (e.g. Segment[3].IsEnabled = TRUE) from a watch table and confirm the script reads True.
  5. Run the script with OnError Resume Next during commissioning to log the exact failing string; replace with explicit error handling before deployment.
Dim sTag, v
On Error Resume Next
For i = 0 To 9
    sTag = "WorkingData.Segment[" & i & "].IsEnabled"
    v = SmartTags(sTag)
    If Err.Number <> 0 Then
        Trace "Lookup failed for: '" & sTag & "' Err=" & Hex(Err.Number)
    End If
Next
On Error Goto 0

Performance and Best Practices

  • Hoist the constant prefix out of the loop: sPrefix = "WorkingData.Segment[" and concatenate only the index + member suffix per iteration.
  • Avoid SmartTags calls inside Timer events fired faster than 250 ms — batch the work and cache the array bounds.
  • Prefer reading the whole array as a Variant when the connection is symbolic (S7-1500) — one round-trip vs. n round-trips.
  • Use WString on S7-1500 instead of String to avoid codepage conversion; SmartTags round-trips Unicode cleanly only for WString.
  • Document the array lower bound in a constant tag (e.g. WorkingData.LoIndex) so the script can read it instead of hard-coding 0.

Troubleshooting Matrix

Symptom Likely cause Fix
"Invalid procedure call or argument" for every index Whitespace in tag string Strip spaces around brackets and dots
Works for index 0, errors for others Loop variable scope, or array lower bound is 1 Set Option Base 1 or use LBound
Reads Empty but no error Tag access right disabled on CPU Enable "Permit access with PUT/GET"
Error after PLC UDT change HMI project not recompiled Right-click PLC > "Compile and download"; recompile HMI
Tag browser shows red entry Connection lost, wrong subnet, or wrong CPU selected Verify S7 connection / PROFINET name
Case mismatch error after migration Symbolic name changed case Match case to PLC declaration exactly

Frequently Asked Questions

Why does SmartTags fail with a hard-coded index but succeed with the same index in a loop variable?

The hard-coded form has no whitespace, while "Segment [" & i & "]. IsEnabled" introduces spaces around the brackets and dot. Strip the spaces: SmartTags("WorkingData.Segment[" & i & "].IsEnabled").

Can I use round brackets like Segment(i) instead of Segment[i]?

No. The WinCC SmartTags resolver requires square brackets. Round brackets are VBScript function-call syntax and cause "Object required" or "Wrong number of arguments" errors.

Are the WinCC tag names case-sensitive when the PLC is an S7-1500?

Yes, from TIA Portal V14 onward the S7-1500 symbolic name is case-sensitive in WinCC scripting. The HMI tag browser shows the canonical case; match it exactly in your VBScript strings.

How do I read the whole array in one call instead of looping?

Use vArray = SmartTags("WorkingData.Segment"). The returned Variant is a VBScript array whose elements expose the struct members directly: vArray(0).IsEnabled. This is supported on symbolic S7-1200/1500 connections in WinCC V14+.

Does the same syntax work in WinCC Unified?

No. WinCC Unified uses a JavaScript API; SmartTags is not available. The Unified equivalent is Tags("WorkingData.Segment[" + i + "].IsEnabled").Read().

What is the maximum nesting depth of a UDT inside a script-accessible tag?

WinCC RT Advanced supports 6 levels of nested UDTs; WinCC RT Professional and Unified support 8. Each level adds one dot chain, all of which must be whitespace-free.

Back to blog