WinCC TIA Portal: Run VBScript Every 2 Seconds on TP900

David Krause14 min read
HMI / SCADASiemensTutorial / 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 TIA Portal: Run VBScript Every 2 Seconds on TP900

This reference documents how to implement a periodic VBScript execution in WinCC (TIA Portal) Comfort/Advanced running on a SIMATIC TP900 Comfort panel, with the script driven by a cyclic tag value change originating from an S7-1500 PLC (tested on CPU 1512). The technique replaces the legacy WinCC flexible scheduler, gives sub-second resolution, and supports clean start/stop semantics through a single boolean tag.

1. Problem Definition and Design Goals

WinCC Comfort/Advanced does not expose a VBScript "tick" or "timer" object on the HMI runtime in the same way a desktop application does. The available triggers for a scheduled function are limited to:

  • Tag value change (rising edge, falling edge, any change, or value range)
  • Scheduled task (date/time, daily, weekly - resolution 1 minute minimum on TP900)
  • Screen event (load, clear)

To obtain a 2-second (or any value between 0.1 s and several hours) execution interval, the most reliable pattern is to use a cyclic PLC tag as the heartbeat and bind the VBScript to its value change event. The script body is wrapped with an If ... Then guard that reads a Start/Stop tag, so the same heartbeat tag can be enabled or disabled at runtime without reconfiguration.

Why not the Scheduler? The TP900 internal Scheduler has a 1-minute minimum granularity and is not suitable for second-resolution tasks. The TIA Portal documentation for the SIMATIC Comfort Panels (see WinCC Comfort/Advanced VBScript manual entry ID 109746537) recommends the tag-change approach for sub-minute timing.

2. Architecture Overview

S7-1500 / CPU 1512 OB1 cyclic IEC Timer (TP/TOF) Pulse Generator (BOOL) Start/Stop Tag (BOOL) TP900 HMI Tag (acq. cyclic 100 ms) Event: Value Change -> VBS VBScript (data logging) \Logs\ *.csv

The PLC generates a square-wave Boolean at the required period. The HMI polls that tag at a 100 ms acquisition cycle (minimum on TP900) and detects a value change on every transition. The transition fires the VBScript, which then performs the user action (read tags, log to file, write back to PLC, animate screen, etc.).

3. Prerequisites

Item Specification Notes
TIA Portal V16 or later (V17 / V18 recommended) Same major version as the panel image
WinCC Comfort/Advanced V16+ Comfort = TP panels; Advanced = WinCC Runtime Advanced
SIMATIC TP900 Comfort Image V16.0.4 or later Earlier images limit the scheduler to 1 min
S7-1500 CPU 1512C-1 PN (or any S7-1500) Firmware V2.9+ recommended for tag prefix behavior
PLC-HMI connection S7-CONNECT, PROFINET, S7 Routing Tags must be accessible from HMI DB
VBScript runtime Built into WinCC RT No additional license
Acquisition cycle floor: WinCC Comfort/Advanced enforces a minimum acquisition cycle of 100 ms for external tags. A 2-second script interval therefore must be derived from a PLC generator (or a counter-based virtual tag) - you cannot set a 2 s acquisition cycle to fire the event directly without overshoot.

4. Step 1 - Build the PLC Heartbeat Generator

Create a data block in the S7-1500 project, for example DB_HMI_Trigger, containing three tags:

Symbol Type Initial value Comment
bStartStop BOOL FALSE HMI button drives this; the script only runs when TRUE
bHeartbeat2s BOOL FALSE Square-wave, period 2 s; the value-change trigger
iTickCount INT 0 Increments each tick; useful for diagnostics

Add the following structured text logic in OB1 (or an OB with the same cycle, e.g. OB35 at 100 ms):

// Heartbeat: toggle every 2 s using a TON + edge
// Tag: iHeartTmr (TIME), iHeartStep (INT) - local statics
IF iHeartStep = 0 THEN
    iHeartTmr := T#2S;          // period for 2-second script interval
    iHeartStep := 1;
END_IF;

// Use a TP (pulse) or simple timer-driven toggle
IF "DB_HMI_Trigger".bStartStop THEN
    IF iHeartTmr <= T#0MS THEN
        // toggle
        "DB_HMI_Trigger".bHeartbeat2s := NOT "DB_HMI_Trigger".bHeartbeat2s;
        "DB_HMI_Trigger".iTickCount := "DB_HMI_Trigger".iTickCount + 1;
        iHeartTmr := T#2S;
    END_IF;
    iHeartTmr := iHeartTmr - OB1_INTERVAL;  // subtract 100 ms per OB1 cycle
