Resolving Siemens S7 STRING Header Errors from AVEVA InTouch Tags

David Krause14 min read
HMI ProgrammingSiemensTroubleshooting
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

1. Problem Overview

When an AVEVA InTouch (formerly Wonderware InTouch) HMI tag bound to a Siemens S7 STRING is read by an S7-300, S7-400, S7-1200, or S7-1500 PLC, the data arrives as a raw character array without the 2-byte header (maximum length + actual length) that the S7 STRING data type requires. The result is that any of the standard IEC blocks that operate on a STRING — FC LEN, FC LEFT, FC RIGHT, FC DELETE, FC INSERT, FC FIND, FC CONCAT, FC REPLACE — return LEN = 0 and fail to operate on the text. Strings entered manually via PLCSIM work correctly because the simulator writes the complete STRING structure, including the header bytes, but the I/O driver (DASServer, DASIDirect, SuiteLink/IO, or OPC tunnel) used by InTouch strips the header when transferring the STRING to the HMI memory and does not re-inject it on the return path.

This article documents the three production-proven fixes, the underlying S7 STRING memory model, a tag-mapping table for both STEP 7 V5.x and TIA Portal, a verification procedure you can run on a live plant or a simulator bench, and a decision matrix to choose the correct path for your project.

2. S7 STRING Data Type Architecture

The Siemens S7 STRING is not a NULL-terminated C-style character array. It is a packed structure that occupies up to 256 bytes in a data block, instance DB, or memory area. The classic STEP 7 V5.x layout (S7-300/S7-400) is:

Byte Offset Contents Range / Notes
+0 Maximum string length 1 to 254 (BYTE). Set by the STRING[N] declaration.
+1 Actual / current string length 0 to value at byte 0. Maintained by the runtime or by user code.
+2 to +(N+1) ASCII character payload Holds the text. Not necessarily NULL terminated.
+(N+2) to +255 Undefined Filled with 16#00 by PLCSIM; undefined in arbitrary drivers.

For an S7 STRING[20] the structure occupies 22 bytes: 2 header bytes + 20 character bytes. For an S7 STRING[254] it occupies 256 bytes. The IEC 61131-3 STRING standard uses a different convention (length as a separate variable, NULL terminator) so block portability across platforms requires care. On S7-1500 the STRING data type preserves the same 1+1+N header format; WSTRING is the 2+2+N variant and is outside the scope of an InTouch DASServer tag.

S7 STRING[20] memory layout (22 bytes total) MAX=20 LEN=N 'H' 'e' 'l' 'l' 'o' ' ' 'W' 'o' 'r' 'l' 'd' ... Byte 0 Byte 1 Bytes 2 .. N+1 (character payload) Header (2 bytes) + payload (N bytes)

3. Root Cause: Why the 2-Byte Header Is Missing

InTouch's access name for Siemens S7 PLCs (DASIDirect, DASServer, or the I/O server bundled with InTouch) uses the OPC/DA tunnel to exchange data with the PLC. The default DB item syntax in InTouch WindowMaker for a Siemens STRING is:

DB<number>,STRING<byte_offset>,<length>

For example, DB15,STRING232,20 references data block 15, byte offset 232, with 20 characters of payload. The driver reads bytes 232 through 251 from the DB and writes them to the InTouch tag's memory as a character array. It does not synthesize a header on the way out and it does not write one on the way in.

When the same tag is written back to the PLC (operator entry on a string input object), the driver sends 20 bytes of characters to byte offset 232 of DB15. The two header bytes at offsets 230 and 231 of DB15 are never touched. As a result, when the S7 user program reads the same data block area and tries to interpret it as a STRING, the actual-length byte (offset 231) still holds whatever value the program last wrote there (often 0 or a stale value from a previous run). The IEC FC LEN reads this byte, sees 0, and reports an empty string — even though the payload is full of valid characters.

Some third-party OPC servers for InTouch synthesize a length prefix on read but not on write. Always verify with a watch table on the actual-length byte before assuming the driver is doing the right thing.

4. Solution 1: Use a CHAR Array in the DB

The simplest fix is to remove the STRING data type from the DB and replace it with an array of BYTE or CHAR that has the same number of bytes as the payload. The FC that decodes the string is then rewritten to read from a known base offset, not from a STRING symbolic name.

