WinCC VBScript: Loop HMI Tags with Counter for Excel Export

David Krause12 min read
HMI ProgrammingSiemensTutorial / 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

WinCC VBScript: Loop HMI Tags with a Counter and Export to Excel

When an HMI project grows to dozens of similarly named tags (for example, twelve recipe data blocks, fifty counter values, or a hundred axis parameters), hard-coding one VBScript line per tag becomes unmanageable. The fix is the same technique used in every other language: build the tag name as a string, concatenate an integer counter, and execute the read/write inside a For...Next loop. This article documents the working pattern for SIMATIC WinCC Professional / TIA Portal and WinCC Runtime Advanced, shows the corrected loop syntax, the Excel automation model, and a fallback path that exports an Online Table Control to CSV without enumerating tags at all.

1. Problem Definition

The starting point is a tag list that follows a strict numeric pattern. In a TIA Portal project with a S7-1500 PLC connection named S7(1), the HMI tag prefix typically resolves to the program block or the data block reference in the runtime namespace:

HMIRuntime.Tags("Programa$S7(1)/DATOS1.B_E_CAD")
HMIRuntime.Tags("Programa$S7(1)/DATOS2.B_E_CAD")
HMIRuntime.Tags("Programa$S7(1)/DATOS3.B_E_CAD")
...
HMIRuntime.Tags("Programa$S7(1)/DATOS12.B_E_CAD")

Repeating that statement 50 times is fragile: any rename of the connection or block forces a global search-and-replace, and the script is difficult to review. The objective is to:

  1. Loop from 1 to N (where N is configurable, e.g. 12 or 50).
  2. Build the runtime tag name as "Programa$S7(1)/DATOS" & i & ".B_E_CAD".
  3. Read the tag value through the HMIRuntime.Tags object.
  4. Write the value into a specific cell of an Excel worksheet.
  5. Close Excel and release the COM object.

2. Prerequisites

Item Requirement
Engineering framework TIA Portal V16 or higher (tested through V18 / V19 with WinCC Professional V18/V19)
Runtime WinCC Runtime Advanced (PC-based) or WinCC Professional (RT) on Windows 10/11 / Windows Server 2019/2022
Scripting language VBScript 5.8 (Windows Script Host), no external libraries
Target application Microsoft Excel 2016 / 2019 / 2021 / 365 (32-bit or 64-bit, matching Office install of the runtime PC)
PLC connection An active S7-1200/1500 HMI connection in the project (e.g. S7(1)). See SIMATIC WinCC Professional V18 - Communication
Tag namespace Numeric suffix must be contiguous (e.g. 1..12). Non-contiguous names require an array of indices.
Bit-version alignment. A 64-bit TIA Portal/WinCC runtime does not load a 32-bit Excel COM add-in (and vice versa). The "Microsoft Excel * Application" reference in Tools > References of the VB editor must be the same bitness as the installed Office. Use Microsoft 365 Apps for Enterprise on the runtime PC or install the matching Office 32-bit / 64-bit build.

3. WinCC Tag Naming Convention

WinCC Runtime Advanced and WinCC Professional expose two distinct namespaces for the Tags object:

Runtime Object model Reference syntax
WinCC Professional (TIA Portal V18+) HmiRuntime HmiRuntime.Tags("TagName")
WinCC Runtime Advanced (TIA Portal V16-V17) HMIRuntime HMIRuntime.Tags("TagName")
WinCC V7 / WinCC V8 (Classic) HMIRuntime with different hierarchy HMIRuntime.Tags("S7(1)/TagName")

For a TIA Portal project where the S7-1500 program block is named Programa and the connection is S7(1), a tag inside an instance DB shows up as:

Programa$S7(1)/DATOS1.B_E_CAD

The separator characters are fixed:

  • $ separates the program block from the connection.
  • ( ) bracket the connection ID number.
  • / separates the connection from the tag path.
  • . (dot) addresses a UDT/struct member, identical to the S7 dot notation in the PLC.
Spelling check. The correct token in modern TIA Portal V18+ is HmiRuntime (capital H, capital R). The legacy spelling HMIRuntime still works in older runtimes but is documented for V16/V17 projects. Mixing the two in the same script triggers Object required: 'HmiRuntime' at runtime. Refer to the TIA Portal V18 WinCC Professional manual for the canonical object name.

4. Why the Original Loop Did Not Run

The original draft contained a syntax error in the For header and was missing the Set keyword on the tag assignment. The two issues are independent:

For i = 1 To i= 12                       ' INVALID: VBScript uses 'To', not 'i= 12'
HMIRuntime.Tags("Programa$S7(1)/DATOS" & i & ".B_E_CAD")   ' missing Set
objTag.Read
o