ELSE
    "DB_HMI_Trigger".bHeartbeat2s := FALSE;
    "DB_HMI_Trigger".iTickCount := 0;
END_IF;

Alternatively, use the built-in Clock Generator from the "Instructions > Timers & counters > Clock" group in TIA Portal with mode Periodic and period 2 s. The clock generator is the cleanest solution - it does not consume OB1 scan time and is fully time-accurate.

OB1_INTERVAL: S7-1500 OB1 always runs at the configured scan time. For deterministic 2 s timing, configure OB35 at 100 ms and place the logic there. OB1_INTERVAL is exposed as a system constant in newer firmware only; otherwise use a local TIME literal of T#100MS.

5. Step 2 - Create the HMI Tags

In the HMI project, create three tags pointing to the corresponding PLC addresses. Mark the connection's "Acquisition mode" as Cyclic continuous (this is the field many engineers miss - default is "On demand" and the value-change event will not fire on every transition).

HMI tag name PLC address Type Acquisition cycle Acquisition mode
tagStartStop DB_HMI_Trigger.bStartStop BOOL 100 ms Cyclic continuous
tagHeartbeat2s DB_HMI_Trigger.bHeartbeat2s BOOL 100 ms Cyclic continuous
tagTickCount DB_HMI_Trigger.iTickCount INT 500 ms Cyclic continuous
Cyclic continuous vs. On demand: With On demand, the HMI updates the tag value only when another tag is read or a function is explicitly requested, and value-change events are silently dropped. With Cyclic continuous, the HMI polls at the configured cycle and the runtime guarantees edge detection. See Siemens KB 109755224 - HMI Tag Acquisition Behavior.

6. Step 3 - Bind the VBScript to the Value-Change Event

  1. In the TIA Portal project tree, open HMIRuntime > Scripts > VBScripts.
  2. Right-click and add a new procedure, e.g. sub_OnTick.
  3. Open the HMI screen where the trigger should be active, or add the function to a global script if it should run regardless of the active screen.
  4. Select the tagHeartbeat2s tag in the screen or in the HMI tags editor.
  5. In the Properties pane, navigate to Events > Value change.
  6. Add a new function call: sub_OnTick (or call the function with parameter tagHeartbeat2s).

7. Step 4 - Write the VBScript Body

The pattern from the original field-tested discussion (the -1 = true convention is specific to HMI tag semantics where the runtime uses -1 for a true Boolean) is preserved and extended with logging and screen I/O:

'--------------------------------------------------------------------
' sub_OnTick  -  triggered by tagHeartbeat2s on every transition
'               guarded by tagStartStop
'--------------------------------------------------------------------
Option Explicit

Dim bRun, bTick, iCount, sPath, sLine, oFSO, oFile
Dim dNow

bRun  = SmartTags("tagStartStop")      ' -1 = true in WinCC convention
bTick = SmartTags("tagHeartbeat2s")
iCount = SmartTags("tagTickCount")

