WinCC OPC Tag to Internal Tag Copy: VBScript Methods

David Krause14 min read
HMI / SCADASiemensTutorial / How-to
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 Definition

WinCC runtime projects regularly need to mirror the value of an external OPC item into a local internal tag. The canonical case is a WinCC station that has been deployed for many years, where the source PLC/HMI system cannot be firmware-upgraded, the field bus topology cannot be changed, and the only available data path is the OPC DA (or OPC UA) subscription that is already in place. The engineer is then forced to bridge an external OPC tag into a tag that lives inside the WinCC tag database, so that the local HMI screens, scripts, archives, and alarms can reference it the same way they would reference any locally-defined tag.

This article documents the practical VBScript-based bridge patterns that work in WinCC V7.0 through V7.5 SP2, the configuration parameters that govern refresh cadence, the quality-code handling that prevents stale or bad values from contaminating internal tags, and the cross-platform equivalents that apply when the runtime is Ignition or Kepware/KEPServerEX instead of WinCC.

Scope: The instructions below cover WinCC 7.x with the standard VBScript engine. WinCC Professional (TIA Portal) uses a different API surface (HMIRuntime.SmartTags is replaced by tag-pluggable I/O fields in the Unified Comfort Panel line) and is referenced in Section 9 only for orientation. For full TIA Portal coverage, see the Siemens WinCC V7.5 SP2 scripting reference and the WinCC global script action documentation.

2. Architecture Decision Matrix

Two valid topologies exist. Choose before you write any code, because the choice dictates which machine runs the OPC stack, which side owns the tag database, and which direction the data flows.

Topology WinCC Role Data Flow Where the Bridge Code Lives Typical Trigger
A. WinCC = OPC Client DA/UA client, subscribes to remote server Remote server → WinCC OPC channel → internal tag WinCC Global Script (C or VBS) Cyclic timer (1 s / 2 s / 5 s) or tag-change event
B. WinCC = OPC Server DA/UA server, exposes internal tags Remote client → writes directly to internal tag None required — the client writes to the WinCC namespace directly Client-driven; no WinCC timer needed

Use Topology A when the remote system is fixed as the data source and the local WinCC project needs a local copy. Use Topology B when the remote system is the writer and WinCC is the canonical store. The remainder of this article focuses on Topology A, which is the case the original question describes.

3. Prerequisites

  1. WinCC V7.0 SP3 or later installed with a valid runtime license. The VBScript global action engine requires the "WinCC Scripting" option to be enabled at install time.
  2. An active OPC DA connection in the WinCC project. Verify in WinCC Explorer > Tag Management > OPC > [connection name] that the connection state is OK and that at least one OPC tag is returning a non-bad quality code.
  3. The list of OPC tag names exactly as they appear in the WinCC tag database (case-sensitive). These follow the naming convention [OPC_ConnectionName]\GroupName\TagName when referenced by VBScript via the internal OPCServer.Namespace, or the plain tag name when referenced through the HMIRuntime object.
  4. The destination internal tags must already exist in the WinCC tag database with the correct data type. A mismatch between the OPC source data type (e.g. VT_I4) and the internal tag type (e.g. 16-bit unsigned) is the single most common cause of runtime exceptions.
  5. Editor rights on the WinCC project and write access to the runtime directory so that compiled C-actions (if used) can be rebuilt by Graphics Designer.
Licensing caveat: Each WinCC OPC channel consumes one external-connection count. If the project is licensed for 128 power tags but the OPC channel adds another 64 OPC tags, confirm that the total number of configured tags does not exceed the licensed power-tag limit. Internal tags do not count toward the power-tag license.

4. Configuring the OPC Channel in WinCC

Open WinCC Explorer > Tag Management and right-click OPC. Select Add New Connection and enter the ProgID or CLSID of the remote OPC DA 2.05 / 3.0 server. The most common values are:

Remote System ProgID Recommended Refresh (ms)
WinCC V6.x on remote PC OPCServer.WinCC 1000
SIMATIC NET OPC Server OPC.SimaticNET 500
KEPServerEX KEPware.KEPServerEx.V6 1000
Generic third-party DA 3.0 Vendor-supplied 2000

After the connection is established, the WinCC tag browser shows the remote namespace. Drag the desired tags into the project tag list. Do not reference these tags from VBScript directly as if they were local tags — although WinCC does expose them, doing so couples the script to a single OPC topology. Always mirror them into a dedicated internal tag (Data Type = Binary Tag, no I/O address) and let the script read from / write to that internal tag.

5. VBScript Implementation

All examples below assume the following tag set exists in the WinCC project:

Tag Name Type Direction Source
OPC_Source_Temp 32-bit float, OPC channel Read from remote Remote WinCC/PLC
OPC_Source_State 16-bit unsigned, OPC channel Read from remote Remote WinCC/PLC
Internal_Temp_Mirror 32-bit float, internal Local copy This script
Internal_State_Mirror 16-bit unsigned, internal Local copy This script
Internal_Cmd_Reset Binary tag, internal Local trigger Operator HMI button
OPC_Dest_ResetAck Binary tag, OPC channel Write to remote This script

5.1 Unidirectional Mirror (Remote → Local)

This is the most common requirement: copy a handful of values from the remote OPC server into local internal tags once per second. Place the following VBScript into a Global Action triggered by a 1-second cyclic timer.

' === Global Action: gAct_OPCToInternal (cyclic, 1 s) ===
Option Explicit

Dim oSrc, oDst
Dim sErr

' --- Temperature (analog) ---
Set oSrc = HMIRuntime.Tags("OPC_Source_Temp")
Set oDst = HMIRuntime.Tags("Internal_Temp_Mirror")
oSrc.Read
If oSrc.Quality <> 0 Then
    ' Quality code 0 = Good. Anything else means stale or bad.
    ' Skip the write to avoid overwriting the last good value with garbage.
    sErr = "OPC_Source_Temp quality=" & oSrc.Quality
    HMIRuntime.Trace sErr
Else
    oDst.Value = CDbl(oSrc.Value)
    oDst.Write
End If

' --- State (digital/word) ---
Set oSrc = HMIRuntime.Tags("OPC_Source_State")
Set oDst = HMIRuntime.Tags("Internal_State_Mirror")
oSrc.Read
If oSrc.Quality = 0 Then
    oDst.Value = CLng(oSrc.Value) And &HFFFF&  ' mask to 16 bits
    oDst.Write
End If
Quality gating: The oSrc.Quality <> 0 check is critical. OPC quality codes 1–255 mean the value is not Good; copying it blindly will surface bad values to the HMI and can poison the tag archive. Quality codes of interest: 0x00000000 Good, 0x40000000 Uncertain, 0x80000000 Bad, 0xC0000000 BadComm (connection lost).

5.2 Bidirectional Mirror with Edge Detection

For commands that must travel back to the remote system, use edge detection so the write fires exactly once per operator action. Polling the value every cycle would re-fire the command continuously as long as the operator held the button.

' === Global Action: gAct_InternalToOPC (cyclic, 500 ms) ===
Option Explicit

Dim oCmd, oPrev, oAck
Dim bRise

Set oCmd  = HMIRuntime.Tags("Internal_Cmd_Reset")
Set oPrev = HMIRuntime.Tags("Internal_Cmd_Reset_Prev")
Set oAck  = HMIRuntime.Tags("OPC_Dest_ResetAck")

oCmd.Read
oPrev.Read

' Rising-edge detection: 0 -> 1 transition
bRise = (oCmd.Value = 1) And (oPrev.Value = 0)

If bRise Then
    oAck.Value = 1
    oAck.Write         ' Write to remote OPC server
    ' Latch a one-shot acknowledgement back to the local mirror after 1 s
    HMIRuntime.Wait 1000
    oAck.Value = 0
    oAck.Write
End If

oPrev.Value = oCmd.Value
oPrev.Write

5.3 Centralized Mapping via a Dictionary

For more than a handful of tags, maintain a VBScript Scripting.Dictionary in a project module so the mapping is data-driven and easy to extend without editing every action.

' === Project Module: mod_OPCBridge ===
Option Explicit

Public Sub Bridge_OPC_To_Internal()
    Dim map, k, oSrc, oDst, v
    Set map = CreateObject("Scripting.Dictionary")
    map.Add "OPC_Source_Temp",      "Internal_Temp_Mirror"
    map.Add "OPC_Source_State",     "Internal_State_Mirror"
    map.Add "OPC_Source_Pressure",  "Internal_Pressure_Mirror"
    map.Add "OPC_Source_ValvePos",  "Internal_ValvePos_Mirror"

    For Each k In map.Keys
        Set oSrc = HMIRuntime.Tags(k)
        Set oDst = HMIRuntime.Tags(map(k))
        oSrc.Read
        If oSrc.Quality = 0 Then
            v = oSrc.Value
            ' Type coercion: WinCC returns Variants; force the right type
            Select Case oDst.TypeName
                Case "Float":     v = CDbl(v)
                Case "Integer":   v = CLng(v)
                Case "Bool":      v = CBool(v)
                Case "String":    v = CStr(v)
            End Select
            oDst.Value = v
            oDst.Write
        End If
    Next
