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.
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
- 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.
- An active OPC DA connection in the WinCC project. Verify in WinCC Explorer > Tag Management > OPC > [connection name] that the connection state is
OKand that at least one OPC tag is returning a non-bad quality code. - 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\TagNamewhen referenced by VBScript via the internalOPCServer.Namespace, or the plain tag name when referenced through theHMIRuntimeobject. - 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. - 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.
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
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 |
7. Inline SVG: Data-Flow Topology
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 |
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.
-
Verify OPC subscription state. Open WinCC Explorer > Tag Management > OPC > [connection]. The connection state must be
OKand the OPC tags must show a green indicator. Result: AllOPC_*tags show a numeric value and a quality ofGood. -
Verify mirror values match. In the Graphics Designer, drop two I/O fields on a test picture, one bound to
OPC_Source_Tempand one toInternal_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. -
Verify quality gating. Disconnect the OPC connection by stopping the remote OPC server. Result: The
OPC_*tag quality goes toBadCommand theInternal_*tag retains the last good value. The WinCC Trace log shows the quality message from the script. -
Verify bidirectional write-back. Set
Internal_Cmd_Resetto1from a test screen button. Result: The remote system'sOPC_Dest_ResetAcktag transitions to1within the 500 ms cycle, holds for 1 second, then returns to0. Check the remote HMI to confirm the acknowledgement was received. -
Verify CPU load. Open Windows Task Manager > Details, sort by CPU, and observe
CCWriteArchive.exeandCCExplorer.exe. Result: Combined CPU stays under 5 % during a 10-minute steady-state run. -
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_I4 → Real) |
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 Explicitat 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.Readpopulates.Valuefrom the tag database. Calling.Writepushes.Valueback. Forgetting.Readis 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
Writepath of the bridge must be logged. WinCC's Audit option (separately licensed) records tag writes automatically; without it, an unauthorizedWriteinto 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.