VBScript requires the bound of a For loop to be a constant, variable, or numeric expression — never an assignment. The corrected header is:

For i = 1 To 12
    Set objTag = HMIRuntime.Tags("Programa$S7(1)/DATOS" & i & ".B_E_CAD")
    objTag.Read
    objWorkSheet.Cells(1 + i, 2).Value = objTag.Value
Next

Three things changed:

  1. For i = 1 To 12 instead of For i = 1 To i = 12 — the loop end value is the literal 12, not a re-assignment.
  2. Set objTag = HMIRuntime.Tags(...) — the Tags(...) property returns a Tag object; assigning it to a variable requires the Set statement in VBScript.
  3. objTag.Read refreshes the value from the PLC; without it, the cached value from the last cycle is returned.

5. Building the Tag String with a Counter

String concatenation in VBScript uses the ampersand (&). The numeric counter is implicitly converted to a string when concatenated with a string literal. The expression "Programa$S7(1)/DATOS" & i & ".B_E_CAD" builds:

i Resolved runtime tag path
1 Programa$S7(1)/DATOS1.B_E_CAD
2 Programa$S7(1)/DATOS2.B_E_CAD
3 Programa$S7(1)/DATOS3.B_E_CAD
... ...
12 Programa$S7(1)/DATOS12.B_E_CAD

For non-contiguous indices (for example, odd/even sets or a recipe that uses 10, 20, 30...), replace the integer with a constant array and iterate over it:

Dim idx
idx = Array(1, 4, 7, 12, 19, 27)
For Each i In idx
    Set objTag = HMIRuntime.Tags("Programa$S7(1)/DATOS" & i & ".B_E_CAD")
    objTag.Read
    objWorkSheet.Cells(1 + i, 2).Value = objTag.Value
Next

6. Excel Automation Object Model

VBScript launches Excel as an out-of-process COM server through CreateObject("Excel.Application"). The object hierarchy used in this article is:

Object Type / class Role
objExcelApp Excel.Application Top-level Excel process; controls visibility, workbooks, and quit
objWorkBook Excel.Workbook A single .xls[x] file
objWorkSheet Excel.Worksheet One sheet inside a workbook
objWorkSheet.Cells(r, c) Excel.Range A single cell addressed by row/column integers (1-based)

Common pitfalls when scripting Excel from WinCC:

  • File path with spaces. Always double the backslash or use Replace(path, "\\", "\\\\"); the path in Workbooks.Open is a single string.
  • Already-open workbook. If a user has the same file open, Workbooks.Open will attach to that instance. Use ReadOnly or a unique temporary path to avoid conflicts.
  • Process leak. Quit followed by Set objExcelApp = Nothing is mandatory; otherwise the Excel process remains in Task Manager under EXCEL.EXE.

7. Complete Working Script (12-Recipe Export)

Drop this code into a button's Click event in WinCC Professional / Runtime Advanced. Change the constants in the top block to match your connection, tag prefix, and Excel file path.

' --- Configuration --------------------------------------------------
Const TAG_PREFIX    = "Programa$S7(1)/DATOS"
Const TAG_SUFFIX    = ".B_E_CAD"
Const FIRST_INDEX   = 1
Const LAST_INDEX    = 12
Const EXCEL_PATH    = "C:\Recipes\export.xls"
Const SHEET_NAME    = "Recipes"
Const START_ROW     = 2            ' row 1 reserved for header
' -------------------------------------------------------------------

Dim i
Dim objTag
Dim objExcelApp
Dim objWorkBook
Dim objWorkSheet
Dim strHeader
Dim bHeaderWritten

Set objExcelApp = CreateObject("Excel.Application")
objExcelApp.Visible = True
objExcelApp.DisplayAlerts = False

' Open an existing workbook or create a new one
Set objWorkBook = objExcelApp.Workbooks.Open(EXCEL_PATH, , False)  ' ReadWrite
Set objWorkSheet = objWorkBook.Sheets(SHEET_NAME)
If objWorkSheet Is Nothing Then
    Set objWorkSheet = objWorkBook.Sheets.Add
    objWorkSheet.Name = SHEET_NAME
End If

' Write a header once
If objWorkSheet.Cells(1, 2).Value = "" Then
    objWorkSheet.Cells(1, 1).Value = "Recipe #"
    objWorkSheet.Cells(1, 2).Value = "Barcode / CAD value"
    bHeaderWritten = True
End If