Approach DB declaration FC operation Result
STRING (broken with InTouch) MyTxt : STRING[20] LEN(MyTxt) Returns 0 (header missing)
CHAR array (works) MyTxt : ARRAY[1..20] OF CHAR Custom loop on MyTxt[1..20] Returns 20 characters as-is
BYTE array (works) MyTxt : ARRAY[0..19] OF BYTE Custom loop on MyTxt[0..19] Same as CHAR; useful if your FC takes BYTE

This approach is the lightest in engineering effort but requires a small custom FC to compute the actual length. Scan for the first 16#00, or store the length in a separate INT tag that InTouch also writes via a QuickScript on the On-Key event of the string input object.

5. Solution 2: Pre-pend the 2-Byte Header in STEP 7

If you must keep the STRING data type because downstream blocks depend on it (typical for libraries reused across multiple projects), pre-pend the header bytes at the start of the FC that decodes the string. This is the technique used in the original Siemens-supporting forum discussion and is the most portable across the standard IEC function library. Both STEP 7 V5.x and TIA Portal SCL examples are provided.

SCL for S7-300/S7-400 (STEP 7 V5.x):

// SCL: rebuild a STRING header from an array of CHAR // Inputs : arrIn : ARRAY[1..20] OF CHAR (raw InTouch payload) // Outputs: sOut : STRING[20] // Temp : i : INT; bMax : BYTE; bAct : BYTE bMax := B#16#14; // 20 dec = max length bAct := B#16#00; FOR i := 1 TO 20 DO IF arrIn[i] <> 16#00 THEN bAct := INT_TO_BYTE(BYTE_TO_INT(bAct) + 1); END_IF; END_FOR; // Move header + payload into STRING structure sOut := ''; // clear (also clears actual length) FOR i := 1 TO 20 DO sOut := CONCAT(IN1 := sOut, IN2 := STRING(arrIn[i])); END_FOR;

SCL for S7-1500 (TIA Portal):

// TIA Portal S7-1500: same logic, the runtime fills the header // automatically when the CONCAT assignment is performed. sOut := ''; FOR i := 1 TO 20 DO IF arrIn[i] <> 16#00 THEN sOut := CONCAT(IN1 := sOut, IN2 := STRING(arrIn[i])); END_IF; END_FOR;

STL for S7-300/S7-400 (STEP 7 V5.x) — pre-pend header only:

// STL: pre-pend S7 STRING header at offsets 230/231 of DB15 // Source area: MW100..MW119 holds 20 ASCII characters L B#16#14 // max length = 20 T DB15.DBB 230 L 0 T DB15.DBB 231 // actual length = 0 (will be counted) L 0 T #iAct // loop counter / actual length NEXT: TAR1 #iPtr // pointer arithmetic on payload source L MW [#iPtr] L 16#00 <>I JC COUNT JU COPY COUNT: L #iAct L 1 +I T #iAct COPY: L #iAct L 20 <I JC NEXT L #iAct T DB15.DBB 231 // commit actual length
The STL snippet above is illustrative. In production, prefer SCL for readability or use the standard BLKMOV / S7-SCL CONCAT constructs. Always double-check the actual-length computation against the watch table.

6. Solution 3: Reconfigure the InTouch String Input Object

AVEVA InTouch provides a string input animation link that lets the operator enter a text string of up to a configured maximum length. According to the official AVEVA InTouch HMI documentation, the string input is bound to a memory message tag whose configured length must match the PLC's payload length. When you bind the input to an S7 STRING tag, the runtime inserts the characters at the configured offset but it does not synthesize the length header. You must either:

  1. Use a separate INT tag (e.g., DB15,INT230) to hold the actual length, and write it from a small InTouch QuickScript that runs after the string input is committed.
  2. Wire the string input directly to a CHAR-array memory message tag, then have the PLC build the STRING on the scan.

Refer to the official AVEVA InTouch string input documentation: AVEVA InTouch HMI: String input links.

