WinCC VBScript Excel Export: Fixing Slow Tag Reads with TagSet

David Krause13 min read
SiemensTroubleshootingWinCC
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

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:

  1. Resolve tag name to the configured channel/unit connection.
  2. Issue a single request to the PLC driver (e.g., S7COMM PDU read for S7-1200/S7-1500, or fetch for OPC UA subscription).
  3. Wait for the driver's response/timeout.
  4. Update the local tag cache.
  5. Return .Value to 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.

Engineering caveat: The 50-minute figure assumes WinCC is performing individual driver requests without bundling. On some driver configurations (large S7 PDU size, OPC UA batching enabled), the cost per tag is lower, but the order of magnitude difference between per-tag reads and a single group read remains. Always measure on the production-class network and PLC load before finalizing export scripts.

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

  1. Validate the API surface. Confirm the running WinCC version is at minimum V6 SP3 (or the TIA Portal equivalent). On older versions CreateTagSet does not exist and there is no scripting-side workaround for the per-tag latency.
  2. 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.
  3. Replace the per-tag read loop with the CreateTagSet pattern. Maintain the same tag-to-cell mapping to avoid breaking downstream Excel consumers.
  4. Add diagnostic trace calls at script entry, after the group read, and after the workbook save. Use HMIRuntime.Trace Now & vbCrLf so the WinCC diagnostic file (apdiag.log) records wall-clock time.
  5. Disable Excel display alerts and autosave with objExcel.Application.DisplayAlerts = False and objExcel.Application.ScreenUpdating = False to remove UI-thread stalls from the timing window.
  6. 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).
  7. Release COM objects explicitly with Set objX = Nothing at 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

  1. Run the script with a Trace on entry and exit. Use HMIRuntime.Trace Now & " export start" & vbCrLf and a matching "export end" line. Open the WinCC apdiag file and confirm the wall-clock delta matches expectation (seconds, not minutes).
  2. Inspect the generated workbook. Open filled.xls in Excel and confirm columns 5–7 contain the expected values for rows 2–25 (for a 24-iteration loop).
  3. 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.
  4. 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.
  5. 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.Tags JavaScript 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 TagSet JavaScript 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)":

  1. In the Inspector window, open Properties > Properties > Data export.
  2. Specify the export file path and file format (CSV or XML).
  3. Bind the export trigger to a button event or scheduled task.
  4. 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

Licensing note: Exporting historical/process data to Excel for reporting purposes is a documented runtime capability and is covered by the standard WinCC Runtime license. Recipe / historical archive export via specific licensed add-ons (e.g., WinCC/DataMonitor) is a separate scope and is not enabled by the VBScript + TagSet pattern alone. Confirm the project licensing before relying on automated reports in regulated environments.
  • 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; the xls format and certain xlsx configurations 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.

Back to blog