For i = FIRST_INDEX To LAST_INDEX
    Set objTag = HmiRuntime.Tags(TAG_PREFIX & i & TAG_SUFFIX)
    If objTag Is Nothing Then
        ' Skip missing tags - avoids 0-value pollution
        HmiRuntime.Trace "Missing tag: " & TAG_PREFIX & i & TAG_SUFFIX & vbCrLf
    Else
        objTag.Read
        objWorkSheet.Cells(START_ROW + i - 1, 1).Value = i
        objWorkSheet.Cells(START_ROW + i - 1, 2).Value = objTag.Value
    End If
Next

objWorkBook.Save
objWorkBook.Close False
objExcelApp.Quit

Set objWorkSheet  = Nothing
Set objWorkBook   = Nothing
Set objExcelApp   = Nothing
Set objTag        = Nothing

The script will:

  1. Open (or create) C:\Recipes\export.xls.
  2. Activate the sheet Recipes or create it if it does not exist.
  3. Write the header line the first time the script runs.
  4. Loop from 1 to 12, reading each tag and writing two columns: index and value.
  5. Save and close the workbook, then release the COM references.

8. Scaling the Loop to 50+ Tags

Replacing LAST_INDEX = 12 with 50 is the only change required to export fifty tags. Two refinements are recommended when the loop count grows:

  1. Drive LAST_INDEX from a tag. A signed integer HMI tag (for example, Config.LastIndex) makes the script reusable without recompilation.
    Const FIRST_INDEX = 1
    Dim nLast
    Set objTag = HmiRuntime.Tags("Config.LastIndex")
    objTag.Read
    nLast = CInt(objTag.Value)
    For i = FIRST_INDEX To nLast
       ...
    Next
  2. Batch the read with a tag array. If all 50 tags share the same S7 connection and area, declare a tag array in TIA Portal (right-click on the HMI tag > Properties > Array). The runtime exposes the whole array with a single Read:
    Set objTag = HmiRuntime.Tags("Programa$S7(1)/DATOS_Array")
    objTag.Read
    For i = 0 To objTag.Count - 1
        objWorkSheet.Cells(START_ROW + i, 2).Value = objTag.Value(i)
    Next
    A single read of a structured array is typically 10-50x faster than 50 individual round-trips because the S7 communication driver batches the request.
Performance caveat. Calling objTag.Read inside a tight loop blocks the HMI main thread. With 50 tags on a 100 ms tag cycle this adds 5 s of UI latency. Use Change-made triggered reads (event-driven) or the array method above to keep the HMI responsive. See SIMATIC WinCC Performance Tuning for guidance on tag acquisition rate and screen refresh.

9. Exporting the Online Table Control to CSV without Enumerating Tags

When the data already lives inside an Online Table Control (the WinCC control that records a tag history and renders it as a table), there is a one-liner that exports the visible buffer as a CSV without iterating tag names. The runtime object is ScreenItems("ControlName"), exposed through the WinCC Controls documentation:

Dim objTable
Set objTable = HmiRuntime.Screens("Recipe_Overview").ScreenItems("TableControl1")
objTable.SaveData "C:\Recipes\table_export.csv", 1   ' 1 = CSV

The SaveData method is part of the Online Table Control interface and supports the following flags:

Constant Value Effect
vbCSV 1 Comma-separated values, one header row + one row per record
vbText 2 Tab-separated, useful for paste into Excel via clipboard
vbUnicode 64 UTF-16 LE output; required for non-ASCII characters in recipes

To export several online table controls at once (e.g. one per process cell), use a For Each loop over a collection or an array of control names:

Dim sName, sScreen
Dim aControls
sScreen = "Recipe_Overview"
aControls = Array("TableControl1", "TableControl2", "TableControl3")
For Each sName In aControls
    HmiRuntime.Screens(sScreen).ScreenItems(sName).SaveData _
        "C:\Recipes\" & sName & ".csv", 1 + 64   ' CSV + Unicode
Next

This approach exports the runtime history of each table control — which is what end users want when they press the "Export" button on a process screen. It does not require enumerating the source tags at all.

10. Reading, Quality Code, and Caching

The Tag object exposes a QualityCode property and a Quality string that report the validity of the last read. Always check it before writing a value to Excel, otherwise a stale or bad value (PLC disconnected, area pointer overflow) silently contaminates the report:

objTag.Read
Select Case objTag.QualityCode
    Case 0          ' Good
        objWorkSheet.Cells(START_ROW + i - 1, 2).Value = objTag.Value
    Case &H0010    ' Uncertain
        objWorkSheet.Cells(START_ROW + i - 1, 2).Value = objTag.Value
        objWorkSheet.Cells(START_ROW + i - 1, 3).Value = "UNCERTAIN"
    Case Else       ' Bad / Not connected
        objWorkSheet.Cells(START_ROW + i - 1, 2).Value = ""
        objWorkSheet.Cells(START_ROW + i - 1, 3).Value = "BAD (0x" & Hex(objTag.QualityCode) & ")"