7. Step-by-Step Implementation: CHAR Array Method

  1. Open STEP 7 (V5.x) or TIA Portal and load the project that contains the affected DB.
  2. Open DB15 and locate the STRING[20] tag at offset 230.
  3. Replace the STRING[20] declaration with arr_Msg : ARRAY[1..20] OF CHAR;. Shift any tags downstream by 22 bytes if their offsets are not symbolic.
  4. Open the InTouch WindowMaker database and change the access name item from DB15,STRING232,20 to DB15,CHAR232,20 (offset 232, 20 characters). For DASIDirect you can also use DB15,B232,20.
  5. Add a separate INT tag DB15,INT230 to carry the actual length. Wire the On-Key entry of the string input object to a QuickScript that writes LEN(MSG_BODY) to that tag.
  6. Rewrite the decoding FC to read arr_Msg[1..20] and the integer i_ActLen instead of relying on LEN(STRING).
  7. Compile and download the DB to the PLC, then restart the InTouch I/O server.

8. Step-by-Step Implementation: Header Pre-pending Method

  1. Keep the existing STRING[20] tag at offset 230 in DB15.
  2. Add a new FC, for example FC 200 "Build_S7_String", that takes the InTouch-side CHAR array as input and writes the header bytes at offsets 230 (max) and 231 (actual).
  3. Call FC 200 from OB 1 on every scan, before any block that operates on the STRING.
  4. Update the InTouch tag definition to DB15,CHAR232,20 (raw payload) and add a tag for the length if you want to write it from the HMI side.
  5. Compile, download, and run a verification cycle as described in section 9.
  6. After verification, lock the InTouch operator against typing more than 20 characters using the Max Length property on the string input object.

9. Verification and Commissioning

After applying the fix, perform the following checks before releasing the line. Capture a watch-table screenshot and an HMI screen capture with the operator entry visible, and store both with the FAT/SAT package.

Test Procedure Expected result Pass criterion
PLCSIM string injection Type 9 characters in PLCSIM into the STRING[20] tag LEN("MyTxt") = 9 Matches the typed length
InTouch entry, then FC LEN Type 9 characters on the HMI LEN("MyTxt") = 9 Matches the characters entered
Watch table byte 230 Open DB15 watch table at offset 230 16#14 Header max length = 20
Watch table byte 231 Open DB15 watch table at offset 231 16#09 Header actual length = typed length
Find substring Call FC FIND on a known substring Position returned > 0 Position matches expected index
Empty string Clear the HMI input and commit Byte 231 = 16#00, FC LEN = 0 No spurious characters
Max length string Type 20 characters Byte 231 = 16#14 Equal to max length byte
Overflow attempt Type 22 characters Truncated to 20 Header actual length never exceeds 16#14
Decision flow: selecting the fix for S7 STRING from InTouch STRING FCs (LEN, FIND, CONCAT) required? No Yes CHAR array in DB + custom length FC STRING in DB + pre-pend header FC

10. Edge Cases and Special Characters

NULL padding. InTouch's CHAR input typically pads short strings with 16#00 up to the declared length. The PLC must scan for the first 16#00 to determine actual length; otherwise LEN returns the full buffer length and downstream blocks process garbage characters.

