Problem Overview
In SIMATIC WinCC Professional V14 SP1 Update 5 (part of the TIA Portal V14 SP1 engineering framework), many HMI/SCADA applications consume process data from external OPC DA / OPC UA servers such as the TCSB bridge referenced in the source. A frequent operational requirement is to detect a stale value condition — that is, an OPC tag whose value is no longer being refreshed by the underlying controller or by the OPC server itself. Unlike a hard communication error, a stale value still satisfies the OPC quality code (GOOD), so the HMI cannot rely on quality flags alone.
This article documents a two-script scheduled-task architecture for WinCC Professional that:
- Periodically shadows the current OPC value into an internal HMI tag.
- Compares the current OPC value against the shadow and increments a counter when they match for a sustained period.
- Drives a digital alarm tag when the counter crosses a configurable threshold (for example, 60 increments = 1 hour of staleness).
The approach is purely tag-based, requires no PLC logic changes, and uses only VBScript actions available in the WinCC Professional scripting environment.
Architecture and Data Flow
The detection pattern is intentionally simple: one OPC tag, one internal shadow tag, one counter tag, and one alarm tag. All four tags are HMI-side; the PLC is not modified.
| Tag Name | Source | Data Type | Purpose |
|---|---|---|---|
opctag1 |
OPC server (e.g. TCSB) | INT / REAL / DINT | Live process value from the external OPC server |
internaltag1 |
HMI internal | Same as opctag1
|
Snapshot of the OPC value taken at the previous comparison cycle |
stale_counter |
HMI internal | DINT | Cumulative count of consecutive unchanged samples |
stale_alarm |
HMI internal | BOOL | Alarm bit, latched true when threshold is exceeded |
The detection loop is driven by two scheduled tasks with deliberately different cycle times. The shadow-copy task runs on a slow cycle (e.g. 10 minutes) to ensure the previous snapshot is always sufficiently aged; the comparison task runs on a fast cycle (e.g. 5 seconds or 1 minute) to detect drift quickly.
Prerequisites
- TIA Portal V14 SP1 with WinCC Professional V14 SP1 Update 5 installed.
- An HMI device of type WinCC RT Professional added to the project.
- Configured OPC DA / OPC UA connection to the external server (here, the TCSB bridge). The OPC connection wizard in TIA Portal V14 SP1 supports both SIMATIC HMI Option+ for OPC UA and the classic OPC DA channel.
- Four HMI tags created as described in the architecture table.
- Access to Scheduled tasks on the WinCC RT Professional target (RT license required for runtime execution).
Step 1: Declare the HMI Tags
In the TIA Portal project tree, expand your HMI device and open HMI Tags. Create the following four tags. The OPC tag opctag1 must be configured against the OPC server connection; the remaining three are HMI-internal.
| Name | Connection | Data Type | Acquisition Cycle | Comment |
|---|---|---|---|---|
| opctag1 | OPC_TCSB | INT | 1 s | Live value from OPC |
| internaltag1 | <internal> | INT | — | Snapshot from previous cycle |
| stale_counter | <internal> | DINT | — | Counts unchanged samples |
| stale_alarm | <internal> | BOOL | — | Stale-value alarm bit |
Make sure the acquisition cycle on the OPC tag is faster than the comparison task; otherwise the comparison will see identical values simply because no new value has been fetched yet.
Step 2: Write the Shadow-Copy Script
The shadow-copy script is intentionally minimal. Its only job is to read the current OPC value and write it to internaltag1. The script is triggered by a slow scheduled task (10 minutes is a common choice for long-running process values).
' Filename: CopyValuesForChangeAlarm
' Trigger: Scheduled task, 10-minute interval
' Purpose: Capture the current OPC value into the shadow tag
Dim currentValue
' Read OPC tag (asynchronous, by default in WinCC Professional)
currentValue = HMIRuntime.Tags("opctag1").Read
' Write the snapshot to the internal tag
HMIRuntime.Tags("internaltag1").Write currentValue
For floating-point values, you may want to add a small dead-band to avoid spurious counter increments caused by LSB noise:
Dim currentValue, lastValue
currentValue = HMIRuntime.Tags("opctag1").Read
lastValue = HMIRuntime.Tags("internaltag1").Read
If Abs(CDbl(currentValue) - CDbl(lastValue)) > 0.01 Then
HMIRuntime.Tags("internaltag1").Write currentValue
HMIRuntime.Tags("stale_counter").Write 0
End If
Step 3: Write the Comparison Script
The comparison script runs on a fast cadence (1 minute in the source implementation, 5 seconds is also valid for sub-minute detection) and decides whether the value has changed since the last snapshot. If the value is unchanged and non-zero, the stale counter is incremented; otherwise it is reset.
' Filename: CompareValuesForChangeAlarm
' Trigger: Scheduled task, 1-minute interval (5 s is acceptable)
' Purpose: Increment the counter when opctag1 equals internaltag1
' and the value is non-zero. Latch the alarm at 60 cycles.
Dim currentValue, lastValue, counter
currentValue = HMIRuntime.Tags("opctag1").Read
lastValue = HMIRuntime.Tags("internaltag1").Read
counter = HMIRuntime.Tags("stale_counter").Read
' Use AND, not &. The & operator in VBScript concatenates strings.
If (currentValue = lastValue) And (currentValue <> 0) Then
counter = counter + 1
HMIRuntime.Tags("stale_counter").Write counter
If counter >= 60 Then
HMIRuntime.Tags("stale_alarm").Write 1
End If
Else
' Value has changed — reset the counter and clear the alarm
HMIRuntime.Tags("stale_counter").Write 0
HMIRuntime.Tags("stale_alarm").Write 0
End If
The threshold of 60 is derived from the comparison cycle: 60 cycles × 1 minute = 1 hour of staleness. Adjust the constant to match the desired detection window and the configured trigger interval.
Step 4: Configure the Scheduled Tasks
Open the HMI device in TIA Portal and navigate to Schedules > Tasks. Create the two tasks with the following properties:
| Task Name | Trigger | Script | Additional Notes |
|---|---|---|---|
| CopyValuesForChangeAlarm | 10 minutes | CopyValuesForChangeAlarm | Select Start once at system startup if you want the first snapshot taken immediately on RT boot. |
| CompareValuesForChangeAlarm | 5 seconds (or 1 minute) | CompareValuesForChangeAlarm | For 1-minute cadence, multiply threshold accordingly. |
For background on the WinCC Professional scheduler, see the WinCC Professional V14 SP1 - Programming and Reference Manual (entry ID 109755224), section "Scheduling tasks and time-driven events".
Step 5: Wire the Alarm to a Discrete Alarm
To turn stale_alarm into a logged alarm, open HMI Alarms > Discrete Alarms and add an alarm whose trigger tag is stale_alarm. Recommended properties:
-
Alarm text: "OPC value
opctag1has not changed for 1 hour" - Alarm class: Warnings (or Errors if the process is safety-relevant)
- Acknowledgement: Required, with the Single acknowledgment model
The alarm will appear in the WinCC alarm view, the alarm log, and (if enabled) on connected WinCC clients via the integrated web server or OPC A&E.
Troubleshooting: Common Pitfalls and Fixes
Three classes of error are typical during first-time implementation. They map directly to the issues identified in the source thread.
5.1 The "&" vs "AND" Operator Bug
Symptom: The comparison script runs without VBScript errors, but the counter never increments and the alarm never fires — even when the value is clearly stale.
Root cause: In VBScript, & is the string concatenation operator, not a logical AND. The original line
If HMIRuntime.Tags("opctag1") = HMIRuntime.Tags("internaltag1") & HMIRuntime.Tags("internaltag1") <> 0 Then
is parsed as
If (opctag1 = (internaltag1 & (internaltag1 <> 0))) Then
which compares the OPC value to a concatenated string and almost always evaluates false.
Fix: Use the keyword And for logical conjunction, and parenthesise each comparison to make precedence explicit:
If (currentValue = lastValue) And (currentValue <> 0) Then
5.2 Synchronous vs Asynchronous Tag Writes
Symptom: On some HMI targets, HMIRuntime.Tags("internaltag1").Write either throws an error or silently fails to update the tag when internaltag1 is an HMI-internal tag (not a PLC tag).
Root cause: The Write method on HMIRuntime.Tags defaults to synchronous mode, which requires a defined response path. Internal HMI tags sometimes do not satisfy this contract, depending on the runtime version and tag configuration.
Fix: Explicitly use the asynchronous overload:
HMIRuntime.Tags("internaltag1").Write currentValue, 1 ' 1 = async
For a full method reference, see the WinCC Professional V14 SP1 scripting reference, section "HMIRuntime.Tag object".
5.3 Comparing Tag Objects Instead of Values
Symptom: The script executes but always increments the counter, even when the OPC and shadow values are obviously different.
Root cause: Reading HMIRuntime.Tags("opctag1") (without .Read) returns the tag object itself. Two different tag objects are never equal in VBScript, so the comparison always yields false.
Fix: Always append .Read to obtain the variant value, then coerce to the correct type before comparison:
Dim a, b
a = CInt(HMIRuntime.Tags("opctag1").Read)
b = CInt(HMIRuntime.Tags("internaltag1").Read)
If (a = b) And (a <> 0) Then ...
Verification and Commissioning
-
Static check: With the RT running, set
opctag1to a known non-zero value via the OPC server or a tag simulator. The counter should not advance and the alarm should not trigger. -
Freeze simulation: Stop updating the OPC value (for example, by pausing the source PLC or by setting the OPC server's update rate to 0). The counter should increment by 1 every comparison cycle. After 60 cycles,
stale_alarmshould latch and the discrete alarm should appear in the alarm view. -
Recovery test: Force a value change on
opctag1. Within one comparison cycle,stale_countershould reset to 0 andstale_alarmshould clear (if the alarm is configured as non-latching). - Long-duration test: Run the RT for at least one full threshold window under normal traffic to confirm the counter never spools the alarm during healthy operation. Watch the WinCC tag logging for the counter trace.
- Restart test: Restart the WinCC RT. The shadow tag is in-memory, so the first comparison cycle will compare the OPC value to a zeroed shadow and may falsely trip the alarm. Decide whether to (a) pre-load the shadow from the OPC value at RT startup, or (b) exclude the first N cycles after boot from the counter logic.
Performance and Sizing Notes
Each scheduled task executes a single VBScript action that performs four Read calls and one Write call. The CPU cost on a typical WinCC RT Professional target is well under 1 ms per invocation; memory cost is the four HMI tags. For installations with hundreds of OPC tags to monitor, the script can be parameterised by tag name (read the tag names from a configuration tag and iterate) or executed in parallel scheduled tasks to keep the per-task execution time bounded.
| Cycle | CPU/task | Memory | Suitable for |
|---|---|---|---|
| 5 s | < 1 ms | ~80 B | Sub-minute detection on a single tag |
| 1 min | < 1 ms | ~80 B | Hour-scale detection on a single tag |
| 10 min (shadow) | < 1 ms | — | Decoupling snapshot from comparison |
Field-Proven Variations
Three extensions appear frequently in real installations.
-
Quality-aware version: Read the OPC quality code via
HMIRuntime.Tags("opctag1").Qualityand trip the alarm immediately when quality becomes BAD, bypassing the counter. - Multi-tag polling: Loop over a string array of tag names; one scheduled task monitors dozens of OPC tags without code duplication.
- OPC UA Pub/Sub over MQTT: In TIA V17/V18 projects, the same logic is portable to OPC UA Pub/Sub; replace the OPC DA channel with an OPC UA channel and the script body is unchanged.
References to Official Documentation
- WinCC Professional V14 SP1 - Programming and Reference Manual (SIOS entry ID 109755224)
- SIMATIC HMI Option+ for OPC UA (SIOS entry ID 109767706)
- WinCC V14 SP1 - Tips and Tricks for Scripting (SIOS entry ID 67598699)
- SIMATIC WinCC Professional V14 SP1 Update 5 - Release Notes (SIOS entry ID 109751498)
FAQ
Why does my counter never increment even though the value clearly has not changed?
You are almost certainly using & (string concatenation) instead of And (logical AND) in the IF condition, or you are comparing the HMIRuntime.Tags objects instead of calling .Read first. Replace the operator with And and append .Read to every tag access.
How do I avoid the false alarm on RT startup?
Pre-load the shadow tag from the OPC value in a startup script that runs once after RT boot, or skip the first N comparison cycles after RT start. A common pattern is a boolean bootstrapped tag that is cleared on RT start and set after the first shadow-copy cycle.
Can I run this on an OPC UA tag instead of OPC DA?
Yes. The WinCC Professional V14 SP1 scripting API is identical for both channel types; only the tag configuration in the HMI tag table changes. Point the opctag1 connection at the OPC UA server and the script body remains the same.
What is the right cycle for the comparison script?
Match the cycle to the detection window. A 1-minute cycle with a threshold of 60 gives 1-hour detection; a 5-second cycle with the same threshold gives 5-minute detection. Keep the shadow-copy cycle at least 10× slower than the comparison cycle to guarantee the snapshot is sufficiently aged.
Does the stale_alarm tag latch automatically?
No. The script sets the tag to 1 when the counter reaches the threshold and clears it when the value changes. To make the alarm operator-acknowledgeable, bind it to a discrete alarm in TIA Portal with the Single acknowledgment model so an operator must confirm it before the bit can be cleared in the alarm log.