End Select

Quality code values are defined by the OPC UA specification. The most common codes observed in a WinCC runtime are:

Code (hex) Status Meaning in a WinCC/S7 environment
0x00000000 Good Value valid, fresh, no constraint violation
0x40000000 Uncertain Value is plausible but the source is suspect
0x803D0000 Bad - Out of Service Tag disabled in TIA Portal configuration
0x803F0000 Bad - Communication Error Connection to the S7 PLC is down
0x80400000 Bad - Waiting for Initial Data Runtime started; no first read yet

Reference the OPC UA Part 4 - Service Set / StatusCodes for the complete list.

11. Error Handling in VBScript

VBScript does not have structured try/catch blocks. The standard pattern is On Error Resume Next followed by explicit error inspection with Err.Number / Err.Description:

On Error Resume Next

Set objExcelApp = CreateObject("Excel.Application")
If Err.Number <> 0 Then
    HmiRuntime.Trace "Excel not available: 0x" & Hex(Err.Number) _
        & " - " & Err.Description & vbCrLf
    Err.Clear
    Exit Sub
End If

On Error Resume Next
Set objWorkBook = objExcelApp.Workbooks.Open(EXCEL_PATH)
If Err.Number <> 0 Then
    HmiRuntime.Trace "Cannot open " & EXCEL_PATH _
        & " - " & Err.Description & vbCrLf
    objExcelApp.Quit
    Exit Sub
End If
On Error Goto 0
Always re-enable strict error handling with On Error Goto 0 before entering the main loop. Leaving Resume Next in effect masks PLC tag errors that should be visible during commissioning.

12. Verification Checklist

Run the script in the runtime and confirm the following before sign-off:

  1. Open the target Excel file and confirm the header row is written only once (not overwritten on every click).
  2. Confirm rows 2..13 contain the values of DATOS1.B_E_CAD through DATOS12.B_E_CAD in column B. Change one of the PLC values and re-click the button; the script should overwrite the cell with the new value.
  3. Stop the S7-1500 connection in TIA Portal's "Online > Accessible nodes" and re-run. The Quality column should read BAD (0x803F0000), and Excel should be empty for that row.
  4. Open Task Manager. After script completion, no EXCEL.EXE instance should remain (orphan process check).
  5. Force a typographic error in the tag name (e.g. DATOS1B_E_CAD missing the dot). The script should produce a trace entry, not a hard crash.

13. Frequently Asked Questions

What is the correct VBScript syntax for a numeric For...Next loop in WinCC?

Use For i = 1 To 12 with the constant 12 as the upper bound; do not write For i = 1 To i = 12. The To keyword requires a constant or expression on its right side, never an assignment. Reference the Microsoft VBScript Language Reference for the full grammar.

Can I read an entire S7 data block with one call instead of looping 50 times?

Yes. Declare a PLC tag of type array of with the right number of elements in the DB and read it once through HmiRuntime.Tags("Programa$S7(1)/DATOS_Array"). The runtime returns a Value array that you can index directly: objTag.Value(i). This is the recommended pattern for 30+ tags because it cuts S7 round-trips from 30 to 1.

Why does the script fail with "Object required: 'HmiRuntime'"?

Either the object name is misspelled (use HmiRuntime in TIA Portal V18+, HMIRuntime in older runtimes), or the script is running in a context where the runtime is not initialised (e.g. a global function called from the scheduler before the first screen loads). Move the script to a button Click event in a loaded screen, or use the HMIRuntime.Trace method to confirm the runtime is alive.

How do I export several Online Table Controls to separate CSV files?

Use the SaveData method on the control object: HmiRuntime.Screens("MyScreen").ScreenItems("TableControl1").SaveData "C:\export\a.csv", 1. Loop over an array of control names to export them all in one script. The flag 1 requests CSV; add 64 to request Unicode encoding for non-ASCII recipes.

What is the difference between HMIRuntime and HmiRuntime?

HMIRuntime is the legacy object name used in WinCC Runtime Advanced / WinCC Flexible / TIA Portal V15-V17. TIA Portal V18 standardised on HmiRuntime (capital H, capital R). Both still compile in the current WinCC Professional V18/V19 editor for backward compatibility, but new projects should use the V18 spelling. The complete API surface is documented in the TIA Portal V18 WinCC Professional manual.

Back to blog