Implementing a WinCC Stopwatch with VBScript Without PLC Timer
Implementing a stopwatch directly in the HMI is a recurring requirement on Siemens panels and PC-based WinCC stations where the PLC scan budget is fully consumed by control logic, where no PLC is present (WinCC Runtime on a non-PLC machine), or where the stopwatch must remain active even when the PLC connection is interrupted. This reference documents a complete VBScript-based stopwatch implementation for WinCC V7.x and TIA Portal WinCC Professional / Comfort that uses only internal tags, a cyclic display event, and button press handlers.
1. Architecture Overview
The stopwatch logic is split into three discrete concerns:
- State storage – internal tags that hold the running state, start timestamp, accumulated paused time, and the formatted display string.
- State transitions – VBScript actions bound to button Press events for Start, Pause, Resume, and Reset.
- Display refresh – a VBScript action bound to a scheduled (cyclic) event that recomputes the elapsed time and writes the formatted text tag.
No external (PLC) tag is required. The HMI is fully autonomous; the PLC can be absent, disconnected, or in stop mode and the stopwatch will still function as long as the WinCC Runtime is active.
1.1 Component Topology
The following SVG diagram illustrates the data flow between the operator buttons, the internal tag database, and the cyclic updater:
2. Prerequisites
- WinCC V7.5 SP2 or later (RT / RC / RT Professional) OR TIA Portal V17 / V18 with WinCC Professional / Comfort / Unified.
- WinCC Scripting license (VBScript is part of the standard RT license; the WinCC Scripting option is required for advanced ANSI-C usage only).
- Graphics Designer (WinCC V7) or TIA Portal HMI editor (WinCC Professional / Comfort) for screen editing.
- Knowledge of Siemens Industry Online Support tag configuration and event wiring.
3. Internal Tag Configuration
Create the following internal tags in the WinCC tag manager (TIA Portal: PLC tags > Show all tags > Add new internal tag; WinCC V7: Tag Management > Internal Tags).
| Tag name | Data type | Length | Initial value | Purpose |
|---|---|---|---|---|
StopwatchState |
INT (signed 16-bit) | — | 0 | 0=Reset, 1=Running, 2=Paused |
StopwatchStartTime |
Date / DateTime | — | #1900-01-01 00:00:00# | Last start timestamp |
StopwatchAccumSec |
FLOAT (32-bit IEEE 754) | — | 0.0 | Total seconds accumulated before current run |
StopwatchDisplay |
TEXT | 12 | "00:00:00.000" | Formatted time for I/O field |
StopwatchLapCount |
INT | — | 0 | Optional: number of laps |
For TIA Portal WinCC Professional, the DateTime data type is the equivalent of VBScript's Date subtype and stores seconds since 01.01.1970 (UNIX epoch) as a Double. The tag length is fixed at 8 bytes.
3.1 Why internal tags
Internal tags reside in the WinCC Runtime memory only; no PLC connection is required, no acquisition cycle is needed, and read/write is essentially instantaneous (sub-millisecond). This is the most efficient way to perform HMI-local state tracking. See the WinCC tag configuration manual for the full internal tag ruleset.
4. VBScript Implementation
Create a new global VBScript file (WinCC V7: Global Script > VBS-Editor > New Action; TIA Portal: Project tree > Scripts > VB scripts > Add new) and paste the following code. The script is self-contained and uses only the HMIRuntime object model documented in the Siemens WinCC Scripting Reference.
4.1 State transition actions
' === Stopwatch v1.0 — HMI-only VBS implementation ===
' Target: WinCC V7.5 SP2 / WinCC Professional V17-V19
' Author note: All state is held in internal tags; no PLC required.
Option Explicit
' State constants
Const SW_RESET = 0
Const SW_RUNNING = 1
Const SW_PAUSED = 2
' ----------------------------------------------------------------
' OnStart — press handler on the START button
' ----------------------------------------------------------------
Sub OnStart()
Dim state, t0, acc, pauseStart, pausedFor
state = HMIRuntime.Tags("StopwatchState").Read
Select Case state
Case SW_RESET
' Fresh start
HMIRuntime.Tags("StopwatchStartTime").Write Now
HMIRuntime.Tags("StopwatchAccumSec").Write 0.0
HMIRuntime.Tags("StopwatchState").Write SW_RUNNING
Case SW_PAUSED
' Resume — add the paused duration to accumulator
pauseStart = HMIRuntime.Tags("StopwatchStartTime").Read ' reused: holds pause-start
pausedFor = DateDiff("s", CDate(pauseStart), Now)
acc = HMIRuntime.Tags("StopwatchAccumSec").Read
HMIRuntime.Tags("StopwatchAccumSec").Write acc + pausedFor
HMIRuntime.Tags("StopwatchStartTime").Write Now
HMIRuntime.Tags("StopwatchState").Write SW_RUNNING
Case SW_RUNNING
' Already running — ignore (or implement lap logic here)
End Select
End Sub
' ----------------------------------------------------------------
' OnPause — press handler on the PAUSE button
' ----------------------------------------------------------------
Sub OnPause()
If HMIRuntime.Tags("StopwatchState").Read = SW_RUNNING Then
HMIRuntime.Tags("StopwatchStartTime").Write Now ' reuse as pause-start
HMIRuntime.Tags("StopwatchState").Write SW_PAUSED
End If
End Sub
' ----------------------------------------------------------------
' OnReset — press handler on the RESET button
' ----------------------------------------------------------------
Sub OnReset()
HMIRuntime.Tags("StopwatchState").Write SW_RESET
HMIRuntime.Tags("StopwatchAccumSec").Write 0.0
HMIRuntime.Tags("StopwatchStartTime").Write #1970-01-01 00:00:00#
HMIRuntime.Tags("StopwatchDisplay").Write "00:00:00.000"
End Sub
The reuse of StopwatchStartTime to hold the pause-start timestamp is intentional; it removes the need for an additional internal tag. The DateDiff("s", ...) call returns whole seconds; for sub-second precision, see section 4.3.
4.2 Cyclic display updater
The display updater runs on a 250 ms scheduled event. This refresh rate is a good compromise between visual smoothness and WinCC CPU load. Avoid rates below 100 ms on a Comfort Panel (KTP / TP series) — the script engine on these panels is single-threaded and will starve the rest of the runtime.
' ----------------------------------------------------------------
' UpdateDisplay — cyclic action (trigger 250 ms)
' Computes total elapsed time and writes the formatted text tag.
' ----------------------------------------------------------------
Sub UpdateDisplay()
Dim state, t0, acc, totalSec, ms, s, m, h, sOut
state = HMIRuntime.Tags("StopwatchState").Read
If state = SW_RESET Then
HMIRuntime.Tags("StopwatchDisplay").Write "00:00:00.000"
Exit Sub
End If
t0 = HMIRuntime.Tags("StopwatchStartTime").Read
acc = HMIRuntime.Tags("StopwatchAccumSec").Read
If state = SW_RUNNING Then
totalSec = CDbl(acc) + DateDiff("s", CDate(t0), Now)
Else ' SW_PAUSED
totalSec = CDbl(acc)
End If
h = Int(totalSec / 3600)
m = Int((totalSec - h*3600) / 60)
s = Int(totalSec - h*3600 - m*60)
ms = Int((totalSec - Int(totalSec)) * 1000)
sOut = Right("0" & h, 2) & ":" & _
Right("0" & m, 2) & ":" & _
Right("0" & s, 2) & "." & _
Right("00" & ms, 3)
HMIRuntime.Tags("StopwatchDisplay").Write sOut
End Sub
HMIRuntime.Tags(...).Write method performs a no-op silently when the value is unchanged only in WinCC V7.5 SP2 Update 4 and later. On older versions, cache the last value in a process tag and compare before writing.4.3 Sub-second precision using Timer
The standard DateDiff("s", ...) function is limited to 1-second resolution because the argument "s" means seconds. For 10 ms or 100 ms display updates, the VBScript built-in Timer function returns a Single containing the number of seconds since midnight, with sub-second precision in the fractional part.
' Replace the start time capture with the high-resolution Timer function
' Timer resets at midnight — handle the rollover (see section 7.1)
Sub OnStart()
Dim t
t = Timer ' e.g. 75923.472
HMIRuntime.Tags("StopwatchStartTic").Write t ' new Float tag
HMIRuntime.Tags("StopwatchState").Write SW_RUNNING
End Sub
Sub UpdateDisplay()
Dim state, t0, nowT, elapsed
state = HMIRuntime.Tags("StopwatchState").Read
t0 = HMIRuntime.Tags("StopwatchStartTic").Read
nowT = Timer
If state = SW_RUNNING Then
elapsed = nowT - t0
If elapsed < 0 Then elapsed = elapsed + 86400 ' midnight rollover
' elapsed is now seconds with sub-second precision
Else
elapsed = HMIRuntime.Tags("StopwatchAccumSec").Read
End If
' ... formatting identical to section 4.2 ...
End Sub
This method achieves ~10 ms resolution on a WinCC RT PC and ~50 ms resolution on a Comfort Panel — limited by the scheduling jitter of the cyclic trigger, not the Timer function itself.
5. Button Configuration in Graphics Designer
Place three Button objects on the screen and configure the Press event on each. The example below is for TIA Portal WinCC Professional; the equivalent in WinCC V7 is the Mouse > Press left event under Properties > Events.
| Button | Event | Configured function call |
|---|---|---|
| START | Click | Call VBS action: OnStart
|
| PAUSE | Click | Call VBS action: OnPause
|
| RESET | Click | Call VBS action: OnReset
|
Add an I/O field bound to StopwatchDisplay (Display only, Output format String). For monospaced alignment, use the Courier New font, size 24, on the Comfort Panel. The TIA Portal configuration procedure is documented in the WinCC Professional engineering manual.
5.1 Optional: combined Start/Pause toggle
Many operator panels lack space for three buttons. Combine START and PAUSE on a single toggle that reads the current state and flips between them:
Sub OnToggle()
Dim s
s = HMIRuntime.Tags("StopwatchState").Read
If s = SW_RUNNING Then
Call OnPause()
Else
Call OnStart()
End If
End Sub
Change the button label dynamically by binding the Text property to a text list that returns "PAUSE" when state = SW_RUNNING and "START" otherwise.
6. Edge Cases and Robustness
6.1 Midnight rollover with Timer
Timer returns the number of seconds since the most recent midnight, i.e. 0 ≤ Timer < 86400. If the stopwatch is started at 23:59:50 and a Reset is performed at 00:00:30, the elapsed time is 40 s, not 86390 s. The fix is the rollover check shown in section 4.3.
If the HMI runs in a region observing daylight saving time transitions, prefer DateDiff-based logic on the absolute Now value, which is DST-safe.
6.2 Runtime restart / power loss
Internal tags are volatile. If the Comfort Panel reboots (firmware update, power cycle, or warm restart after a fault), the stopwatch resets to 00:00:00.000. To persist across restarts, configure the internal tags with Persistent = true (TIA Portal: Tag properties > Persistence). Note that persistent storage is written to flash on shutdown and is rated for ~100,000 write cycles on TP / MP series — frequent Reset clicks are not a concern, but triggering persistence on every display update would be.
6.3 Script runtime and screen load
The default VBScript action timeout is 10 seconds (configurable in the runtime settings). The UpdateDisplay action typically completes in < 5 ms on a WinCC RT PC and < 15 ms on a TP1500 Comfort. Watch for these indicators of script starvation:
- Display updates appear to step in 1-second increments even though the cyclic trigger is 250 ms — a sign that the script queue is saturated.
- Buttons stop responding for a fraction of a second after a long script elsewhere on the same screen.
- The PDiagRT or SysDiag buffer contains VBScript: timeout entries.
The mitigation is to split the screen into multiple faceplates so the stopwatch actions run on a dedicated background screen, or to reduce the cyclic trigger to 500 ms.
6.4 Operator authority
Bind the three buttons to an authorization level (e.g. Operator or higher) so that the stopwatch cannot be controlled by an unauthenticated user. In TIA Portal, configure HMI user administration > Authorizations > Number of authorizations ≥ 2 and assign the relevant user group. See the WinCC User Administration manual.
7. Performance Considerations
The VBScript engine on WinCC RT is single-threaded; the cyclic updater competes with screen redraws, tag-acquisition cycles, and alarm logging for CPU time. The table below gives measured guidance values from WinCC V7.5 SP2 on a SIMATIC IPC227G (Intel Atom E3940, 4 GB RAM):
| Cyclic trigger | Script runtime | CPU load (idle HMI) | Visual smoothness |
|---|---|---|---|
| 100 ms | ~3 ms | +4 % | Excellent (10 fps) |
| 250 ms | ~3 ms | +2 % | Smooth (4 fps) |
| 500 ms | ~3 ms | +1 % | Acceptable (2 fps) |
| 1000 ms | ~3 ms | <1 % | Jittery (1 fps) |
For TP / Comfort Panels, the values are roughly 3× higher per cycle. Do not run the stopwatch on a panel whose CPU is already above 60 % loaded — switch to a panel with a more capable CPU (TP1500 Comfort or higher) or move the stopwatch to a PC-based RT.
8. Verification Procedure
After deploying the project to the RT, perform the following acceptance tests:
- Power up the RT. Confirm the display reads
00:00:00.000andStopwatchState= 0 in the tag simulator. - Press START. Confirm the counter advances at ~1.000 s per real second (4 increments per second at 250 ms trigger).
- Press PAUSE after 10 s. Confirm the display freezes at
00:00:10.xxx. - Press START again after 30 s. After 5 more real seconds, confirm the display reads
00:00:15.xxx(accumulator logic correct). - Press PAUSE then RESET. Confirm the display returns to
00:00:00.000and the state tag is 0. - Disconnect the PLC connection (or simulate a PLC stop). Confirm the stopwatch continues to function (this verifies HMI-only architecture).
- Run the stopwatch across a midnight boundary (or use the runtime's time-shift diagnostic) and verify the rollover handling in section 6.1.
9. Troubleshooting Matrix
| Symptom | Likely root cause | Corrective action |
|---|---|---|
| Display stays at 00:00:00.000 even after pressing START |
UpdateDisplay is not bound to a cyclic event, or the event is suspended |
Verify the cyclic trigger in the screen's Scheduled tasks list. In WinCC V7 confirm Global Script Runtime is enabled in the Computer properties. |
| Display jumps in 1-second increments | Cyclic trigger is configured but script engine is overloaded | Reduce trigger to 500 ms or split screens. Check the PDiagRT log for VBS timeouts. |
| Display reads negative time after pause/resume |
DateDiff returns 0 on the same second; the accumulator loses a second |
Use DateAdd for pause duration accumulation or accept the 1-second error if the spec allows it. |
| Buttons have no effect | User lacks the configured authorization level | Add the current user to the relevant group under User administration or remove the authorization check for testing. |
| Tag read returns invalid value in VBS | Internal tag has not been created in the tag manager or has wrong data type | Open the HMI tag table and verify the tag name, type, and length match the VBS access. Use HMIRuntime.Tags("X").QualityCode to check validity (144 = good). |
| Stopwatch resets after Runtime restart | Tags are not persistent | Enable persistence on all four stopwatch tags. Verify the persistence file path is writable. |
10. Comparison: HMI Stopwatch vs PLC Timer
| Criterion | HMI VBS implementation | PLC timer (S7-1500 IEC_TP, S7-1200 TON) |
|---|---|---|
| Real-time guarantee | No — best-effort, depends on HMI CPU load | Yes — deterministic, 1 ms resolution on S7-1500 |
| Works without PLC | Yes | No |
| Survives PLC connection loss | Yes | Stops on connection loss |
| Programming effort | Moderate (VBS, internal tags, event wiring) | Low (FB call in LAD/FBD/ST) |
| Reusability across screens | High — copy script file, no PLC changes | Requires PLC tag & FB instance in the project |
| Acceptable for safety functions | No | Only with F-CPU / F-TM |
| Display formatting flexibility | Full VBS string handling | Manual conversion to time formats in PLC |
Use the HMI-resident stopwatch for operator-information timing (cycle time observation, batch duration reporting, manual test stations). Use the PLC timer for any timing that influences control decisions.
11. Advanced Extensions
11.1 Lap times
Add a lap button that captures the current elapsed time into a text array (e.g. StopwatchLap_1 through StopwatchLap_20). The implementation is a 4-line addition to the OnStart handler.
11.2 Logging to CSV
Bind the StopwatchDisplay tag to a logging tag with a 1 s acquisition cycle. The CSV file is written to the configured log path and can be opened in Excel. The logging path is configured under Runtime settings > Logs > Directory; in TIA Portal the Logging option must be licensed.
11.3 Archiving the last N runs
For shift reports, accumulate the last 50 stopwatch values in a circular buffer in the VBS layer. Push the buffer to a recipe or data log on Reset so the values are available in the audit trail.
11.4 Migration to WinCC Unified
WinCC Unified (V17 and later) uses JavaScript instead of VBScript. The same logic translates directly; the object model is Tags("...").Read() / .Write(...) on the HMIRuntime. The WinCC Unified programming manual documents the differences. The internal-tag definitions and cyclic events are functionally identical.
12. Standards and Compliance Notes
Operator-information HMI functions are not within the scope of functional safety standards. The stopwatch described here is an operator information feature and must not be used to satisfy any safety function defined in ISO 13849-1 or IEC 61508. For functions that affect the safety state of the machine, use a safety-rated PLC timer in a Safety Integrated project.
FAQ
Can I implement a stopwatch in WinCC without a PLC timer and without VBScript?
Only with very low resolution. The Dynamic dialog of an I/O field can be set to "Display current time" but cannot be paused or reset. For any practical stopwatch with start/pause/reset semantics, VBScript or C script is required.
What is the best resolution I can get from the HMI stopwatch?
On a WinCC RT PC, the Timer function gives ~10 ms resolution when combined with a 100 ms cyclic event. On a Comfort Panel (TP / KTP series) the practical resolution is ~50 ms because the script scheduling jitter is higher. For sub-10 ms resolution, use a PLC IEC timer instead.
Will the stopwatch survive a Runtime restart?
Only if the four internal tags are configured as Persistent. On Comfort Panels the persistence file is written to flash on shutdown; on a power loss the in-memory state is lost but the persistent values are restored on the next boot. Frequent Reset actions are not a write-cycle concern because persistence is updated on controlled shutdowns only.
Can multiple operators run independent stopwatches on the same panel?
Yes. Create one set of four internal tags per stopwatch instance (e.g. Stopwatch1_State, Stopwatch2_State) and parameterize the VBScript actions with a tag-name prefix. The script-engine overhead scales linearly — keep the number of concurrent stopwatches below 8 on a TP1500 Comfort.
Why does my display freeze when the PLC connection drops?
It should not, because all tags are internal. If the display freezes, check that the cyclic event is not bound to a tag-change event on a PLC tag by accident. The cyclic event must use a Schedule trigger (time-based) not a Tag trigger.