Non-printable characters. If the operator enters characters such as CR (16#0D), LF (16#0A), or DEL (16#7F), the HMI display layer may filter them. The PLC should never use these as a delimiter inside the payload because InTouch strips them before transmission. If a barcode scanner writes into the same tag, configure the scanner for prefix and suffix suppression.

Strings longer than the buffer. If the operator types 22 characters into a 20-byte buffer, InTouch truncates silently. To detect truncation, set a separate INT tag in the InTouch script that holds LEN(MSG) and alarm if the value is greater than the configured buffer length. The pre-pend FC must clamp the actual-length byte to the max-length byte to prevent downstream buffer overflows.

Unicode / UTF-8. S7 STRING is a 7-bit ASCII data type. Multi-byte UTF-8 sequences (e.g., 0xC3 0xA4 for 'ä') will be stored as two separate bytes and the LEN field will count both. If your application requires UTF-8, switch to WSTRING (S7-1500) and bind it through an OPC UA channel rather than a legacy DASServer. Be aware that InTouch's string input object only supports 7-bit ASCII; non-ASCII characters appear as '?'.

Cross-DB boundary. If your STRING is defined at the end of a DB, the header pre-pend can write into the next DB's region. Confirm the boundary with the STEP 7 cross-reference and the DB length in the project tree.

Multiple strings in the same DB. When two STRINGs are adjacent in the DB (e.g., STRING[20] at offset 230 and STRING[20] at offset 252), the pre-pend FC must operate on the exact two header bytes for the right string. Use symbolic addressing, not absolute offsets, to avoid shifting the boundary when the DB is re-edited.

PLCSIM vs. real PLC. PLCSIM initializes bytes outside the active payload with 16#00. A real S7-300/400 CPU can read undefined memory in those positions. If a STRING FC such as FIND or CONCAT scans past the actual length, the result will be a long delay on real hardware. Always trust the actual-length byte, not a NULL scan, on real CPUs.

11. STRING vs CHAR Decision Matrix

Criterion Use STRING (with pre-pend FC) Use CHAR array (custom FC)
Standard Siemens IEC FC usage (LEN, FIND, CONCAT) Yes — drop-in No — write a custom FC
Portability of the FC across projects High Medium
InTouch HMI direct tag binding Requires header pre-pend Drop-in
Memory efficiency (header overhead) 2 bytes per string 0 bytes overhead
Cross-platform (WinCC TIA, TIA Portal HMI) Yes Yes — with separate length tag
UTF-8 / multi-byte support No — use WSTRING No — use ARRAY OF BYTE
Ease of commissioning Medium High
Library reuse across multiple PLCs High Low — FC must be re-engineered
Performance (scan time impact) Negligible (1+1 byte copy) Negligible (loop on N bytes)
S7-1500 compatibility Yes — same format Yes

The CHAR array approach wins on simplicity and HMI binding. The STRING approach wins on long-term maintainability of the FC library. If the project will outlive the current HMI, prefer STRING with a pre-pend helper FC. If the project is a one-off with a fixed InTouch integration, the CHAR array is faster to commission.

12. Frequently Asked Questions

Why does my S7 STRING LEN return 0 when the string is written from AVEVA InTouch?

The InTouch DASServer transmits the raw character payload of a STRING tag without the 2-byte S7 header. The actual length byte (offset +1 from the string base) remains at 0 or stale, so FC LEN reads 0. Switch the DB tag to ARRAY OF CHAR or add a header pre-pend FC. See AVEVA InTouch string input documentation for the HMI-side binding rules.

Can I bind a TIA Portal S7-1500 WSTRING to InTouch?

Not directly through the legacy DASIDirect. WSTRING requires OPC UA, which InTouch can consume via a third-party OPC UA client driver such as the IGS / OPC UA Tunneler. For 7-bit ASCII payloads, use STRING with the pre-pend fix described in section 5.

What is the InTouch syntax DB15,STRING232,20?

It means: read 20 bytes from data block 15 starting at byte offset 232 and treat them as a character payload. The STRING keyword in this context is an alias for raw character access; it does not mean the S7 STRING data type. Use CHAR or B for an explicit character or byte reference, e.g., DB15,CHAR232,20 or DB15,B232,20.

How do I compute the actual length inside the FC?

Scan the character array from index 1 to the max length and increment a counter until you hit 16#00, or store the length in a separate INT tag that is written by an InTouch QuickScript on the On-Key event of the string input object. The latter is more reliable because the operator can enter strings that contain embedded 16#00 (rare in operator messages but possible in barcode scans).

Does the same fix apply to WinCC Comfort/Professional HMI tags?

Yes. WinCC TIA tags bound to an S7 STRING receive the full STRING structure including the header, so the FC works out of the box. If you bind to a CHAR-array tag, the same CHAR-array method described for InTouch applies. Mixing the two bindings inside the same project is the most common source of header-related errors in mixed-vendor plants.

Why is the S7 STRING[254] limit 254 and not 255 or 256?

The 1-byte length field holds values 0 to 255, but the maximum string length byte must be non-zero to indicate a valid STRING. 254 is the highest value that fits in a single length byte while leaving room for at least one null/padding byte in the structure. S7-1500 WSTRING uses a 2-byte (WORD) length field and supports up to 16 382 characters.

Back to blog