WinCC V14 SP1: Detect Unchanged OPC Values Using VBScript

David Krause11 min read
SiemensTutorial / How-toWinCC
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

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:

  1. Periodically shadows the current OPC value into an internal HMI tag.
  2. Compares the current OPC value against the shadow and increments a counter when they match for a sustained period.
  3. 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.

Scope: This guide targets WinCC Professional V14 SP1 Update 5 (V14.0.1.5). Most constructs are valid from V13 SP1 onward and through V15.x/V16 with minor property name changes. Confirm against the specific V14 SP1 manual set when upgrading in either direction.

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).
Licensing: Scheduled VBScript actions in WinCC Professional require a valid Runtime license. If you are running on a WinCC RT demo/trace mode, scheduled tasks may execute with reduced cadence or be disabled entirely.

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
Why two cycle times? If the shadow-copy and the comparison run at the same cadence, a single missed OPC update can be masked by the next shadow-copy. Decoupling the two timers means the comparison always evaluates against a snapshot that is at least one comparison cycle older than the current value.

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 opctag1 has 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

  1. Static check: With the RT running, set opctag1 to a known non-zero value via the OPC server or a tag simulator. The counter should not advance and the alarm should not trigger.
  2. 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_alarm should latch and the discrete alarm should appear in the alarm view.
  3. Recovery test: Force a value change on opctag1. Within one comparison cycle, stale_counter should reset to 0 and stale_alarm should clear (if the alarm is configured as non-latching).
  4. 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.
  5. 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
Edge case — zero is a valid value: The non-zero guard prevents the alarm from tripping when the process legitimately settles to zero. If zero is a meaningful live value in your process, replace the guard with a quality-code check or a separate "first-cycle bootstrap" flag.

Field-Proven Variations

Three extensions appear frequently in real installations.

  • Quality-aware version: Read the OPC quality code via HMIRuntime.Tags("opctag1").Quality and 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

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.

Back to blog