Overview: Using WinCC as a Tag Bridge Between Two PLCs
SIMATIC WinCC is a supervisory control and data acquisition (SCADA) and human-machine interface (HMI) system from Siemens. In a typical plant, each controller (SIMATIC S7-1200, S7-1500, S7-300/400, or ET 200SP) holds the variables for its own island, and the HMI reads them for visualization. A frequent field request, however, is to drive a final-control element on PLC B from a sensor on PLC A when no direct PLC-to-PLC link (PUT/GET, Profinet I-Device, OPC UA server method) is available, available quickly, or approved by the network administrator.
The pattern in this article routes an ultrasonic level sensor (ULS) reading on PLC A to a pump start/stop command on PLC B by way of a WinCC Global Script. The HMI acts as a "transparent bridge": a periodic trigger reads the ULS tag from PLC A, evaluates a threshold with hysteresis, and writes the resulting boolean to a tag on PLC B. The technique is generic — any source value (level, pressure, flow, temperature) can replace the ULS reading, and any boolean/integer command (pump, valve, heater) can replace the pump output.
When to Use This Pattern (and When Not To)
Use the HMI-as-bridge pattern when all of the following are true:
- Direct PLC-to-PLC communication (S7 PUT/GET, Profinet I-Device, open IE communication) is blocked, undocumented, or pending a project change.
- The response time can tolerate the WinCC update cycle (default 1 s, minimum 250 ms in V7.x; configurable down to 100 ms in WinCC Unified V20).
- The controlled equipment has its own local hard-wired interlock (thermal overload, dry-run probe, level switch).
- The HMI server is in redundant operation or has a documented fallback.
Do not use this pattern for:
- Safety instrumented functions (SIF) at SIL 1 or higher — use a F-CPU with PROFIsafe.
- Hard real-time motion or coordinated drives (use Profinet IRT or Profibus DP).
- Functions that must continue to operate if the HMI server is offline (use direct PLC-PLC communication).
Prerequisites
| Item | Specification |
|---|---|
| WinCC version | WinCC V7.4 SP1 or later, or WinCC V8.0 / V8.1 (TIA-based Comfort/Professional), or WinCC Unified V17/V18/V20 |
| Licensing | WinCC RT (128 / 512 / 2048 / 8192 PowerTags), or RT Professional (V8.x), or Unified RT (V17+) |
| PLC A (sensor) | SIMATIC S7-1200 (FW 4.2+), S7-1500, or ET 200SP CPU; project must expose the ULS raw value to WinCC |
| PLC B (pump) | SIMATIC S7-1200/1500/300/400; must have a writable tag (e.g. DB20.DBX0.0) for the pump command |
| Network | Ethernet (Profinet) on a common subnet with TCP/IP routing; the HMI must reach both PLCs |
| Channel driver | SIMATIC S7-1200/1500 Channel (preferred), or OPC UA client to a S7-1500 OPC UA server |
| Engineering tool | WinCC Explorer (V7/V8) or TIA Portal with HMI configuration (V15.1+) |
System Architecture and Data Flow
The data flow is strictly one-way at the script boundary. The HMI reads from PLC A and writes to PLC B on a fixed cadence.
- PLC A scales the ULS 4–20 mA signal to a level value (m) and stores it in a data block (e.g.
DB50.DBD0asREAL). - WinCC has a configured external tag
ULS_Level_m(REAL, length 4 bytes) pointing toDB50.DBD0via the S7 channel. - A Global Script (VBScript) is scheduled with a 1-second trigger.
- Each trigger tick: the script reads
ULS_Level_m, compares it with a configured threshold (with hysteresis), and computes a boolean command0(stop) or1(start). - The script writes the boolean to
Pump_Command_HMI, an external tag in WinCC that is mapped toDB20.DBX0.0on PLC B. - PLC B applies its own local logic (run permissive, motor contactor feedback, VFD enable) before energising the pump.
Pump_Command_HMI. PLC B should include a watchdog that trips the pump to a safe state if the HMI command does not refresh within a defined window (e.g. a 5-second timer reset on each valid write).Step 1: Configure the External Tags
Open WinCC Explorer and create two external tags under the SIMATIC S7-1200/1500 channel.
| WinCC tag | PLC | Address | Data type | Length | Direction |
|---|---|---|---|---|---|
ULS_Level_m |
PLC A | DB50.DBD0 | FLOAT (REAL) | 4 B | read-only |
Pump_Command_HMI |
PLC B | DB20.DBX0.0 | BOOL | 1 bit | read/write |
Also create three internal tags for threshold, hysteresis, and last-command memory:
-
ULS_Threshold_m—FLOAT, initial value 2.50 (pump trips off at 2.50 m) -
ULS_Hysteresis_m—FLOAT, initial value 0.10 (pump re-arms 0.10 m below threshold) -
Pump_LastCommand—BOOL, initial value 0
Tag settings in the WinCC Tag Management dialog:
- Right-click → Add New Tag, name it
ULS_Level_m. - Set Type = External tag, select the SIMATIC S7-1200/1500 Channel connection that targets PLC A.
- Enter address
DB50,DBD0, data typeFloat 32-bit IEEE 754. - Repeat for
Pump_Command_HMIagainst the connection to PLC B, addressDB20,DBX0.0, data typeBinary Tag.
Step 2: Verify Tag Communication with the Channel Diagnostics
Before adding any scripting, confirm the tags are live:
- In WinCC Explorer, right-click the channel and select Channel Diagnostics.
- Watch the connection state to PLC A and PLC B. Green = connection established, yellow = establishing, red = error.
- Open Tag Simulator or a temporary I/O field bound to
ULS_Level_min Graphics Designer. - Force a value into
DB50.DBD0on PLC A using a watch table in TIA Portal and verify it appears on the HMI faceplate within the configured update time.
Step 3: Build the Global Script
Open Global Script → VBS Editor in WinCC Explorer. Create a new action, paste the following routine, and save it as ULS_PumpBridge.
Option Explicit
Function action
' --- Configuration constants (WinCC internal tags) ---
Const TAG_LEVEL = "ULS_Level_m" ' REAL from PLC A
Const TAG_THRESH = "ULS_Threshold_m" ' REAL, internal
Const TAG_HYST = "ULS_Hysteresis_m" ' REAL, internal
Const TAG_LASTCMD = "Pump_LastCommand" ' BOOL, internal
Const TAG_PUMP = "Pump_Command_HMI" ' BOOL to PLC B
Const TAG_STATUS = "HMI_BridgeStatus" ' WORD, internal, 0=OK 1=Stale 2=Error
Dim tLevel, tThresh, tHyst, tLast, tPump, tStat
Dim level, threshold, hysteresis, lastCmd, pumpCmd, status
status = 0
Set tLevel = HMIRuntime.Tags(TAG_LEVEL)
Set tThresh = HMIRuntime.Tags(TAG_THRESH)
Set tHyst = HMIRuntime.Tags(TAG_HYST)
Set tLast = HMIRuntime.Tags(TAG_LASTCMD)
Set tPump = HMIRuntime.Tags(TAG_PUMP)
Set tStat = HMIRuntime.Tags(TAG_STATUS)
If tLevel.Read = 0 And tThresh.Read = 0 And tHyst.Read = 0 And tLast.Read = 0 Then
level = tLevel.Value
threshold = tThresh.Value
hysteresis = tHyst.Value
lastCmd = tLast.Value
' --- Hysteresis decision logic ---
If level >= threshold Then
pumpCmd = 0 ' high level: stop
ElseIf level < (threshold - hysteresis) Then
pumpCmd = 1 ' recovered: start
Else
pumpCmd = lastCmd ' in dead-band: hold
End If
' --- Write to PLC B ---
tPump.Value = pumpCmd
tPump.Write
' --- Persist state ---
tLast.Value = pumpCmd
tLast.Write
' --- Status ---
tStat.Value = status
tStat.Write
Else
' QualityCode <> 0 on any tag: signal stale and revert to safe
tStat.Value = 1
tStat.Write
tPump.Value = 0 ' safe: stop pump
tPump.Write
End If
End Function
Line-by-line notes
-
Option Explicitforces every variable to be declared, eliminating a common source of Type mismatch runtime errors. - The
Readmethod returns the WinCC quality code (0 = Good). If any of the four reads returns non-zero, the script assumes the HMI lost contact with a PLC and forces the pump to the safe state (0 = stop). - The hysteresis dead-band prevents chattering when the surface ripples around the setpoint.
-
HMIRuntime.Tags(...).Writeperforms an immediate write; for higher throughput, wrap multiple writes inTags.Item(...).Writewith a single HMI variable list.
Read. Always check this value before acting on .Value, otherwise a stale tag will silently propagate bad data into PLC B.Step 4: Configure the Trigger
Global Scripts in WinCC V7/V8 execute on a defined trigger. The choice of trigger directly controls the response time of the bridge.
- Open the action's Properties dialog and select the Trigger tab.
- Add a Time trigger. The minimum reliable value is 1 second; values below 500 ms can starve the script runtime queue under heavy graphics load.
- Optionally add a Tag trigger on
ULS_Level_mwith a change threshold (e.g. 0.05 m). This couples execution to actual process change and reduces CPU load. - Save the action. The trigger editor shows the next fire time and current period.
| Trigger type | Recommended setting | Use case |
|---|---|---|
| Time (cyclic) | 1 s | Slow processes (tank level, sump) |
| Time (cyclic) | 250 ms | Fast processes, only if HMI CPU is < 60 % |
| Tag change | Threshold 0.05 m on ULS_Level_m | Reduces work when level is stable |
| Hotkey | User action | Manual override / engineering test |
Step 5: Runtime and Startup Settings
- In WinCC Explorer, open Computer → Properties → Startup tab.
- Enable Global Script Runtime. Without this checkbox, the VBS actions never execute, even though the editor compiles them.
- Add the action
ULS_PumpBridgeto the Startup list if you want it to run once at WinCC startup (for example, to initialise the internal tags). A cyclic trigger will also fire on its own, so this is optional. - If WinCC is on a redundant server pair, replicate the script and tag list to the standby and confirm the licence covers both.
Step 6: Verification and Commissioning
Validate the bridge in three layers: tag level, script level, and process level.
-
Tag level: Use an I/O field in Graphics Designer bound to
ULS_Level_mto confirm the ULS reading updates. -
Script level: Open the Global Script diagnostic view (Ctrl + F12 in the VBS editor) and watch
HMI_BridgeStatus. The value should remain 0 (OK) under healthy conditions. -
Process level: Force
DB50.DBD0on PLC A to 3.00 m (above threshold) using TIA Portal. Within one cycle,Pump_Command_HMIshould write0to PLC B. Force 1.50 m and the script should write1after the dead-band passes.
| Forced level (m) | Expected Pump_Command_HMI
|
Pass criterion |
|---|---|---|
| 0.50 | 1 (start) | Within 1 s of trigger fire |
| 2.45 | 1 (hold, in dead-band) | No change from last command |
| 2.55 | 0 (stop) | Within 1 s of trigger fire |
| Stale tag (unplug PLC A) | 0 (safe stop) and HMI_BridgeStatus = 1
|
Within 1 s of failure detection |
Alternative 1: Direct PLC-to-PLC S7 Communication
If the network allows it, replace the HMI bridge with a direct S7 connection. PLC A is the S7 client, PLC B is the S7 server. Two common methods are:
-
S7 PUT/GET — Available on S7-1200 (FW 4.0+ with the "Permit access with PUT/GET" option) and S7-1500. Configure a
PUTblock in PLC A to copy the level value to a tag in PLC B every cycle. This is the most common cross-PLC pattern and survives HMI outages. - Profinet I-Device — Configure PLC A as an I-Device with a Profinet slot for the level value, and PLC B consumes that slot as if it were a remote I/O module. Latency is sub-millisecond.
These methods remove the HMI from the control path, which is the recommended approach whenever the network topology permits.
Alternative 2: OPC UA Method Routing
For S7-1500 controllers, the OPC UA server in the CPU (FW 2.0+) can expose a method that the HMI calls. The HMI script then becomes a single method invocation rather than a periodic tag write, which is cleaner and gives the PLC authority to validate the call.
- In the S7-1500 OPC UA server configuration, enable the Methods option and publish a method
SetPumpCommandwith one input argument of type Boolean. - In the WinCC Unified V20 tag browser, drag the method onto a button.
- The button's OnClick event calls the method, passing the calculated
pumpCmdas a parameter.
This pattern works for both WinCC Unified and for an external SCADA (Ignition, FactoryTalk) and is documented in the WinCC V8.1 Working with Controls manual.
Alternative 3: WinCC Unified V20 JavaScript
WinCC Unified V20 uses JavaScript instead of VBScript. The equivalent global script lives under Scripts → Global module and is triggered from a scheduled task.
// WinCC Unified V20 — scheduled task body
export async function Bridge_ULS_ToPump() {
const level = await Tags("ULS_Level_m").Read();
const threshold = await Tags("ULS_Threshold_m").Read();
const hysteresis = await Tags("ULS_Hysteresis_m").Read();
let cmd;
if (level.value >= threshold.value) {
cmd = 0; // stop
} else if (level.value < (threshold.value - hysteresis.value)) {
cmd = 1; // start
} else {
cmd = await Tags("Pump_LastCommand").Read().then(r => r.value);
}
await Tags("Pump_Command_HMI").Write(cmd);
await Tags("Pump_LastCommand").Write(cmd);
await Tags("HMI_BridgeStatus").Write(level.qualityCode === 0 ? 0 : 1);
}
Unified benefits over V7/V8:
- Native
async/awaitremoves the manual quality-code branching that VBScript requires. - Triggers can be 100 ms with deterministic execution.
- Cross-platform (PC, Panel, Edge) without recompile.
Reference: Process control (RT Unified) - WinCC Unified.
Safety and Reliability Considerations
An HMI-driven control path introduces failure modes that a direct PLC link does not. Address these during design review:
-
HMI server loss. PLC B must contain a watchdog timer reset by the most recent
Pump_Command_HMIwrite. If the HMI is offline for more than the watchdog window, PLC B forces the pump to a defined safe state (typically stop). - HMI overload. A 250 ms trigger under heavy graphics load can be deferred. The watchdog above also covers this.
-
Initial state on WinCC startup. The
Pump_LastCommandinternal tag persists the last command. After a power-cycle, the bridge resumes from the last known state. If the operator requires a manual acknowledgement on restart, the bridge should write0on the first trigger tick after startup. - Network partition. WinCC will detect the loss of either PLC connection through the S7 channel quality code. The script's quality-code branch forces the safe state.
- Cyber security. Allow only authenticated S7 connections and disable the HMI's external WebUX (if not required) to reduce the attack surface. A cross-PLC bridge inside the HMI is a tempting pivot point for an attacker.
Troubleshooting Matrix
| Symptom | Likely cause | Resolution |
|---|---|---|
| Pump never responds to level change | Global Script Runtime not enabled | Computer → Properties → Startup → tick Global Script Runtime, restart runtime |
| Pump chatters at threshold | No hysteresis in script | Set ULS_Hysteresis_m to 5–10 % of threshold |
| Script log shows Type mismatch | Internal tag data type mismatch | Re-create ULS_Threshold_m as Float 32-bit IEEE 754, not Integer |
Quality code 6 on ULS_Level_m
|
PLC A connection lost | Check channel diagnostics, ping PLC A, verify TSAP and rack/slot |
Pump_Command_HMI writes flicker 0/1/0/1 |
Trigger period shorter than S7 write time | Lengthen trigger to 1 s, or move to a single batched tag list write |
| HMI_BridgeStatus stuck at 1 after recovery | Status tag not cleared on success branch | Verify the status = 0 write executes; check that Read quality code on each tag is 0 |
| Pump trips on every WinCC restart |
Pump_LastCommand initialised to 1 |
Change initial value to 0, or add a startup handshake in PLC B |
FAQ
Can I route any tag type through this pattern, or only booleans?
Any tag type works. The example uses a REAL (level) and a BOOL (command), but the same HMIRuntime API handles INT, WORD, DWORD, and STRING. For strings larger than 256 bytes, use the S7 channel's raw pointer or move the data via a sequenced PUT/GET rather than a global script.
What is the minimum reliable trigger period for a WinCC V7.x Global Script?
500 ms is the practical floor for a single-action script on a typical IPC. Values below 250 ms can collide with the WinCC graphics scheduler and produce non-deterministic firing. For sub-second response, switch to WinCC Unified V20, where 100 ms scheduled tasks are supported.
Will the bridge still work if WinCC Runtime is restarted while the plant is running?
The HMI loses contact with both PLCs during the restart. PLC B should contain a watchdog that trips the pump to a safe state if no command refresh arrives within 3–5 seconds. When WinCC returns, the first trigger tick re-establishes the bridge and Pump_Command_HMI resumes from Pump_LastCommand (or from a forced 0 if you add a startup reset).
Why is the quality code from HMIRuntime.Tags(...).Read important?
A non-zero quality code means the HMI's last cached value for that tag is stale (PLC unreachable, address invalid, or driver not licensed). Acting on a stale value is the most common cause of phantom control commands. The script in this article treats any non-zero quality as a fault and forces the pump off.
Is there a way to do this without writing a VBScript at all?
Yes, if both PLCs support OPC UA. Enable the OPC UA server on PLC A and PLC B, configure the HMI as an OPC UA client, and use a tag connection with a derived/internal tag to do the mapping. For level-to-command logic with hysteresis, however, the script remains the simplest implementation, because the decision must live somewhere in the runtime.