End Sub

Call Bridge_OPC_To_Internal from a cyclic global action. Adding a new mirror is now a one-line change in the dictionary.

6. Cycle Selection and Performance Budget

The cycle at which the bridge action runs is the single most important performance parameter. Faster is not better — every cycle triggers one Read and one Write per tag pair, and each call carries an OPC DA round-trip in the order of 1–5 ms on a 100 Mbit/s LAN. A conservative budget is shown below.

Number of Mirrored Tags Recommended Cycle Estimated CPU on a Core i5-6xxx Notes
1–10 500 ms < 1 % Safe default
11–50 1000 ms 1–3 % Typical HMI project
51–200 2000 ms 3–8 % Consider switching to a C-Action
> 200 5000 ms + batch grouping > 10 % Re-evaluate architecture: use the OPC subscription callback directly
Do not run a VBScript action faster than 250 ms. The WinCC scripting engine serializes action execution on a single thread, and actions faster than 250 ms can starve the Graphics Designer picture refresh on lower-end panels.

7. Inline SVG: Data-Flow Topology

Remote System OPC DA/UA Server (PC Base, non-upgradeable) WinCC OPC Channel Tag Management > OPC OPC_ tags (read/write) Internal Tags Internal_ mirror tags No I/O address VBScript Global Action Bridge_OPC_To_Internal() Cyclic 1 s, quality-gated Subscription Read .Value Trigger / re-Read Write .Value

8. Trigger Mechanisms: Cyclic vs Event-Driven

By default, global actions are configured as cyclic on a fixed interval. For tag values that change infrequently, an event-driven trigger that fires only when the OPC value actually changes is far more efficient.

Trigger Type Configuration Path When to Use CPU Cost
Cyclic 1 s Global Action > Triggers > Timer: 1 s Default; simple values; demo work Constant load
Tag-change Global Action > Triggers > Tag: OPC_Source_Temp Slow-changing process values (level, temp) Near zero at idle
Picture change Triggers > Picture: name Mirrors needed only on a specific screen Zero when picture closed
Hotkey Triggers > Hotkey: F12 One-shot manual sync (engineering tool) Zero until pressed
Tag-change triggers require a "standard" trigger type. Some WinCC versions only support cyclic triggers on global actions and require the use of a picture-level event for tag-change-driven logic. Verify against the project documentation before relying on this.

9. Cross-Platform Equivalents

When the runtime is not WinCC but an Ignition by Inductive Automation deployment, the same bridge can be implemented with much less code because Ignition natively differentiates between OPC items (direct references to the live PLC tag, no local copy) and tags (internal Ignition tags with optional OPC tag binding). If a true copy is required, the recommended approach in Ignition is a Tag History binding or an Expression tag with {[~]OPC/path/Tag} as the source expression. Drag-and-drop from the OPC Browser creates the tag in the Tag Browser window and preserves the binding automatically. See the Ignition 7.9 Browsing and Creating OPC Tags manual and the Creating OPC Tags Manually video for the canonical procedure.

For Kepware/KEPServerEX installations, the bridge can be implemented at the driver level using internal tags (sometimes called "advanced tags" or "client tags"). These tags live entirely inside the KEPServerEX process and are not visible in the server configuration, but they can be browsed by an OPC client and used as a way to hold a value computed from other tags. The KEPServerEX internal tags reference lists the special tag syntax used for this purpose.

10. Verification Procedure

After implementing the bridge, perform the following checks before declaring the work complete. Each step has an objective, the action to take, and the expected result.

  1. Verify OPC subscription state. Open WinCC Explorer > Tag Management > OPC > [connection]. The connection state must be OK and the OPC tags must show a green indicator. Result: All OPC_* tags show a numeric value and a quality of Good.
  2. Verify mirror values match. In the Graphics Designer, drop two I/O fields on a test picture, one bound to OPC_Source_Temp and one to Internal_Temp_Mirror. Force a value change at the source (e.g. write to the remote PLC). Result: Both fields update within one cycle (default 1 s) and show identical numeric values.
  3. Verify quality gating. Disconnect the OPC connection by stopping the remote OPC server. Result: The OPC_* tag quality goes to BadComm and the Internal_* tag retains the last good value. The WinCC Trace log shows the quality message from the script.
  4. Verify bidirectional write-back. Set Internal_Cmd_Reset to 1 from a test screen button. Result: The remote system's OPC_Dest_ResetAck tag transitions to 1 within the 500 ms cycle, holds for 1 second, then returns to 0. Check the remote HMI to confirm the acknowledgement was received.
  5. Verify CPU load. Open Windows Task Manager > Details, sort by CPU, and observe CCWriteArchive.exe and CCExplorer.exe. Result: Combined CPU stays under 5 % during a 10-minute steady-state run.
  6. Verify archive continuity. Open the WinCC Tag Logging editor and confirm the Internal_* tags are being archived. Result: No gaps in the archive timeline when the OPC source is healthy.

