TIA Portal HMI Indexed String in UDT Array S_MOVE Workaround

David Krause12 min read
SiemensTIA PortalTroubleshooting
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

Problem Overview

Engineers using SIMATIC WinCC in TIA Portal frequently encounter a tag-table compiler error when trying to expose an indexed String field that lives inside an Array[*] of a PLC data type (UDT) inside a data block. The tag-table declaration returns a syntax error, the connection diagnostic logs the array element as "not a valid address", and the HMI faceplate or text field cannot bind to the value.

The typical user setup is:

  • S7-1200 (firmware V4.2 or later) or S7-1500 (firmware V1.8 or later) running TIA Portal V16 or later.
  • CPU-side DB "RecipeData" with Array[0..15] of "tRecipe".
  • UDT tRecipe contains a member Name : String[20] alongside numeric fields.
  • WinCC Comfort/Advanced panel or WinCC Professional Runtime trying to display "RecipeData".Recipe[3].Name via an HMI tag.

The HMI rejects the symbolic indexed address even though every other indexed field (Bool, Int, Real) inside the same UDT is accepted without issue.

Symptom signature: The HMI tag editor displays "The address is invalid" or "Data type is not supported for indexed access", while the same string compiled successfully when accessed at a fixed array index outside the UDT wrapper (a plain Array[0..15] of String[20]).

Root Cause: HMI Tag Compiler Restrictions

Three layers of TIA Portal interact when an HMI tag is created:

  1. PLC symbol table resolution. TIA Portal uses the symbolic name to compute the absolute address (e.g., DB2.DBX158.0 BYTE 22). For optimized DBs the address is computed lazily by the compiler.
  2. HMI tag datatype matching. The HMI side of TIA Portal enforces its own tag type system, which is a subset of the PLC type system. WString and Array-of-Struct indirections are allowed only in specific configurations.
  3. HMI tag table driver limits. The S7 HMI driver (used by Comfort Panels and WinCC Runtime Advanced/Professional) does not generate the indirect S7 read request required to dereference a String member inside a UDT-array element when the array index is variable at runtime.

The driver can dereference numeric elements with a variable index because the S7 protocol exposes DB[n].field as DB[n].DBX<offset> with a single indirect read. A String[WLEN] on the other hand requires a length prefix read followed by an arbitrary-length payload read, which the S7 HMI driver only implements for direct (non-indexed, non-indirect) symbolic addresses.

The TIA Portal online help entry for "Permitted data types for HMI tags" lists the following accepted types for WinCC V16/V17/V18 HMI tags under an S7-1500 connection:

PLC type Indexed HMI tag (no UDT) Member inside UDT (no index) Indexed member inside Array of UDT
Bool, Int, DInt, Real, LReal, Word, DWord Supported Supported Supported
String (8-bit) Limited (index 0 only) Supported Rejected
WString (16-bit, S7-1500) Not supported Limited Not supported
Struct (UDT) Not supported Flat members only Not supported
Array of UDT Not supported Flat members only Not supported

The compound failure of all three layers is what surfaces as the HMI tag editor error. The fix must work around the S7 HMI driver by exposing a non-indexed, flat String address that the driver can handle natively.

Diagnostic Workflow

Before changing the program, confirm the failure mode with these steps:

  1. Open the HMI tag table and select HMI Tags > Show > Address Overview. Note the error symbol next to the offending tag.
  2. Right-click the tag and choose Compile > Check Consistency. Capture the exact error code and message from the output window (typically under Compile Output > Tags).
  3. Open the PLC Program Blocks > RecipeData and confirm:
    • The DB has S7_Optimized_Access = TRUE (default on S7-1500).
    • The UDT is referenced through "UDT_name" and not a copy of the structure.
    • The array bound is [0..N] with N ≥ 1.
  4. In the HMI tag table, change the connection from S7-1500 to S7-1200 (and vice versa) to rule out a CPU-family specific restriction.
  5. Try a direct non-indexed access to a string inside the same UDT (e.g., element zero). If the non-indexed form compiles, the failure is confirmed to be the indexed-string-inside-UDT-array combination.

