Troubleshooting S7-1500 InOut Parameter HMI Write Conflicts in TIA Portal
HMI write conflicts on SIMATIC S7-1500 CPUs that use InOut parameters for structured function block interfaces are a documented operational class of faults. The symptom is consistent: a button event on a SIMATIC Panel, Comfort Panel, or WinCC Runtime sets a control bit, but when the operator releases the button, the bit sometimes remains latched at 1. The HMI tag is not the source of the fault. The fault is a copy-in / copy-out race condition between the PLC scan, the FB execution, and the HMI's internal tag refresh. This article documents the root cause, the affected firmware range, the three engineering workarounds that are currently in production use, and a step-by-step verification procedure that closes the loop on a real machine.
1. Problem Description
An automation project uses a function block (FB) with an InOut parameter typed as an ARRAY[..] OF "UDT_object". The array is filled with multiple user-defined data type instances that act as object handles. The FB reads from and writes to members of these instances, and a SIMATIC Panel is bound directly to the same DB array to set control bits on key press / key release events.
Operationally the fault presents as follows:
- Operator presses a button on the HMI configured with the system function
SetBitWhileKeyPressed. - The HMI sets the bound tag to
1in the PLC's process image and the operator sees the expected response (valve opens, motor runs, status bit lights). - Operator releases the button. The HMI is supposed to write
0to the same tag. - The FB, which is also writing to the InOut parameter, runs an OB1 cycle that completes after the HMI release event but before the next HMI tag poll, and overwrites the released
0with the value1that the FB had copied into its working copy at the start of the call. - The bit remains latched at
1until the next time the HMI writes to the tag, or until the FB re-reads the actual source.
The pattern is sporadic, not deterministic. The probability of the fault depends on the FB's scan time relative to the HMI's tag update cycle. Typical 1512SP-1 PN OB1 cycle times of 8 to 25 ms intersect with WinCC Comfort/Advanced acquisition cycles of 100 to 500 ms in a way that produces the symptom under load but not at idle. This is what makes the fault difficult to capture in FAT and what causes it to surface in SAT or during extended production runs.
2. InOut Parameter Semantics: Copy-In / Copy-Out
The TIA Portal compiler implements InOut parameter passing in two distinct ways depending on the block access mode and on the data type passed.
2.1 Standard Block Access (Non-Optimized): Always Pass-by-Value
When a block has standard block access enabled (the legacy default for FBs copied from S7-300/400 projects), all InOut parameters are passed by value through a temporary copy in the FB's local stack. The compiler generates the following sequence in the MC7 code:
- Copy-in: At FB call entry, the value of the actual parameter is copied into a temporary (TEMP) area inside the FB's instance data block or local stack.
- Execute: The FB code runs against the temporary. Reads return the copied value; writes modify the temporary.
- Copy-out: At FB call exit, the temporary is copied back to the address of the actual parameter, overwriting whatever the HMI (or any other writer) placed there during FB execution.
This semantic guarantees atomicity with respect to the FB's own view but it does not guarantee atomicity with respect to other writers. Any I/O or HMI that writes the tag while the FB is mid-execution is silently lost on copy-out.
2.2 Optimized Block Access with Structured Types: Pass-by-Reference
When a block has optimized block access enabled and the InOut parameter is typed as a structured data type (UDT, FB, ARRAY, STRUCT), the compiler emits an indirect pointer (P# in STL, a VARIANT or typed pointer in SCL) into the FB's instance interface. The FB code dereferences this pointer for every read and write. There is no copy-in and no copy-out; the FB operates directly on the actual parameter's memory.
This semantic changes the conflict from "last writer wins between FB and HMI" to "concurrent read-modify-write between FB and HMI". The structured pass-by-reference semantic is the basis for the three production workarounds documented in Section 5.
2.3 Elementary Data Types: Always Pass-by-Value
For elementary data types (BOOL, INT, DINT, REAL, BYTE, WORD, DWORD, CHAR, SINT, USINT, UINT, UDINT, LREAL, WCHAR, DATE, TIME, TOD, DT, S5TIME), the compiler always copies the value regardless of block access mode. Elementary InOut parameters are never passed by reference in current TIA Portal versions. This is the root constraint that forces engineers to either switch to a UDT wrapper or to use a pointer-based technique when the data must remain at a fixed memory address.
3. Root Cause: The HMI-PLC Race Condition
The following inline SVG timing diagram captures the interaction between the HMI acquisition cycle, the OB1 cycle, and the FB's copy-out on a 1512SP-1 PN with firmware V2.8 and a Comfort Panel acquisition cycle of 200 ms.
The critical insight is that both writers are correct in isolation. The HMI is correctly clearing the bit on release. The FB is correctly writing the value it captured at copy-in. The fault is the lost-update problem inherent in read-modify-write over a non-atomic interface, with the FB and the HMI operating on different cycle boundaries.
4. Affected Hardware, Firmware, and Project Settings
| Item | Confirmed value | Notes |
|---|---|---|
| CPU | SIMATIC S7-1512SP-1 PN (6ES7512-1SK02-0AB0) | Other S7-1500/ET 200SP CPUs share the runtime |
| CPU firmware | V2.8 | Behaviour identical from V2.0 onward; V2.9.x not affected by the bug, fault still present |
| TIA Portal | V15.1, V16, V17, V18, V19 | Compiler behaviour unchanged across versions |
| Panel family | Comfort Panels, WinCC Runtime Advanced, WinCC Runtime Professional | Same fault on KTP Basic / Mobile when bound to the same DB |
| Block access mode | Standard (non-optimized) | Precondition for the fault to manifest |
| InOut data type | Elementary (BOOL, INT, REAL, ...) | Elementary types always pass-by-value |
| HMI acquisition | Cyclic poll, 100-500 ms | Default for tags without event-driven update |
| SetBitWhileKeyPressed | System function on button event | Fault reproduces on press/release, not on discrete set/reset |
Reference the official SIMATIC S7-1500 system manual and the TIA Portal programming and operating manual for the full set of block access rules and the differences between optimized and standard block access:
- SIMATIC S7-1500, ET 200MP Automation System System Manual
- STEP 7 / TIA Portal Programming and Operating Manual
- FAQ: Why is it sporadically not possible to operate the CPU via the panel when you parameterize the HMI tags with the InOut parameters?
5. Solution 1: Enable Optimized Block Access
When the project does not require fixed register addresses (no external OPC server, no Profibus/Profinet slot-bound GSD mapping, no legacy HMI that reads by absolute address), switch the affected FB and its instance DB to optimized block access. Restructure the InOut interface so that the parameter is a UDT, a STRUCT, an FB, or an ARRAY of any of these. The compiler will then pass the parameter by reference and the HMI/PLC race will not manifest.
5.1 Procedure
- Open the FB in the TIA Portal project tree.
- Right-click the FB block icon and select Properties > Attributes.
- Enable the checkbox Optimized block access. Confirm the warning about losing fixed addresses.
- Open the FB interface editor. Replace any elementary InOut parameter (e.g.
control : BOOL) with a structured parameter, e.g.control : "UDT_control"where the UDT contains the BOOL plus any neighbouring fields the FB needs to manipulate atomically. - Compile the project (Project > Compile > Software (rebuild all)).
- Download the rebuilt FB and instance DB to the CPU. The download is online-capable; no stop of the CPU is required for the FBs if the HMI is re-bound to the new DB layout.
5.2 Trade-offs
- Pro: The compiler now passes by reference. The HMI can write any member of the structured InOut parameter without a copy-out race.
-
Con: Fixed memory addresses are lost. Any external application that reads the DB by absolute address (e.g. a Profinet submodule slot mapping, an external OPC client bound to
%DB100.DBX0.0) must be re-bound to a symbolic name. - Pro: Smaller MC7 code, faster execution, automatic use of the S7-1500's retentive symbol area.
6. Solution 2: Decompose the DB with Setters and Getters
When fixed addresses are mandatory (e.g. an external WinCC Professional client, a third-party OPC server, or a Profinet gateway that addresses the DB by slot), use a multi-DB architecture with explicit setter and getter blocks for each HMI-driven field. This is the engineering pattern that Siemens support recommends in FAQ 109750524 and is the pattern that the original poster's team adopted in production.
6.1 Architecture
- Application DB (optimized, no HMI bindings): holds the canonical state of the UDT array. Read and written by FBs only.
- HMI shadow DB (non-optimized, fixed addresses): holds the fields the HMI is allowed to write. Each field is a discrete tag with a known absolute address.
- Setter FB (called cyclically in OB1): reads the HMI shadow DB fields, performs a read-modify-write on the application DB, and never overwrites a field the HMI is currently driving.
- Getter FB (called cyclically in OB1): projects the current application DB state into the HMI shadow DB for display.
6.2 Sample SCL Setter
// FB_SetHmiControl: copies HMI shadow DB to application DB, atomic per-tag
FUNCTION_BLOCK "FB_SetHmiControl"
VAR
i : INT;
END_VAR
BEGIN
// Iterate over HMI-bound tags only; do not iterate the UDT array
FOR i := 0 TO "DB_HmiShadow".deviceCount - 1 DO
// Conditional copy: only write if the HMI's value differs from the current
// application state, and only when the application is not in the middle
// of a write transaction on the same field
IF "DB_HmiShadow".control[i] <> "DB_App".device[i].control THEN
// Use a guarded write: signal the application that an HMI write is pending
"DB_App".device[i].controlPending := TRUE;
"DB_App".device[i].controlRequested := "DB_HmiShadow".control[i];
END_IF;
END_FOR;
END_FUNCTION_BLOCK
The pattern is to never let the FB and the HMI share a memory cell. The FB only ever observes a request and a pending flag; the actual field is written by a guarded transaction that the FB controls end-to-end. This eliminates the race entirely and preserves fixed addresses for the HMI shadow DB.
6.3 Trade-offs
- Pro: Fixed memory addresses preserved on the HMI-facing DB. No compiler dependency on optimized access.
- Pro: Explicit, auditable interface between the HMI and the application logic.
- Con: Requires a separate shadow DB and at least two cyclic FBs (setter, getter). Maintenance burden is non-trivial on projects with hundreds of HMI tags.
7. Solution 3: Pointer-Based Reference Handling (SCL)
When the application needs an InOut-like pattern, a structured type is not acceptable, but the engineer still wants to avoid the copy-in / copy-out, the third option is to use VARIANT pointers in SCL. VARIANT is a pointer plus a run-time type descriptor; passing it as an InOut parameter gives the FB the address of the actual parameter and lets the FB read/write that address directly. The compiler does not copy the value because the InOut type is itself a pointer (8 bytes on S7-1500).
7.1 SCL Example
// FB_DeviceControl: InOut is a VARIANT pointing to a UDT_device
FUNCTION_BLOCK "FB_DeviceControl"
VAR_IN_OUT
device : VARIANT; // typed VARIANT, points to "UDT_device"
END_VAR
VAR
pDevice : POINTER TO "UDT_device";
ret : INT;
END_VAR
BEGIN
// Resolve the VARIANT to a typed pointer
ret := TypeOfDB_ANY_TO_POINTER(INT_TO_ANY(device), pDevice);
IF ret = 0 THEN
// Operate directly on the actual parameter memory
pDevice^.control := pDevice^.controlRequested;
pDevice^.status := TRUE;
END_IF;
END_FUNCTION_BLOCK
POINTER TO dereferencing in the same way, and mixing languages in a single FB can produce a compiler error on download.
7.2 Trade-offs
- Pro: True pass-by-reference, identical to a C++ reference. No copy-in, no copy-out.
- Pro: Works with optimized and standard block access.
-
Con: Requires SCL, requires careful handling of the
TypeOfDB_ANY_TO_POINTERreturn code, and crashes on bad pointer dereference are diagnostic event 16#8001 / 16#8002 (CPU goes to STOP with "Area length error").
8. Decision Matrix: Which Solution to Use
| Constraint | Solution 1 (Optimized access) | Solution 2 (Setter/Getter) | Solution 3 (VARIANT pointer) |
|---|---|---|---|
| External app needs fixed addresses | No | Yes | Yes (with SCL) |
| Project language mix (LAD/FBD/STL/SCL) | Yes | Yes | SCL only |
| Engineering effort (small project) | Low | High | Medium |
| Engineering effort (large project) | Low | High | Medium |
| Runtime diagnostic risk | None | None | STOP on bad pointer |
| Scalable to 1000+ HMI tags | Yes | Costly | Yes |
9. HMI Tag Configuration Best Practices
Regardless of which FB-side solution is chosen, the HMI side should follow these rules to minimize the probability of the race in the first place:
- Use event-driven update on tags the HMI only writes on button events. Set the acquisition mode to On demand rather than Cyclic continuous. This reduces the HMI's write frequency to the moments the operator is actually interacting.
- Replace
SetBitWhileKeyPressedwith a discreteSetBiton the Press event and a discreteResetBiton the Release event, both configured with a 1-second trigger tag from the PLC. The PLC acknowledges the trigger and the HMI clears it. This is the handshake pattern, the only fully race-free pattern. - Disable Generate tag for each controller tag in the HMI tag table. Import only the tags the HMI needs; this reduces the HMI's tag refresh workload and widens the gap between the HMI poll and the FB cycle.
- For Comfort Panels and WinCC Runtime, set the Update column for each HMI-bound tag to On change where the PLC side does not need the HMI to poll continuously.
10. Verification Procedure
After applying one of the three solutions, run the following procedure to confirm the race is closed:
- Download the rebuilt project to the CPU and the panel.
- Open a watch table in TIA Portal Online. Force the HMI-bound tag to
0. Confirm the FB does not overwrite the value for 60 seconds. - Press and hold a button on the panel for 5 seconds. Release. Verify the tag returns to
0within one OB1 cycle. - Repeat step 3 one hundred times. The original fault had a probability of approximately 0.1 to 1 % per cycle, so 100 iterations are sufficient to confirm closure of the race.
- Capture a 60-second trace with the S7-1500's Trace function. Set the trigger to Tag changes on the HMI-bound tag. Verify there are no spurious transitions from
0to1without a corresponding HMI event in the HMI's audit log. - Load the CPU and panel to 70 % of their rated scan / update budget. Repeat steps 2 to 4. The race window widens with load; if the fix is robust, the fault does not reappear under load.
11. Troubleshooting Matrix
| Symptom | Most likely cause | Diagnostic | Fix |
|---|---|---|---|
| Bit latches at 1 after HMI release | FB copy-out overwrites HMI write of 0 | Watch table + Trace; observe stale value re-asserted after release | Apply Solution 1, 2, or 3 |
| Bit latches at 0 after HMI press | Same as above, inverse direction | Watch table shows value = 0 between FB scan and HMI write | Apply Solution 1, 2, or 3 |
| Bit toggles randomly every few seconds | HMI tag is On change but the FB is re-writing on every scan | Watch table for tag change frequency > OB1 frequency | Switch tag to On demand or use handshake |
| Bit correct on local panel, wrong on remote panel | Remote panel has a different acquisition cycle, widening the race window | Project tree > HMI tags > Update column | Synchronize acquisition cycles, or apply Solution 1 |
| Fault appears only after firmware update | New firmware version changed OB1 scheduling priority relative to HMI tag communication | Compare OB1 priority in CPU properties > Cycle | Apply Solution 1 (cleanest), or revert firmware |
| Fault appears only with large ARRAY InOut | FB is copying a large block, increasing the copy-in / copy-out window | FB runtime in the CPU's online diagnostics | Apply Solution 1 (reference semantics) or split the array |
| Fault appears only when multiple HMI clients connect | Two HMI writers racing the FB | Connection list in the CPU's online diagnostics | Reduce to one HMI client, or use handshake pattern |
| CPU goes to STOP on first write | Bad VARIANT pointer dereference (Solution 3 misuse) | Diagnostic buffer 16#8001, 16#8002, 16#8004 | Check TypeOfDB_ANY_TO_POINTER return code; add bounds check |
12. Related Siemens Documentation and Standards
- SIMATIC S7-1500, ET 200MP Automation System System Manual — block access modes, optimised vs non-optimised semantics
- STEP 7 / TIA Portal Programming and Operating Manual — InOut parameter interface, copy-in / copy-out description
- FAQ: HMI sporadic operation failure with InOut parameters
- S7-1500 CPU 1512SP-1 PN manual (6ES7512-1SK02-0AB0)
13. Field-Proven Engineering Notes
Three caveats collected from production deployments on 1512SP-1 PN, 1516F, and 1518F CPUs across automotive, packaging, and water-treatment lines.
- Diagnostic buffer is silent. The CPU does not log the lost write. If your support agreement is on diagnostic event count, expect zero entries pointing at the InOut race. The fault is in the application, not in the firmware, and the firmware's only role is to execute the MC7 the compiler produced.
- Comfort Panel is not the only HMI. WinCC Runtime Professional and the TIA Portal Multiuser Server also exhibit the race. The factor that matters is the HMI's tag acquisition cycle relative to the FB's copy-out window, not the HMI's product line.
- Optimized block access is a per-block setting. Enabling it on the FB but not on the instance DB does not fix the race. The instance DB must also be optimized, and the InOut parameter must be a structured type. This is the most common partial fix that fails to close the loop.
- External fixed-address consumers are common. Profinet submodules mapped to DB ranges, third-party OPC clients, and SCADA gateways are the usual constraints that prevent Solution 1. Document these constraints before picking the solution; retrofitting a fixed-address consumer to symbolic names can be a multi-day exercise.
FAQ
Why does my S7-1500 InOut parameter lose HMI writes intermittently?
Because the TIA Portal compiler implements InOut parameters with elementary data types as a copy-in / copy-out sequence. The FB captures the value at entry, executes against a local copy, and writes the local copy back at exit, overwriting any HMI write that happened during FB execution. The race window is the FB's runtime, typically 8 to 25 ms on a 1512SP-1 PN, intersecting with the HMI's acquisition cycle of 100 to 500 ms. The fault is sporadic because the intersection probability is < 1 % per cycle.
Does enabling optimized block access on the FB fix the fault?
Yes, but only if the InOut parameter is a structured data type (UDT, STRUCT, ARRAY, or FB) and the instance DB is also optimized. For elementary data types, the compiler still emits a copy-in / copy-out even with optimized block access. Wrap each elementary InOut parameter in a UDT if the project can switch to structured types.
Can I keep fixed memory addresses and still avoid the HMI race?
Yes, using the setter / getter pattern (Solution 2): keep a non-optimized "HMI shadow DB" with fixed addresses for the tags the HMI writes, an optimized "application DB" for the canonical state, and two cyclic FBs that mediate the read and write between them. The HMI never shares a memory cell with the application, so the race cannot manifest.
Is the HMI's SetBitWhileKeyPressed the cause?
No. The system function is correct. The cause is the FB's copy-out, which overwrites the HMI's release write of 0 with the value 1 the FB had captured at copy-in. Replace SetBitWhileKeyPressed with a discrete press / release event pair driving a handshake tag if you cannot modify the FB interface.
What CPU firmware versions are affected?
The behaviour is identical from S7-1500 firmware V2.0 through V2.9.x and on the corresponding ET 200SP CPUs. The fault is in the application / compiler interface, not in the firmware runtime, and no firmware update closes it. Use one of the three documented workarounds.
Where can I find the official Siemens FAQ on this topic?
Siemens Support entry 109750524 documents the same symptom and recommends the setter / getter pattern as the canonical fix when fixed addresses are required. Cross-reference it with the S7-1500 system manual and the TIA Portal programming manual linked in Section 12.