If bRun = -1 Then
    ' --- user code begin ---
    dNow = Now                          ' local WinCC clock
    sLine = FormatDateTime(dNow, vbShortDate) & "," & _
            FormatDateTime(dNow, vbLongTime) & "," & _
            CStr(iCount) & "," & _
            CStr(SmartTags("tagProcessValue1")) & "," & _
            CStr(SmartTags("tagProcessValue2"))

    ' --- log to file on the panel's local storage ---
    sPath = "\Storage Card SD\Logs\telemetry.csv"
    Set oFSO = CreateObject("Scripting.FileSystemObject")
    If Not oFSO.FolderExists("\Storage Card SD\Logs\") Then
        oFSO.CreateFolder "\Storage Card SD\Logs\"
    End If
    If Not oFSO.FileExists(sPath) Then
        Set oFile = oFSO.CreateTextFile(sPath, True)
        oFile.WriteLine "Date,Time,Tick,PV1,PV2"
        oFile.Close
    End If
    Set oFile = oFSO.OpenTextFile(sPath, 8, False)   ' 8 = ForAppending
    oFile.WriteLine sLine
    oFile.Close
    Set oFile = Nothing
    Set oFSO  = Nothing

    ' --- visual feedback on screen ---
    SmartTags("tagLastTick") = dNow
End If

WinCC VBS Boolean convention: The runtime stores BOOL as a 16-bit integer internally. A Boolean "true" is represented as -1 (all bits set), not 1. Comparing against -1 is the only safe form of identity comparison; If SmartTags("x") Then also works because VBScript coerces any non-zero to true, but the explicit form is preferred when the bit pattern matters (e.g. latching a flag back to FALSE inside the script).

8. Step 5 - Wire the Start/Stop Button

Place a button on any HMI screen. Configure the Press event with the InvertBit system function on tagStartStop. The Release event should do nothing - toggling on press gives clean debounced semantics for an operator pushbutton.

Property Value
Appearance > Mode "Switch" (latching) or "Button" (momentary with toggle)
Events > Press System function: InvertBit - tagStartStop
Events > Release (none)
Appearance > Label "ON"/"OFF" Dynamic via text list bound to tagStartStop

If the button is wired to a physical I/O on the PLC (rather than a screen button), bind tagStartStop to the input in the HMI tag properties and set its acquisition mode to Cyclic continuous; the script guard handles the rest.

9. Step 6 - Optional: Display Date/Time with Each Sample

The script's Now function uses the panel's local clock, which is synchronized from the PLC by default. For plants where the panel is not synchronized, replace Now with reading a DTL tag from the PLC, formatted using FormatDateTime on the variant. Example PLC side:

// DB_HMI_Trigger.dtlNow : DTL  (read by HMI as DTL, 8 bytes)
"DB_HMI_Trigger".dtlNow := DTL#2024-01-01-00:00:00;  // populated from PLC clock

On the HMI, display via an Output field with format pattern yyyy-MM-dd HH:mm:ss. The script can also read this DTL and append to the log line for traceability.

10. Verification Procedure

  1. Compile the HMI project and download to the TP900 (use Software (full) on first load, Delta thereafter).
  2. Connect with WinCC Tag Simulation or a PLC online watch table to confirm bHeartbeat2s toggles every 2 s.
  3. Set bStartStop := TRUE in the PLC; the HMI should write one line to \Storage Card SD\Logs\telemetry.csv per transition (so two lines per second, but the semantic "tick" is the 2 s boundary - script execution happens once per transition).
  4. Set bStartStop := FALSE; the file should stop growing. Set it back to TRUE; logging resumes on the next 2 s transition.
  5. Power-cycle the panel; confirm that on restart the script does not run because tagStartStop defaults to FALSE.
Power-on behavior: Because the script is event-driven, it does not execute during startup or on every screen change. It only runs when the heartbeat transitions AND the start tag is true. This is the desired behavior for periodic logging - there is no need to manually disable the script on shutdown.

11. Performance and Timing Budget

Element Typical time Constraint
HMI acquisition cycle 100 ms Hard floor; can be raised to 200/500/1000 ms if the network is loaded
Value-change detection latency 0 to 1 acquisition cycle 0 to 100 ms; means a 2 s tick has up to 100 ms of jitter
VBScript execution 5 to 50 ms Depends on body; >100 ms starts to starve other runtime tasks
File write to SD card 20 to 200 ms SD card class matters; class 10 recommended for 1 Hz logging
PLC scan to HMI tag update 1 to 5 OB1 cycles (10 to 50 ms typical) Profinet update time adds another 1 to 4 ms

For sub-100 ms requirements, the value-change approach breaks down. Use a C/C++ script in WinCC Professional, or move the periodic task to a PLC OB (e.g. OB35 + a small DB write) and treat the HMI as a pure display.

12. Troubleshooting Matrix

Symptom Root cause Fix
Script never fires Tag acquisition set to On demand Set Acquisition mode to Cyclic continuous
Script fires once, then never again PLC heartbeat is not toggling; OB1 paused or CPU in STOP Watch the tag in HMI online; verify PLC is in RUN and OB1 is executing
Script fires at random intervals (1 s, 3 s, 5 s) Heartbeat driven by OB1 with variable scan time; use a TON with a fixed PT Replace with a TP/clock generator, or use OB35 at 100 ms
Script runs even though Start button is OFF Comparison uses = 1 instead of = -1, or the If guard is missing Use If SmartTags("tagStartStop") = -1 Then
Log file is empty after restart SD card path is wrong, or the card is not inserted at startup Verify \Storage Card SD\ exists; insert card before boot
Log file is full of duplicate timestamps Edge detection is firing on every acquisition (rising AND falling) Trigger on Rising edge only, not Any change
Compiles but VBScript error in Runtime Typo in SmartTags name; tag renamed but script not updated Re-bind tags; TIA Portal does not refactor SmartTags calls
Compiles but error in Runtime: "Permission denied" on file File already opened by another application (FTP, viewer) Close the file before transferring via FTP, or use a different file name each shift
Rising vs. Any change: Binding the event to Value change (any) causes the script to fire on both edges of the square wave - effectively halving the period. To execute the script exactly once per 2 s interval, configure the event to trigger on the Rising edge of the heartbeat, or use a counter tag and trigger on a specific value range. This is the single most common cause of "double-speed" logging in field installations.

13. Edge Cases and Field-Proven Caveats

  • Multiple scripts per tick: A single value-change event can only call one function. If two scripts must run at 2 s, drive them from a single function that calls both, or use two separate boolean heartbeat tags with different phases.
  • Runtime Advanced vs. Comfort: WinCC Runtime Advanced on a PC supports the same VBScript syntax but adds the HMIRuntime object for system-level queries. The TP900 restricts filesystem access to \Storage Card SD\ and a few read-only paths.
  • Time-zone correctness: Now on the panel returns local time. For UTC logging, use SmartTags("tagDTL_UTC") populated from RD_SYS_T on the PLC and converted with the appropriate offset.
  • Log rotation: SD cards fill up. Implement a daily rename in the script using FormatDateTime(Now, vbShortDate) as part of the file name, or use the built-in WinCC logging (with a tag logging group at 2 s) which handles rotation automatically.
  • Commissioning without PLC: During bench test, replace the PLC heartbeat with a WinCC internal Simulation tag that toggles via a second VBScript bound to a 100 ms scheduler. This pattern is documented in the WinCC Comfort manual chapter "Tag simulation".
  • Security: VBScript has no sandbox; an oFSO.DeleteFile call in the same script can wipe the SD card. Wrap the body with try/catch and prefer Tag logging for production audit trails.

14. Alternative: WinCC Tag Logging (no VBScript required)

If the periodic action is only "record values over time", the tag logging subsystem in WinCC Comfort/Advanced is the intended path and avoids VBScript entirely. Configure a logging group with:

Setting Recommended value
Acquisition cycle 2 s
Logging mode Cyclic with continuous logging
Storage location \Storage Card SD\Logs\
File format CSV (RDB for SQL retrieval)
Logging tags tagProcessValue1, tagProcessValue2, tagTickCount

Use VBScript only when you need to do something the logger cannot (compute a moving average, send an email at threshold, animate a trend, etc.). For pure periodic recording, the logging system is faster, more reliable, and writes to RDB format for the Information Server.

15. Related Patterns

  • Edge-triggered logging on operator action: Use the Click event of a button to call the script directly, without a heartbeat tag. Same start/stop semantics by reading a global "logging enabled" tag inside the script.
  • Time-of-day trigger: Combine a clock generator (e.g. pulse at 06:00:00) with the value-change event to run a script at a fixed wall-clock time without polling.
  • Multi-rate sampling: Use three heartbeat tags at 0.5 s, 2 s, 10 s and bind three different scripts. The PLC generates them all from a single OB35 tick counter.
  • Remote trigger from another HMI: Wire a tag from HMI_1 to HMI_2 via the PLC and let the value-change event on HMI_2 react. Round-trip latency on Profinet is <10 ms, so the script is essentially synchronous.

16. Standards and Documentation Cross-References

How do I run a WinCC VBScript exactly every 2 seconds?

Use a PLC boolean heartbeat tag that toggles every 2 s (e.g. via a TP timer or clock generator), set the HMI tag's acquisition mode to Cyclic continuous with a 100 ms cycle, and bind the VBScript to the tag's Rising edge value-change event. Guard the body with an If SmartTags("tagStartStop") = -1 Then for start/stop control.

Why is the value-change event not firing on my TP900?

The most common cause is that the HMI tag's acquisition mode is set to On demand instead of Cyclic continuous. Switch it in the tag properties under "Acquisition" and redownload. The HMI will then poll the tag at the configured cycle and detect transitions.

Why is the script running twice as fast as expected?

The event is bound to Value change (any), which fires on both rising and falling edges of the heartbeat. With a 2 s square wave, this gives one execution per second. Change the event to Rising edge (or Value change with edge direction "Rising") to get one execution per 2 s.

Can I trigger a VBScript without writing to the PLC?

Yes, for test purposes. Use a WinCC internal tag with Simulation enabled and drive it from a second VBScript bound to the panel's internal 100 ms scheduler. In production, the PLC-driven heartbeat is preferred because the PLC clock is more accurate and survives panel restarts.

What is the difference between = -1 and = True for a WinCC BOOL?

WinCC stores BOOL as a 16-bit integer, with "true" represented as -1 (all bits set). Direct identity comparison against True can fail for tags that PLC code writes via bit-set operations. The convention If SmartTags("x") = -1 Then is the safe form and is the pattern used in the original WinCC field-tested code.

Is the 1-minute Scheduler the only way to run a script periodically?

On Comfort Panels, yes - the built-in Scheduler has a 1-minute minimum granularity. For higher resolution (sub-minute, including 2 s), the documented Siemens pattern is the PLC-driven heartbeat tag with a value-change event, as described in this article.

Back to blog