Record the project version: TIA Portal Help > About shows the exact build (for example, V18 Update 2, build 18.2.0.2). Siemens has corrected several string-related tag bugs between V15 and V19, so verifying the exact version is mandatory before applying firmware workarounds.

Solution: S_MOVE Buffer Pattern (the Proven Fix)

The most robust and widely deployed workaround is the S_MOVE mirror-buffer pattern. The PLC writes the currently relevant indexed string into a flat, non-indexed String tag inside an "HMI mirror" DB, and the HMI binds to that flat tag.

Step 1 - Create the Mirror DB

Add a new data block named DB_HMI_Mirror with optimized access enabled. Declare the buffer as a single non-indexed string that matches the source length:

DATA_BLOCK "DB_HMI_Mirror"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
NON_RETAIN
   STRUCT
      SelectedIndex  : Int;            // current selection (-1 = invalid)
      MirrorString   : String[20];     // flat buffer for the HMI
      MirrorLen      : DInt;           // optional current length
   END_STRUCT;
END_DATA_BLOCK

Step 2 - Trigger S_MOVE on Index Change

In the PLC program, use the standard S_MOVE (string move) instruction from the Basic Instructions > Move operations palette. S_MOVE performs a length-correct copy including the S7-1500 length prefix (max 254 bytes) and works in both LAD and FBD. In SCL, the equivalent direct assignment MirrorString := SourceString; uses the same internal copy routine.

// LAD/FBD network
NETWORK 1 // "Select" input from HMI changes
      L     %DB_HMI_Mirror.SelectedIndex
      L     0
      >=I
      A(    ;
            L     %DB_HMI_Mirror.SelectedIndex
            L     15
            <=I
      )     ;
      JCN   END1
      CALL  "S_MOVE"
            IN    := "RecipeData".Recipe[%DB_HMI_Mirror.SelectedIndex].Name
            OUT   := "DB_HMI_Mirror".MirrorString
END1: NOP   0

For SCL programmers, the equivalent block uses simple assignment plus an edge-detect to avoid unnecessary copies:

// FB_StringMirror - SCL
FUNCTION_BLOCK "FB_StringMirror"
VAR
    LastIndex : Int := -2;
END_VAR
BEGIN
    IF "HMI_DB".SelectedIndex <> "HMI_DB".LastIndex THEN
        IF "HMI_DB".SelectedIndex >= 0
           AND "HMI_DB".SelectedIndex <= 15 THEN
            "DB_HMI_Mirror".MirrorString :=
                "RecipeData".Recipe["HMI_DB".SelectedIndex].Name;
            "DB_HMI_Mirror".MirrorLen :=
                LEN("RecipeData".Recipe["HMI_DB".SelectedIndex].Name);
        ELSE
            "DB_HMI_Mirror".MirrorString := '';
            "DB_HMI_Mirror".MirrorLen := 0;
        END_IF;
        "HMI_DB".LastIndex := "HMI_DB".SelectedIndex;
    END_IF;
END_FUNCTION_BLOCK

Call this FB from Main_OB1 or a cyclic OB (typically OB30 or OB35 at 100 ms for HMI refresh).

Step 3 - Bind the HMI Tag to the Flat Buffer

Open the HMI tag table and create a new tag:

Field Value
Name HMI_RecipeName
Connection HMI_Connection_1 (S7-1500)
PLC tag "DB_HMI_Mirror".MirrorString
Data type String[20]
Length 20 (matches source)
Acquisition mode Cyclic continuous
Cycle 500 ms (matches DB load)

Compile the HMI. The error is gone because the tag now points to a flat, non-indexed, non-UDT string address which the S7 HMI driver supports fully.

Step 4 - Handle the Write-Back Path

If the operator must edit the string on the HMI and write it back to the indexed slot, declare a second mirror EditBuffer : String[20]. On the Edit Confirmed event, the PLC reverses the move using the same S_MOVE instruction:

// Write-back using S_MOVE
CALL "S_MOVE"
      IN    := "DB_HMI_Mirror".EditBuffer
      OUT   := "RecipeData".Recipe[%DB_HMI_Mirror.SelectedIndex].Name

