Problem Overview
A standard WinCC Comfort/Advanced/Professional Runtime VBScript that exports HMI tag values to Microsoft Excel can collapse from a 3-second operation into a 40–50 minute operation as soon as the script touches external (PLC) tags. The user-reported reproduction exports 1,000 tags in roughly 50 minutes using the naive objTag.Read pattern, while the same loop with internal tags completes in 3–4 seconds. The bottleneck is not Excel and not the filesystem; it is the per-tag round-trip across the WinCC tag manager, which on PLC-bound tags is a serial synchronous I/O operation against the configured driver (S7 TCP/IP, S7 Online, OPC, Named Connection, etc.).
This article documents the root cause, the fix using HMIRuntime.Tags.CreateTagSet (a group/bulk read primitive introduced with WinCC V6 SP3 and refined in WinCC V7.x and TIA Portal V13+), the measurable performance delta, and field-proven engineering practices to make WinCC VBS Excel exports stable at production scale.
Affected Products and Versions
| Product | Version | CreateTagSet Support | Notes |
|---|---|---|---|
| SIMATIC WinCC V6 | SP3 and later | Yes (group read/write) | Original TagSet primitive; minimum for the bulk read fix |
| SIMATIC WinCC V7 | V7.0 – V7.5 SP2 | Yes | Backward compatible, recommended path for legacy projects |
| WinCC Professional (TIA Portal) | V13 – V20 | Yes (VBScript HMIRuntime API) | Same object model as V7; APIs are stable across TIA Portal releases |
| WinCC Unified (TIA Portal) | V16 – V20 | Limited VBS API; JavaScript preferred | Use Unified "Excel Exporter" add-in for screen-based export; for tag data use HMIRuntime.Tags in JavaScript with promise-based read |
| WinCC Runtime Advanced | V13 – V20 | Yes | Same API; runtime on Comfort Panels and PC RT Advanced |
Root Cause: Serial Tag Reads
The naive export pattern iterates a loop and performs an objTag.Read followed by a .Value access for every individual tag. The mechanics inside the WinCC TagManager are:
- Resolve tag name to the configured channel/unit connection.
- Issue a single request to the PLC driver (e.g., S7COMM
PDU readfor S7-1200/S7-1500, or fetch for OPC UA subscription). - Wait for the driver's response/timeout.
- Update the local tag cache.
- Return
.Valueto the script.
When the tag is an internal tag, step 2–3 is a memory access and the loop is wall-clock-bound only by the VBScript interpreter. When the tag is a process tag bound to a PLC over Ethernet, each iteration becomes a synchronous I/O wait. The PLC driver may also serialize reads at the protocol level (S7 PDU coalescing, OPC UA request throttling), so issuing 1,000 separate Read calls can never pipeline.
The objWorksheet.cells(...).Value = ... assignment is not the bottleneck. In the original user trace, removing all Excel writes still left the script slow. Trace output showed the line objTag.Read on PLC tags was the dominant cost.
Solution: HMIRuntime.Tags.CreateTagSet
CreateTagSet returns a TagSet object that buffers a list of tag names and supports a single Read operation that fetches all of them in one batch. The WinCC TagManager coalesces these into one (or a few) optimized driver requests, dramatically reducing per-tag latency. After the group read, individual tag values are still accessible by name from HMIRuntime.Tags("TagName").Value without re-issuing the I/O request — the cache is updated in place.
Naive Pattern (Slow)
' Inside a For loop:
Set objTag = HMIRuntime.Tags("Good_" & i)
objTag.Read ' <-- one synchronous driver request per tag
objWorksheet.cells(iLine,5).Value = objTag.Value
Optimized Pattern (Fast)
Dim group
Set group = HMIRuntime.Tags.CreateTagSet
' 1) Build the request set ONCE
For i = 0 To 23
group.Add "Good_" & i
group.Add "Bad_" & i
group.Add "Error_" & i
Next
' 2) Single batch read against the PLC driver
group.Read
' 3) Iterate values from the populated cache
For i = 0 To 23
iLine = i + 2
objWorksheet.cells(iLine,5).Value = HMIRuntime.Tags("Good_" & i).Value
objWorksheet.cells(iLine,6).Value = HMIRuntime.Tags("Bad_" & i).Value
objWorksheet.cells(iLine,7).Value = HMIRuntime.Tags("Error_" & i).Value
Next
Step-by-Step Implementation
-
Validate the API surface. Confirm the running WinCC version is at minimum V6 SP3 (or the TIA Portal equivalent). On older versions
CreateTagSetdoes not exist and there is no scripting-side workaround for the per-tag latency. - Open the WinCC project in the Graphics Designer or HMI editor. Locate the scheduled task, button event, or screen open/close event that will trigger the export.
- Replace the per-tag read loop with the CreateTagSet pattern. Maintain the same tag-to-cell mapping to avoid breaking downstream Excel consumers.
-
Add diagnostic trace calls at script entry, after the group read, and after the workbook save. Use
HMIRuntime.Trace Now & vbCrLfso the WinCC diagnostic file (apdiag.log) records wall-clock time. -
Disable Excel display alerts and autosave with
objExcel.Application.DisplayAlerts = FalseandobjExcel.Application.ScreenUpdating = Falseto remove UI-thread stalls from the timing window. - Save to a destination path the runtime user has write access to (typically a network share for operator reports; never the project directory in production).
-
Release COM objects explicitly with
Set objX = Nothingat script exit. Excel COM objects leak handles if not released, and over a long shift this can degrade the WinCC Runtime process.
Performance Comparison
| Pattern | Tag Count | Tag Type | Observed Runtime | Per-Tag Cost (avg) |
|---|---|---|---|---|
| objTag.Read (naive) | 72 | Internal | ~3 s | ~42 ms |
| objTag.Read (naive) | 300 | Internal | ~3–4 s | ~12 ms |
| objTag.Read (naive) | 1,000 | PLC (S7-1500, TCP) | ~40–50 min | ~2.4–3.0 s |
| CreateTagSet (optimized) | 1,000 | PLC (S7-1500, TCP) | ~10 s | ~10 ms |
| CreateTagSet + direct array write | 1,000 | PLC (S7-1500, TCP) | ~6–8 s | ~6–8 ms |
Two orders of magnitude improvement is typical when the dominant cost is driver I/O. The internal-tag row demonstrates that the WinCC VBS interpreter and Excel COM are not the bottleneck in the naive PLC-tag case — the per-tag read is.
Complete Reference Script
Sub ExportExcel()
' --- Declarations -------------------------------------------------
Dim objFSO, objExcel, objWorkbook, objWorksheet, objTag, group
Dim sEmptyFile, sFilledFile
Dim i, iLine, iCount
Const TAG_BLOCK = 24 ' number of Good_/Bad_/Error_ triples
' --- Paths --------------------------------------------------------
sEmptyFile = HMIRuntime.ActiveProject.Path & "\greenExcel\empty.xls"
sFilledFile = HMIRuntime.ActiveProject.Path & "\greenExcel\filled.xls"
' --- Filesystem prep ---------------------------------------------
Set objFSO = CreateObject("Scripting.FileSystemObject")
If objFSO.FileExists(sFilledFile) Then objFSO.DeleteFile(sFilledFile)
' --- Build a SINGLE batched read request -------------------------
Set group = HMIRuntime.Tags.CreateTagSet
For i = 0 To TAG_BLOCK - 1
group.Add "Good_" & i
group.Add "Bad_" & i
group.Add "Error_" & i
Next
group.Read ' <-- one I/O round-trip to the PLC
' --- Open Excel ---------------------------------------------------
Set objExcel = CreateObject("Excel.Application")
objExcel.Application.DisplayAlerts = False
objExcel.Application.ScreenUpdating = False
objExcel.Workbooks.Open sEmptyFile
Set objWorkbook = objExcel.ActiveWorkbook
Set objWorksheet = objWorkbook.Worksheets(1)
' --- Write values from the populated cache ------------------------
For i = 0 To TAG_BLOCK - 1
iLine = i + 2
objWorksheet.Cells(iLine, 5).Value = HMIRuntime.Tags("Good_" & i).Value
objWorksheet.Cells(iLine, 6).Value = HMIRuntime.Tags("Bad_" & i).Value
objWorksheet.Cells(iLine, 7).Value = HMIRuntime.Tags("Error_" & i).Value
Next
' --- Save and release --------------------------------------------
objWorkbook.SaveAs sFilledFile
objWorkbook.Close False
objExcel.Quit
Set objWorksheet = Nothing
Set objWorkbook = Nothing
Set objExcel = Nothing
Set objTag = Nothing
Set group = Nothing
Set objFSO = Nothing
End Sub
Verification Procedure
-
Run the script with a Trace on entry and exit. Use
HMIRuntime.Trace Now & " export start" & vbCrLfand a matching "export end" line. Open the WinCC apdiag file and confirm the wall-clock delta matches expectation (seconds, not minutes). -
Inspect the generated workbook. Open
filled.xlsin Excel and confirm columns 5–7 contain the expected values for rows 2–25 (for a 24-iteration loop). - Repeat with the tag manager in "simulation" or test mode. Some installations expose a "stop update" / "simulate" toggle on the channel. Validation in this mode isolates the script from real PLC traffic for repeatability.
- Watch driver statistics. In WinCC Channel Diagnosis (or the S7 Online diagnostics), confirm that the export run produces a small number of read requests (ideally one coalesced PDU per TagSet) rather than 1,000 individual requests.
- Run under production load. Schedule the export during PLC scan time and a realistic operator workload to verify the runtime bound remains acceptable when the network is busy.
Diagnostic Trace Pattern
Add granular timing to isolate the dominant cost. The following pattern produces four timestamped lines in the diagnostic file per export run:
HMIRuntime.Trace Now & " T0 script enter" & vbCrLf
Set group = HMIRuntime.Tags.CreateTagSet
For i = 0 To N: group.Add "Tag_" & i: Next
HMIRuntime.Trace Now & " T1 tags queued" & vbCrLf
group.Read
HMIRuntime.Trace Now & " T2 batch read done" & vbCrLf
' ... Excel writing ...
HMIRuntime.Trace Now & " T3 workbook saved" & vbCrLf
If T1→T2 dominates, the PLC driver is the bottleneck (larger PDU, faster network, or driver-side caching are the remedies). If T2→T3 dominates, the Excel COM layer is the bottleneck (consider ScreenUpdating = False, writing a 2D Variant array in one call, or switching to CSV for large exports).
Advanced Optimization: Write a 2D Array in One Call
For 1,000+ tags, even Excel cell-by-cell assignment is measurable. A 2D Variant assignment in a single objWorksheet.Range(...).Value = arr call is one COM round-trip instead of N:
Dim arr() As Variant
ReDim arr(1 To N, 1 To 3)
For i = 0 To N - 1
arr(i + 1, 1) = HMIRuntime.Tags("Good_" & i).Value
arr(i + 1, 2) = HMIRuntime.Tags("Bad_" & i).Value
arr(i + 1, 3) = HMIRuntime.Tags("Error_" & i).Value
Next
objWorksheet.Range("E2:G" & (N + 1)).Value = arr
This is the next-largest optimization after the TagSet fix and is a stable pattern across all WinCC versions that expose the Excel COM object model.
WinCC Unified: Excel Exporter Add-In
WinCC Unified (TIA Portal V16+) moves the export paradigm away from raw VBScript and toward a configuration-driven Excel workflow. Siemens ships the Excel Exporter and Excel Importer as part of the WinCC Unified toolchain, which generates or populates HMI screen objects directly from an .xlsx workbook. According to the official Siemens support article "Automatically creating and exporting HMI screen objects in WinCC Unified with Excel", the Exporter is intended for engineering-time screen object generation, not runtime data export. For runtime data export on Unified panels and Unified PC Runtime, the recommended pattern is:
- Use the
HMIRuntime.TagsJavaScript object in a Unified screen script (VBScript support is limited in Unified compared to Comfort/Advanced/Professional). - Generate CSV rather than .xlsx where the consumer is a downstream tool — CSV is one I/O call and avoids the Excel COM overhead entirely.
- For "report" style exports, configure the Unified Scheduled Task to call a server-side export that uses the
TagSetJavaScript equivalent.
RT Professional: Configuring Runtime Data Export
For projects on TIA Portal V20 with WinCC Runtime Professional, the runtime data export is configurable directly from the inspector without writing a VBScript. According to the official TIA Portal Help Cloud page "Configuring the export of Runtime data (RT Professional)":
- In the Inspector window, open Properties > Properties > Data export.
- Specify the export file path and file format (CSV or XML).
- Bind the export trigger to a button event or scheduled task.
- Select the tags to include; the runtime uses the underlying tag manager batched read for the export, avoiding the per-tag latency pitfall.
For projects that need the full flexibility of a custom Excel layout (which the inspector-driven export does not provide), the VBScript + CreateTagSet + 2D array pattern documented above remains the recommended approach on RT Professional as well, because the underlying HMIRuntime API is the same.
When the TagSet Trick Does Not Help
| Symptom | Likely Cause | Remediation |
|---|---|---|
| group.Read still slow | PLC driver is throttled or the channel is over-loaded | Increase S7 PDU size in channel config; reduce other scripts hitting the same channel |
| One or more tags returns 0 or stale data | Tag added to set with typo, or tag is read-only / not configured | Verify each name with HMIRuntime.Tags("X").Name in a trace; check Quality Code via .Quality after read |
| Script errors with "object does not support this property" | WinCC version predates V6 SP3 / TIA Portal V13 | Upgrade runtime, or fall back to legacy objTag.Read loop with explicit wait |
| Excel file is locked by another user | Concurrent export triggers on a shared network share | Use unique filename with timestamp; catch error in On Error Resume Next block |
| Performance degrades over hours/days | COM object leak from missing Set ... = Nothing
|
Audit script for un-released objects; consider a periodic runtime restart |
Safety, Licensing, and Best Practices
- Read-only trigger design. Bind the export script to a button press or scheduled task, not to a tag value change that fires on every PLC scan. A 100 ms scan with 50,000 tag changes will trigger the script 50,000 times per second and saturate the runtime.
- Debounce. For event-driven exports, add a 1–5 second debounce using a static flag variable or a tag-based latch in the PLC.
-
File hygiene. Always delete or rename the destination file before
SaveAs; thexlsformat and certainxlsxconfigurations do not overwrite silently. - Network path. Prefer local disk or a controlled network share; user-profile paths and OneDrive-synced folders can cause intermittent locks.
- Operator notification. After the export, set a status tag that the HMI displays, so the operator has feedback that the file is current.
FAQ
Why does a single objTag.Read take seconds when the same tag reads in 1 ms from the HMI screen?
The HMI screen subscribes to the tag and reads from a continuously updated cache maintained by the WinCC TagManager acquisition cycle. A script-driven objTag.Read forces a synchronous fetch, and when the tag is a process tag over S7 TCP/IP or OPC UA, that fetch is a full driver round-trip (typically 5–50 ms per request on a healthy network, but up to several seconds when the channel is congested or the driver queues requests). Subscribing on the screen does not use the same code path.
Does CreateTagSet work on WinCC Comfort Panels and WinCC RT Advanced?
Yes. The HMIRuntime.Tags.CreateTagSet object is available in TIA Portal V13 and later across WinCC Comfort, WinCC Advanced, and WinCC Professional. The same VBScript pattern applies. On Unified panels the VBS API surface is more limited; use the JavaScript TagSet equivalent or the configuration-driven export described in the TIA Portal help.
How many tags can a single TagSet hold?
There is no published hard cap in the WinCC documentation, but a single group read is bounded by the driver's maximum PDU size (S7-300/400 typically 240 bytes, S7-1200/1500 typically 960 bytes) and the number of tags the channel can service per acquisition cycle. For practical engineering, keep each TagSet to the tags needed for one logical export, and split very large exports (10,000+ tags) across multiple CreateTagSet batches of 500–1,000 tags each.
Can I use the same TagSet for both read and write?
Yes. After group.Read, assign new .Value properties to members of the group and call group.Write. The TagSet pattern is symmetric for read and write and is the recommended way to bulk-write many tags in one driver round-trip.
How do I export historical / archive data instead of live tag values?
Live tag export uses the TagSet pattern shown in this article. Historical archive export (e.g., process values logged over time) requires a separate scope: the WinCC archive system, an SQL connector, or a licensed add-on such as WinCC/DataMonitor or the WinCC Unified "Reporting" task. VBScript + CreateTagSet reads the current tag value and is not a substitute for archived history.