11. Troubleshooting Matrix

Symptom Root Cause Diagnostic Step Remediation
Internal tag stays at 0 OPC connection not established, or the tag was never dragged into the project Open Tag Management, browse the OPC namespace, confirm the tag exists and shows a current value Add the tag, or fix the OPC connection ProgID/CLSID
Value flickers between old and new Cyclic action reading the same value as a different cyclic action that also writes to the internal tag Search the project for duplicate Write calls to the same internal tag Centralize all writes in one bridge function
VBScript error "Type mismatch" on oDst.Write OPC value type and internal tag type differ (e.g. VT_I4Real) Add Debug.Print TypeName(oSrc.Value) and check the internal tag data type Apply explicit CDbl, CLng, or CBool coercion, or change the internal tag type
CPU pegged at 100 % after deployment Bridge running on a 100 ms cycle, or running in addition to a heavy archive Measure the cycle time of the global action in the WinCC diagnostic view Increase cycle to 1 s or higher; consider a C-Action
Stale value persisting after a remote PLC reboot Quality gating was not implemented; the last read returned 0 by default Inspect the Trace log for the quality message Implement the oSrc.Quality = 0 guard shown in Section 5.1
Bidirectional command fires continuously Missing edge detection; the Read value stays at 1 Add a HMIRuntime.Trace in the "If bRise Then" block Implement rising-edge detection with a _Prev shadow tag
WinCC runtime slow on startup Global action configured as "OnLoad" trigger, blocking picture rendering Check the trigger configuration in the global action properties Move the bridge to a 1 s cyclic trigger
Values desynchronized by 2–3 s OPC channel configured with a 2 s acquisition cycle, plus 1 s bridge cycle Inspect OPC Channel > Connection Properties > Acquisition Cycle Set the OPC acquisition cycle to match or be less than the bridge cycle

12. Field-Proven Caveats

  • Always declare Option Explicit at the top of every global action. WinCC's VBScript interpreter will silently create Variant variables for undeclared names, masking typos that are otherwise hard to find.
  • The HMIRuntime.Tags(...) object is a tag handle, not a value. Calling .Read populates .Value from the tag database. Calling .Write pushes .Value back. Forgetting .Read is the most common scripting bug.
  • String tags have a maximum length of 256 characters in WinCC V7.x. If the OPC source is a longer string, truncate explicitly with Left(s, 256) before writing, or the write will throw a runtime exception.
  • When the project is later migrated to WinCC Professional in TIA Portal, all VBScript global actions must be re-authored as C# scripts or as a Unified PC script. The VBScript surface is not carried over.
  • For compliance with 21 CFR Part 11 or similar audit frameworks, the Write path of the bridge must be logged. WinCC's Audit option (separately licensed) records tag writes automatically; without it, an unauthorized Write into the internal tag will not leave a trace.

FAQ

Can I copy an OPC tag value to an internal WinCC tag without writing a script?

No. Internal tags have no I/O address, so they cannot be bound directly to an OPC subscription. A bridge — VBScript, C-Action, or external script — is required. The VBScript patterns in Section 5 are the smallest possible implementation.

What is the fastest safe cycle for a VBScript bridge action?

500 ms for fewer than 50 mirrored tags, 1000 ms for 50–200, and 2000 ms above that. Avoid anything below 250 ms because the WinCC scripting engine is single-threaded and slower cycles starve the picture refresh on lower-end panels.

How do I prevent bad OPC values from overwriting good internal values?

Read the source tag's .Quality property after .Read and only call .Write when .Quality = 0 (Good). Quality code 0x80000000 is Bad, 0xC0000000 is BadComm, and 0x40000000 is Uncertain — in all three cases skip the write.

Why does my command fire continuously when I only press the button once?

Because the script polls the value every cycle. The Internal_Cmd_Reset tag stays at 1 as long as the button is held, so each cycle re-sends the write. Implement rising-edge detection with a shadow _Prev tag as shown in Section 5.2.

What is the Ignition equivalent of this bridge?

Ignition tags can reference OPC items directly via the {[~]OPC/path/Tag} expression syntax, which provides the same effect as a local copy without any script. If a true computed copy is needed, use an Expression tag or a Tag History binding. See the Ignition 7.9 OPC tag documentation for the canonical procedure.

Back to blog