Confirm the write by reloading the buffer using the read-network above. On S7-1500 with optimized access the operator can use either absolute or symbolic write access; Siemens Knowledge Base entry 109751784 documents the correct "PUT/GET" access rights configuration.

Alternative Workarounds

Depending on the project constraints, three other patterns are field-proven alternatives to S_MOVE.

Alternative A - Full Mirror Array

Mirror every array element to a parallel flat Array[0..15] of String[20]. The HMI then binds to a fixed index of the mirror array, which the driver accepts (single-level indexed access on a flat array of strings is supported on WinCC V17+). Pros: no PLC logic, no trigger event. Cons: doubles DB footprint and the HMI has to track the full array.

DATA_BLOCK "DB_HMI_MirrorArray"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
NON_RETAIN
   STRUCT
      Name : Array[0..15] of String[20];
   END_STRUCT;
END_DATA_BLOCK

// Initial copy
FOR i := 0 TO 15 DO
    "DB_HMI_MirrorArray".Name[i] := "RecipeData".Recipe[i].Name;
END_FOR;

Alternative B - AT View on Optimized DB

Declare an AT overlay over the optimized array. The AT view can be addressed as a single byte stream, which the HMI driver handles. However, AT-over-String breaks the length semantics, so this approach only works for Char[] array members, not for full String[WLEN] with length prefix. Apply this only when the string is declared as Array[0..19] of Char in the UDT (sometimes called "flat char arrays").

Alternative C - Symbolic Indirect via Standard HMI Driver

WinCC Professional (PC Runtime) supports a limited form of indirect addressing through Multiplex tags and the Tag prefix. Set the HMI tag prefix to "RecipeData".Recipe[i].Name and bind a multiplex variable i to the HMI selection. This works only on WinCC Professional V17+ with PC Runtime; Comfort Panels do not implement multiplex tags.

Alternative D - Split UDT into Parallel Arrays

Restructure the UDT so that strings live in their own array, parallel to the numeric arrays. The HMI then binds two tags (one indexed numeric, one indexed string), which both compile. This is the cleanest long-term solution if the UDT schema is not yet fixed in production.

// Original (problematic)
TYPE "tRecipe"
   STRUCT
      Id    : Int;
      Name  : String[20];
      Value : Real;
   END_STRUCT;
END_TYPE

// Refactored (HMI-friendly)
TYPE "tRecipeNumeric"
   STRUCT
      Id    : Int;
      Value : Real;
   END_STRUCT;
END_TYPE

DATA_BLOCK "RecipeData"
   STRUCT
      Numeric : Array[0..15] of "tRecipeNumeric";
      Names   : Array[0..15] of String[20];
   END_STRUCT;
END_DATA_BLOCK

Performance and Cycle-Time Considerations

The S_MOVE instruction on an S7-1500 CPU 1515-2 PN takes approximately 0.4 µs for a 20-byte string, well below the typical OB1 scan-time budget. For OB1 cycle times of 1 ms this is negligible. For arrays of 100+ elements updated every scan, prefer OB35 at 100 ms with a single S_MOVE per index change rather than copying the whole array every cycle.

Operation S7-1200 CPU 1215C S7-1500 CPU 1515-2 PN S7-1500 CPU 1518-4 PN/DP
S_MOVE 20-byte string ~3 µs ~0.4 µs ~0.05 µs
S_MOVE 254-byte string ~10 µs ~1.2 µs ~0.2 µs
Edge-detect index change ~1 µs ~0.1 µs ~0.02 µs
Full mirror array copy (16 × 20 byte) ~50 µs ~6 µs ~1 µs

Avoid calling S_MOVE for the same index on every OB1 scan. Use a rising-edge trigger (positive edge of SelectedIndex) or a cyclic OB at 100-500 ms to keep load predictable. The standard HMI acquisition cycle is 1 s by default; a 100 ms mirror update is sufficient for operator displays.

