Problem Statement
WinCC Flexible 2008 / WinCC Flexible 2008 SP3 (and earlier TIA Portal WinCC Comfort/Advanced predecessors) display alarm text via two distinct mechanisms: Alarm lists (driven by bit/discrete triggers or limit-value monitoring) and Text lists (driven by tag value change). When a project uses a Text list tied to a single WORD tag (for example a DBW on an S7-300/400 PLC), the visible alarm text reflects the current value of that tag. As soon as the tag value changes, the prior notification text is overwritten and disappears from the HMI screen. The engineer cannot migrate to a standard alarm list because the trigger source is itself a numeric WORD coming from the PLC, and WinCC Flexible's built-in alarm logging cannot be conditioned on "equals specific value" without a script.
The net symptom: only the last text-list message is ever visible. The previous nine — required by the operator's spec — are lost. This article documents the script-based workaround using the AlarmLog object and a cyclic VBScript routine that reads the current text list index, resolves it to a string, and pushes the entry into a user archive that can be displayed on a custom alarm view.
Root Cause: Text List vs. Alarm List Trigger Semantics
Understanding the limitation requires looking at the underlying trigger model in WinCC Flexible's runtime:
| Property | Text List | Alarm List (Bit / Limit) |
|---|---|---|
| Trigger source | Any tag (INT/WORD) — fires on value change | Discrete bit or analog limit (high/low) on a tag |
| Event retention | None — display reflects current value only | Built-in logging in AlarmLog with acknowledge / clear states |
| Number of simultaneously visible entries | 1 (current value) | Configurable queue (depends on configured buffer size) |
| PLC tag requirement | Numeric tag (BYTE/WORD/DWORD/INT) | Bit tag for discrete, or any tag with configured limits |
| Time stamp | Not stored automatically | Automatic (PLC or HMI time, configurable) |
WinCC Flexible's OnValueChanged event for a tag is the only built-in mechanism that fires when a WORD transitions to a new value. The text list is bound to that event, so it is a stateless renderer — it always shows what the tag currently equals. There is no concept of a "history" within a text list itself.
The built-in alarm system, by contrast, generates a discrete AlarmEvent object every time a configured bit goes high or an analog goes out of limit. Each event is timestamped, queued, and can be acknowledged. But you cannot configure a discrete trigger that says "fire an alarm when tag X equals 5." You can only say "fire when tag X exceeds limit L." Hence the engineering deadlock: the PLC provides a code number, not a bit pattern, and the HMI must decode it into a human-readable string.
Solution Architecture: Text List → Script → User Archive → Custom View
The script-based method reproduces alarm-list behavior on top of a text list. The data path is:
- PLC tag (DBW) writes a numeric code into the HMI tag.
- Text list is bound to that HMI tag and resolves the code to a string at display time.
- VBScript scheduled task runs on a 500–1000 ms cycle. It reads the current text-list index, compares it to the previously stored index, and when a change is detected, writes a new record into a User Archive (CSV-backed) or into an internal tag array.
-
Custom alarm view on the HMI screen is configured as an
Alarm Controlwith a filter that displays only the user-archive records, ordered newest-first, with scrollable history.
Components required on the HMI project side:
- One HMI tag of type
WORDorINTmirroring the PLC DBW. - One Text list with N entries mapping each code (0..N-1) to its display string.
- One internal tag of type
WORDnamedLastIndexto remember the previously displayed value. - One User Archive (CSV file) named
AlarmArchivewith columns:TimeStamp(STRING 20),Code(WORD),Message(STRING 50),State(STRING 10). - One scheduled VBScript task running every 500 ms.
- One screen with an
Alarm Controlobject bound to the User Archive via ODBC or direct CSV reference.
Step-by-Step Implementation
1. Create the text list
In the WinCC Flexible project tree, right-click Text and Graphic Lists → Text Lists and create a list named AlarmTexts. Add one entry per alarm code, for example:
| Value | Text (English) | Text (German) |
|---|---|---|
| 0 | No active alarm | Kein Alarm |
| 1 | Motor overload trip | Motorüberlast |
| 2 | Coolant pressure low | Kühlmitteldruck niedrig |
| 3 | Spindle temperature high | Spindeltemperatur hoch |
| 4 | Door interlock open | Türverriegelung offen |
| 5 | Hydraulic filter dirty | Hydraulikfilter verschmutzt |
| 10 | E-Stop pressed | Not-Halt gedrückt |
| 20 | Servo drive fault | Servo-Antriebsfehler |
| 99 | Unknown code | Unbekannter Code |
Bind the text list to a screen IO field or Symbolic IO field with output mode Text list and selection AlarmTexts. Confirm in RT that the displayed string updates when the HMI tag value changes.
2. Create the user archive
Open Archives → User Archives and create AlarmArchive with the columns above. The archive is a CSV file stored on the HMI panel's flash or under \Storage Card\Logs\ on PC-based runtime. Configure the archive to be a limited ring buffer; 1000 records is a reasonable default for a single shift and consumes roughly 80 KB.
| Column | Data type | Length | Notes |
|---|---|---|---|
| TimeStamp | STRING | 20 | Format YYYY-MM-DD HH:MM:SS |
| Code | WORD | — | Numeric code as received from PLC |
| Message | STRING | 50 | Resolved text from text list |
| State | STRING | 10 | "ACTIVE" / "CLEARED" |
3. Configure internal tags
-
AlarmTag— type WORD, sourced from PLC DBW (e.g.DB100.DBW0). Connected to the text list. -
LastIndex— type WORD, internal, persisted to retain value across screen changes. Used by the script to detect transitions. -
LastState— type STRING, internal, 10 chars. Used to remember if the previous record was ACTIVE or CLEARED so that returning to code 0 emits a CLEARED event.
4. Write the scheduled VBScript
Open Schedules → Tasks and create a task with trigger Cyclic, 500 ms. Paste the following into the VBScript editor:
'------------------------------------------------------------------
' Archive text-list alarm transitions into a User Archive.
' Runs every 500 ms in WinCC Flexible RT.
'------------------------------------------------------------------
Option Explicit
Dim curCode, prevCode, curText, curState, prevState
Dim archiveName, oArchive, oRecordset
Dim timeStr
archiveName = "AlarmArchive"
curCode = SmartTags("AlarmTag").Value
prevCode = SmartTags("LastIndex").Value
prevState = SmartTags("LastState").Value
' Resolve text list entry by index
curText = ""
Select Case curCode
Case 0 : curText = "No active alarm"
Case 1 : curText = "Motor overload trip"
Case 2 : curText = "Coolant pressure low"
Case 3 : curText = "Spindle temperature high"
Case 4 : curText = "Door interlock open"
Case 5 : curText = "Hydraulic filter dirty"
Case 10 : curText = "E-Stop pressed"
Case 20 : curText = "Servo drive fault"
Case Else : curText = "Unknown code " & CStr(curCode)
End Select
' Determine state
If curCode = 0 Then
curState = "CLEARED"
Else
curState = "ACTIVE"
End If
' Only act on transition
If curCode <> prevCode Or curState <> prevState Then
timeStr = Year(Now) & "-" & _
Right("0" & Month(Now), 2) & "-" & _
Right("0" & Day(Now), 2) & " " & _
Right("0" & Hour(Now), 2) & ":" & _
Right("0" & Minute(Now), 2) & ":" & _
Right("0" & Second(Now), 2)
Set oArchive = HMIRuntime.BaseScreenName.Parent.Parent.GetObject(archiveName)
If IsNull(oArchive) Then
HMIRuntime.Trace "Archive not found: " & archiveName & vbCrLf
Exit Sub
End If
' Append a record at the head of the user archive
oArchive.MoveFirst
oArchive.Add
oArchive.SetFieldValue "TimeStamp", timeStr
oArchive.SetFieldValue "Code", curCode
oArchive.SetFieldValue "Message", curText
oArchive.SetFieldValue "State", curState
oArchive.Update
SmartTags("LastIndex") = curCode
SmartTags("LastState") = curState
End If
HMIRuntime.BaseScreenName access varies slightly between WinCC Flexible 2008 and the TIA WinCC Runtime Advanced. For TIA-based projects, replace the GetObject call with HMIRuntime.Tags(archiveName).Read via an OLE DB wrapper, or use the documented UserArchiveControl scripting interface. Refer to the Siemens WinCC Flexible Programming and Reference Manual for the exact COM entry point supported by your RT version.5. Display the archive in an Alarm Control
On the alarm screen, drop an Alarm Control object. Configure its source as User Archive → AlarmArchive. Enable the columns TimeStamp, Code, Message, State. Set sort order to TimeStamp descending. Set visible row count to 10 to match the operator's "show last 10 alarms" requirement. Tick Auto-scroll off so the operator can scroll the history without losing their place.
6. Optional: clear-on-acknowledge workflow
For an operator-acknowledge workflow, add a button on the alarm screen with the OnClick event:
Sub AcknowledgeSelected()
Dim oView, oRow, oCol
Set oView = ScreenItems("AlarmControl1").GetRowCollection()
For Each oRow In oView
oRow.SetFieldValue "State", "ACK"
Next
End Sub
The state column becomes ACK and a visual flag can be added in the Alarm Control's column formatting.
Alternative: Internal Tag Array (No User Archive)
For panel-based runtimes (KP / KTP / TP panels with limited flash write endurance), avoid the CSV archive and store the last 10 entries in an array of internal tags. The trade-off is no persistent log across power-cycle:
' Shift registers of length 10
Dim i, tagName
For i = 9 To 1 Step -1
tagName = "Hist_" & CStr(i)
SmartTags(tagName).Value = SmartTags("Hist_" & CStr(i-1)).Value
Next
SmartTags("Hist_0").Value = curCode & "|" & curText
Create 10 string tags named Hist_0 through Hist_9 and display them in 10 symbolic IO fields on the screen, ordered with Hist_0 at top.
Verification and Acceptance Test
After the project is compiled and downloaded, perform this validation sequence on the live HMI before sign-off:
- Force
AlarmTag = 1from the PLC or via the HMI's tag simulation table. Confirm the text list shows Motor overload trip and a new row appears in the Alarm Control with code 1, state ACTIVE, current timestamp. - Change
AlarmTagto 3. Confirm row 2 appears with code 3 and row 1 (code 1) is still present above it in descending timestamp order. - Continue cycling through codes 0, 5, 10, 20, 0. Confirm 7 rows are present and the most recent CLEARED event (code 0) is at the top.
- Return to
AlarmTag = 1. Confirm an ACTIVE row is added again, not a duplicate of the original. - Power-cycle the panel. On panels with persistent user archives, confirm all rows are still present after reboot. On panels with the array-only variant, confirm rows 1–10 are reset to empty strings.
- Disconnect the PLC connection while the script is running. Confirm the script does not write garbage to the archive; trace log should show the script exiting cleanly via
Exit Subin the catch block. - Inject a code value not in the text list (e.g. 77). Confirm the row Unknown code 77 is added so unknown PLC states are still observable.
Performance, Footprint and Lifecycle Considerations
- CPU load: A 500 ms cycle on a TP 177B / OP 177B consumes roughly 1–2 % CPU on the script alone. The User Archive CSV write is the dominant cost; if the panel is resource-constrained, raise the cycle to 1000 ms or switch to the internal-tag array method.
- Flash wear: CSV user archives write a full file rewrite on each append on most panels. Limit the archive to 1000 records and accept that the file will eventually be cycled. For long-life applications (24/7, multi-year), export the CSV to a USB stick daily via a scheduled task instead of relying on the on-board flash.
- Time stamp source: WinCC Flexible can use either the panel's local clock or the PLC's clock. For a multi-HMI cell, set all panels to use the PLC clock via Time Synchronization in the project settings; otherwise event ordering between panels is meaningless.
-
Language switching: The hard-coded
Select Casestrings above are language-specific. For multilingual deployments, read the current language ID fromHMIRuntime.Languageand select the matching text-list column. Alternatively, bind the script'scurTexttoSmartTags("AlarmTag")and use the built-in text-list multilanguage feature by reading the appropriate index from the user-archive column.
When NOT to Use This Workaround
This script-based archive is appropriate when the PLC code is fixed and a discrete-bit alarm interface is impossible. It is not appropriate when:
- The PLC can be modified to expose a bit per alarm state. Always prefer this option for new projects.
- You are migrating to TIA Portal with WinCC Unified. Use the native
Multiplexingtag feature and PLC alarm text lists introduced in WinCC V17 / TIA V17 — they solve this exact problem without scripts. Reference the SIMATIC WinCC Unified Engineering V18 Manual for the multiplex configuration path. - The HMI runtime is WinCC (PC-based, not Flexible). PC-based WinCC has direct
WinCC AlarmControl+TagMultiplexand does not require this workaround. - SIL / safety-relevant alarms are involved. Use a Safety-Integrated alarm path; do not retrofit SIL events through a text list.
Troubleshooting Matrix
| Symptom | Likely Cause | Action |
|---|---|---|
| Script fires but no row appears in Alarm Control | User Archive is not bound to the control, or alarm view column names do not match the archive field names exactly | Open Alarm Control configuration → Columns and confirm TimeStamp, Code, Message, State are present and bound to the archive fields |
| Every poll writes a new row even when code is unchanged |
LastIndex tag is not persisted, or HMI tag update is jittering on the LSB |
Mark LastIndex as Persistent. Add a hysteresis filter: only act when |curCode - prevCode| > 0 and the value has been stable for two consecutive cycles |
| Archive is empty after power-cycle | User Archive persistence is not enabled in the project settings | Open Archives → User Archives → AlarmArchive → Properties and enable Save archive retentively |
| Time stamp is wrong or 1970-01-01 | Panel clock is not set, or the project was downloaded with the wrong time zone | Set panel time via Control Panel → Date/Time on the device, or set up Time Master in the project with the PLC as the source |
| Script halts RT after a few hours | Memory leak from opening the archive object without releasing it, or the file handle is exhausted | Refactor to use Set oArchive = Nothing after each Update. Consider switching to the internal-tag array method |
| Code 99 displayed but PLC sends valid code 5 | PLC and HMI use different byte orders (big-endian vs little-endian) on the WORD tag | Set tag adaptation to Byte swap in the HMI tag properties, or change the PLC to use WORD in the correct byte order |
| Operator screen shows nothing for 30 s after a tag change | Cycle is too long or the script is blocked by a long-running call | Reduce the cycle to 250 ms, profile the script, and split I/O-bound work into smaller chunks |
Migration Path: TIA Portal WinCC Unified
If a project is being modernized, the script-based workaround can be retired. In TIA Portal V17 and later, configure a Multiplexed tag on a WinCC Unified Comfort Panel. The multiplex tag accepts a WORD value and indexes into a text list, just like the old text list, but each transition also generates a discrete alarm event in the unified alarm system with a timestamp, state, and acknowledgement — exactly the behavior the operator originally requested. The configuration is under HMI Tags → [tag] → Properties → Multiplexing. Reference the SIMATIC WinCC Unified System Manual for the procedure.
Why does the text list show only one message at a time in WinCC Flexible?
WinCC Flexible text lists are stateless renderers. The text list is bound to a tag's OnValueChanged event, so the HMI always displays the string that corresponds to the tag's current numeric value. There is no built-in buffer for previous values — the previous string is overwritten the instant the tag changes.
Can a standard alarm list be triggered by a numeric code in a DBW?
No. The WinCC Flexible alarm system fires only on bit transitions (on/off) or on analog high/low limit violations. There is no built-in trigger that says "fire an alarm when tag X equals 5." To use a standard alarm list, expose 10 discrete bits from the PLC, one per alarm state, and bind each bit to a discrete alarm event.
What is the smallest VBScript routine to archive a text-list transition?
A 500 ms scheduled task that reads SmartTags("AlarmTag"), compares it to a persisted LastIndex tag, and on a transition writes a new row into a User Archive via oArchive.Add + oArchive.SetFieldValue + oArchive.Update. The full code is provided in Section 4 above.
Does the user archive persist across power-cycle on a TP panel?
Only if the archive property Save archive retentively is enabled in the project. Otherwise the CSV is regenerated empty on each boot. The internal-tag array alternative does not persist at all and is reset to empty on reboot.
Is there a native WinCC Unified replacement for this script?
Yes. In TIA Portal V17 or later, configure a Multiplexed tag on a WinCC Unified Comfort Panel. The multiplex tag indexes a WORD into a text list and emits a discrete alarm event on every transition, with timestamp and acknowledgement built in. No script is required.