Verification Checklist

  1. Compile the PLC project. No warnings about MirrorString length.
  2. Compile the HMI project. The HMI_RecipeName tag should compile with status "No errors".
  3. Download both projects to the CPU and panel.
  4. Go online on the HMI tag with the tag simulator. Change SelectedIndex from 0 to 15 and verify the HMI faceplate text changes accordingly.
  5. Set a breakpoint at the S_MOVE call to confirm the input and output IN/OUT parameters hold the correct data.
  6. Test the empty-string edge case: set SelectedIndex := -1 and confirm the mirror reads ''.
  7. Test the write-back path by editing EditBuffer from the HMI, confirming Recipe[i].Name updated in the source DB, then re-loading the mirror to verify.
  8. Disconnect and reconnect the HMI connection to confirm no "Tag address invalid" alarms appear in the HMI diagnostics view.

Common Errors and Error Messages

Error text in TIA Portal Cause Fix
"The address is invalid" Indexed UDT member string Apply S_MOVE mirror pattern
"Data type is not supported" WString used on S7-1200 Convert to String or use S7-1500
"Inconsistent length of the string" Mirror length differs from source Match the String[WLEN] declaration
"Address area of the tag is outside the process image" Mirror placed in non-process-image area Move mirror into a standard DB with optimized access
"Quality: BAD - Communication error" HMI connection lost or wrong DB number Re-check connection configuration in Devices & Networks
"Value is not updated" S_MOVE not executed in cyclic OB Call FB_StringMirror from OB1 or OB35

Best Practices for HMI String Tags

  • Always declare the mirror string length at least as long as the source String[WLEN]. Truncation is silent on S7-1500 unless the LEN output is monitored.
  • Use a single cyclic OB (OB30 to OB38) for all mirror updates to centralize the refresh and prevent duplicate moves on index chatter.
  • Keep the mirror DB in the same access level (optimized or non-optimized) as the source DB to avoid two-symbol resolution paths.
  • Document the mirror contract in the UDT header comment so future developers understand the indirection.
  • If the HMI frequently writes back to the source, implement a "dirty flag" in the mirror DB to prevent the read direction from overwriting an in-progress edit.
  • On multi-language HMIs, ensure the mirror string supports the widest character set required. WString (16-bit) requires S7-1500 firmware V2.0+ and the HMI driver must be configured for Unicode.
  • Verify after each TIA Portal upgrade (V17 → V18 → V19). Siemens regularly updates the supported-tag matrix; some patterns that failed in V16 may compile cleanly in V19.

FAQ

Why does TIA Portal reject an indexed String inside an Array of UDT for HMI tags?

The S7 HMI driver used by WinCC Comfort, Advanced, and Professional Runtime only generates the indirect S7 read request required for indexed numeric fields. A String member inside a UDT-array element needs a length-prefix read followed by a payload read, which the driver does not generate for indexed symbolic access. The compile-time rejection is therefore a driver limitation, not a PLC limitation.

Is S_MOVE the only way to expose an indexed string from a UDT array to the HMI?

No. S_MOVE is the most portable pattern because it works on every PLC family and every HMI panel. Alternatives include mirroring the entire array into a parallel flat Array of String, refactoring the UDT into parallel numeric and string arrays, or using WinCC Professional multiplex tags. The S_MOVE buffer pattern remains the lowest-risk choice when project timelines do not allow refactoring.

Does the workaround work on S7-1200 firmware V4.2 and later?

Yes. S_MOVE on S7-1200 uses the same internal string copy routine as S7-1500. The mirror DB must use optimized access (default since TIA V14). Note that WString is not supported on S7-1200; stay with standard String (8-bit). Cycle time on S7-1200 CPU 1215C is roughly 3 µs per S_MOVE for a 20-byte string.

How often should the mirror be refreshed?

Refresh the mirror on a positive edge of the selection index, not on every OB1 scan. A cyclic OB30 at 100 ms combined with an edge-detect is the standard configuration for operator panels. Avoid sub-50 ms refresh on Comfort Panels; the HMI acquisition cycle is typically 500 ms to 1 s and faster mirror updates waste CPU bandwidth without improving display latency.

What happens if the source string is longer than the mirror buffer?

S_MOVE silently truncates to the destination length on S7-1500. If the mirror is shorter than the source, declare the mirror with the same String[WLEN] length as the source UDT member and monitor MirrorLen on the HMI as a separate Int tag. To prevent silent truncation, add a length check before the S_MOVE call: IF LEN(Source) <= WLEN THEN S_MOVE(...).

Back to blog