WinCC Comfort: Cycle All Active Alarms in Single Line View on TP900/TP1900 Comfort
On a SIMATIC TP900 Comfort or TP1900 Comfort panel running WinCC Comfort V16 (TIA Portal V16 Update 6), the configured Alarm Line control shows only the first or the last entry of the active alarm queue, never all of them. The control is single-line by design: it reserves exactly one row on the screen and binds that row to a single record selected by the configured sort order. This article documents the root cause, the limits of the WinCC Comfort alarm API, and a robust, in-field-proven PLC-driven filter cycling workaround that rotates the display through every active alarm at a configurable dwell time, while keeping the original alarm text (with its dynamic process-value fields) intact.
1. Problem Definition
The Alarm Line control is a one-row, low-profile alarm indicator typically placed in the title bar or footer of a Comfort screen. The expected behaviour on panels with a small visible status area is:
- Show the most recent unacknowledged alarm, OR
- Cycle through all current active alarms at a user-defined interval (commonly 2 to 3 seconds per alarm).
What the panel actually does in TIA V16:
- Show first → static display of the topmost alarm in the sort order; the field never refreshes while that alarm is still active.
- Show last → static display of the bottom alarm; the field never refreshes while that alarm is still active.
If more than one alarm is active simultaneously, the operator never sees the other entries unless they open the full Alarm View. The 2-3 second automatic rotation that operators expect from SCADA-style ticker behaviour is not a built-in property of the Alarm Line control on Comfort panels.
1.1 Engineering Impact
The problem is most visible on machines with densely packed alarms - printing lines, packaging cells, conveyor sorters, HVAC plants - where the panel has room for one line of text and a full alarm view would obscure the underlying process image. Operators rely on the single-line indicator to know how many distinct faults are present, not only the most recent one.
2. Root Cause: WinCC Comfort Alarm System Constraints
WinCC Comfort (the runtime shipped on Comfort Panels) is a deliberately trimmed-down SCADA engine compared to WinCC Advanced or WinCC Professional. The trimming matters here because two capabilities that would normally solve the problem are missing:
| Capability | WinCC Professional | WinCC Advanced | WinCC Comfort (TP900/1900) |
|---|---|---|---|
| VBScript access to alarm buffers | Yes | Partial | No |
| System functions for alarm iteration at runtime | Yes | Limited set | No public iteration API |
| Animated alarm line with multi-record rotation | Yes (custom C / VB) | Yes (VB) | No - static first / last only |
| Direct binding of alarm text fields to a text list | Yes | Yes | No - text list is independent of alarm system |
Consequence: the only programmable handle the HMI engineer has on the active alarm set at runtime is the filter string of the Alarm Line / Alarm View. The filter is a string property that can be bound to a PLC tag, and the runtime re-evaluates that string every time the tag changes. This single, well-defined hook is what the workaround below exploits.
3. Required Hardware and Software
| Component | Specification / Order Number | Notes |
|---|---|---|
| HMI panel | 6AV2 124-1JC01-0AX0 (TP900 Comfort, 9") | WinCC Comfort V16.x runtime |
| HMI panel (large) | 6AV2 124-1UC02-0AX0 (TP1900 Comfort, 19") | Same firmware family |
| Engineering | TIA Portal V16 Update 6 | STEP 7 + WinCC Comfort/Advanced |
| PLC | S7-1500 (CPU 1511-1 PN or higher recommended) | S7-1200 also supported; S7-300/400 with caution |
| Connection | PROFINET or PROFIBUS, HMI tag connection enabled | Cycle controller runs in PLC cycle (OB1) |
| Active alarm count (design target) | ≤ 64 discrete alarms | Trivially scalable; only filter width changes |
References: Siemens Industry Online Support, TIA Portal V16 documentation collection, WinCC Comfort/Advanced V16 system manual, SIMATIC HMI Comfort Panels device manual.
4. Solution Architecture: PLC-Driven Filter Cycling
The workaround uses the Alarm Line's Filter property, bound to a PLC string tag, and rotates the value of that string so that the runtime shows a different alarm every N seconds.
The PLC owns the rotation logic; the HMI owns the alarm text rendering. The two meet at exactly one string tag that is updated by the PLC OB1 cycle and read by the Alarm Line every time its value changes.
5. Step 1 - HMI Alarm Configuration with Numeric Prefixes
Each alarm text entry must be unique and must contain a stable, machine-parseable prefix so that the filter can isolate exactly one alarm at a time.
5.1 Naming Convention
Use a fixed-width numeric prefix in front of the human-readable message. A 4-digit zero-padded prefix allows up to 9,999 uniquely-filterable alarms, which is well above the practical limit of any Comfort-panel project.
| Alarm number | Alarm text in HMI | PLC bit that triggers it |
|---|---|---|
| 0001 | 0001 Motor M12 overload - station 3 | %DB12.DBX0.0 |
| 0002 | 0002 Conveyor C4 belt slip | %DB12.DBX0.1 |
| 0003 | 0003 Hydraulic pressure low - 14.2 bar | %DB12.DBX0.2 |
| 0004 | 0004 Safety door open - cell B | %DB12.DBX0.3 |
| ... | ... | ... |
| 0064 | 0064 Air pressure below 4.0 bar | %DB12.DBX7.7 |
Open the HMI tags and alarms editor in TIA Portal V16, declare each as a discrete alarm, assign it to an alarm class (typically Errors for drive and process faults, Warnings for soft limits), and enter the alarm text exactly as above. The dynamic process-value fields (e.g. {Pressure} or station index) remain fully functional because they are still rendered by WinCC from the alarm buffer; the prefix is treated as part of the text only when filtering.
DB12.DBX0.0, then alarm 1 lives at bit 0 of byte 0. The same rule for alarm 8 (bit 0 of byte 1) keeps the PLC scan loop trivial.6. Step 2 - HMI Alarm Line Filter Binding
Place the Alarm Line control on the screen where the operator needs it. The default properties to adjust are:
| Property | Value | Why |
|---|---|---|
| Display mode | First active alarm | Will be overridden by filter; choose this for predictable scroll behaviour |
| Filter (column-bound) | "Alarm text" | Filter operates on text field where prefix lives |
| Filter (value-bound) | PLC tag HMI_FilterString (WSTRING[8]) |
Runtime reads this tag on every change |
| Sort | Priority / time, ascending | Deterministic when multiple alarms match the same filter |
Declare a new HMI tag HMI_FilterString of type WString length 8 (or String length 8, depending on the project), connected to the PLC tag of the same name. The length must be at least 5 characters to hold the prefix 0001 plus the implicit null terminator.
When the HMI tag value changes, the runtime re-evaluates the filter. Setting the filter to the string 0001 shows only the alarm whose text starts with 0001. Setting it to 0002 shows only the second one, and so on. Setting it to an empty string '' removes the filter and shows the first active alarm by sort order.
7. Step 3 - PLC Cycle Controller Implementation
The PLC owns the timing and the decision of which prefix to display. The following SCL block runs in OB1 (or in a cyclic OB 30 ms for tighter timing) and produces a new filter string every dwell time interval.
7.1 Data Block (DB of type)
TYPE "UDT_AlarmCycler"
STRUCT
arrActive : ARRAY[1..64] OF BOOL; // 1 = alarm active
iCurrent : INT; // Currently displayed index (0 = none)
tDwell : TIME; // Time per alarm, default 2 s
bAnyActive : BOOL; // Aggregate "at least one active"
sFilterOut : STRING[8]; // Filter string to HMI
END_STRUCT;
END_TYPE
7.2 Function Block
FUNCTION_BLOCK "FB_AlarmCycler"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
VAR_INPUT
iAlarmCount : INT := 64; // Number of alarms to scan (1..64)
tDwellTime : TIME := T#2S; // Display dwell time per alarm
END_VAR
VAR
statTimer : TON; // Cycle dwell timer
statIndex : INT := 0; // Search start index
statCur : INT := 0; // Currently displayed alarm
statI : INT; // Loop helper
statPad : STRING[4]; // Zero-padded prefix builder
END_VAR
VAR_OUTPUT
sFilterString : STRING[8]; // Bound to HMI tag HMI_FilterString
END_VAR
BEGIN
// 1. Aggregate: at least one alarm active?
#statI.%X0 := FALSE;
FOR #statI := 1 TO #iAlarmCount DO
IF "DB_Alarms".arrActive[#statI] THEN
#statI.%X0 := TRUE; // abuse of the loop variable as a temp BOOL
EXIT;
END_IF;
END_FOR;
// 2. Run the dwell timer when something is active
#statTimer(IN := #statI.%X0, PT := #tDwellTime);
// 3. On dwell expiry (or on first activation), pick the next active alarm
IF #statTimer.Q OR (#statCur = 0 AND #statI.%X0) THEN
#statTimer(IN := FALSE);
// Search forward from current+1, wrap once
#statIndex := 0;
FOR #statI := #statCur + 1 TO #iAlarmCount DO
IF "DB_Alarms".arrActive[#statI] THEN
#statIndex := #statI;
EXIT;
END_IF;
END_FOR;
IF #statIndex = 0 THEN
FOR #statI := 1 TO #iAlarmCount DO
IF "DB_Alarms".arrActive[#statI] THEN
#statIndex := #statI;
EXIT;
END_IF;
END_FOR;
END_IF;
#statCur := #statIndex;
END_IF;
// 4. Build the zero-padded filter string "0001" .. "0064"
IF #statCur > 0 THEN
#statPad := INT_TO_STRING(#statCur);
// Left-pad to 4 characters
WHILE LEN(#statPad) < 4 DO
#statPad := CONCAT('0', #statPad);
END_WHILE;
#sFilterString := #statPad;
ELSE
#sFilterString := '';
END_IF;
// 5. Write to global filter tag (visible on HMI connection)
"HMI_FilterString" := #sFilterString;
END_FUNCTION_BLOCK
statAnyActive : BOOL rather than abuse statI.%X0. The code above is shown compressed to keep the print short; in production use a separate BOOL to satisfy MISRA-SPS2 style checks and keep the optimizer happy.7.3 Clean Production Variant
Replacing the loop-variable abuse with a dedicated variable:
VAR
statAnyActive : BOOL;
END_VAR
BEGIN
// 1. Aggregate
#statAnyActive := FALSE;
FOR #statI := 1 TO #iAlarmCount DO
IF "DB_Alarms".arrActive[#statI] THEN
#statAnyActive := TRUE;
EXIT;
END_IF;
END_FOR;
// 2. Timer and rotation step 3..4 identical to the listing above,
// but use #statAnyActive instead of #statI.%X0.
END_FUNCTION_BLOCK
7.4 Bit-to-Array Population
The DB of type UDT_AlarmCycler still has to be populated. The simplest way is a single block call in OB1 that maps the 64 alarm bits to arrActive[1..64]:
// OB1 - compress the 64 alarm bits into the array
"DB_Alarms".arrActive[1] := "Alarm".MotorM12Overload;
"DB_Alarms".arrActive[2] := "Alarm".ConveyorC4BeltSlip;
"DB_Alarms".arrActive[3] := "Alarm".HydraulicLow;
"DB_Alarms".arrActive[4] := "Alarm".SafetyDoorB;
// ... 5 .. 64 ...
"FB_AlarmCycler_DB"(); // call the cycler
For projects that already maintain an array of alarm bits, the explicit mapping collapses to a single MOVE_BLK or BLK_MOVE instruction.
8. Step 4 - Tag Mapping and Cycle Time Tuning
Bind the PLC tag "HMI_FilterString" to the HMI tag of the same name (default acquisition cycle 1 s is acceptable; the PLC owns the dwell time). Adjust the following parameters for the installed process:
| Parameter | Typical value | Effect |
|---|---|---|
| tDwellTime (FB input) | T#2S to T#3S | Time each alarm remains visible |
| iAlarmCount (FB input) | actual number of alarms in the project | Limits the scan loop, improves cycle time |
| HMI tag acquisition cycle | 100 ms (fast) or 1 s (default) | Lower = more responsive but more HMI-CPU traffic |
| OB1 vs OB35 | OB1 for tDwellTime ≥ 1 s, OB35 for shorter | Keeps the OB1 scan deterministic for the rest of the program |
For 64 alarms and a 30 ms OB35, the cycle FB adds less than 50 µs of CPU load on a CPU 1511-1 PN - well below any threshold worth worrying about.
9. Step 5 - Verification and Commissioning
Commissioning follows the standard Siemens V16 sequence. The checklist below targets the specific behaviour of the alarm cycler.
- Download the PLC project, then the HMI project, to the panel.
- Force the alarm bits one at a time. Confirm that the Alarm Line shows only the matching text, e.g. forcing
arrActive[1]produces0001 Motor M12 overload - station 3in the line. - Force two alarms simultaneously (e.g. 0001 and 0003). The PLC should rotate between them every
tDwellTime. - Force all 64 bits. Confirm that the cycle is smooth and that the prefix always has exactly 4 characters (no spaces, no leading/trailing garbage) - this validates the pad loop.
- Clear all bits. The filter string should become
''and the Alarm Line should empty (or show the configured default), not freeze on the last prefix. - Force a non-existent prefix, e.g. set
statCur := 99in the FB. The runtime should display an empty Alarm Line - confirming that unmatched filters do not crash the line. - Trigger a 100 ms burst of all 64 alarms and then clear them. Confirm that the FB settles back to an empty filter within one dwell interval.
9.1 State Machine (Verification Aid)
9.2 Timing Diagram (Dwell = 2 s, 3 active alarms)
10. Edge Cases, Limitations, and Field Notes
| Situation | Observed behaviour | Recommended action |
|---|---|---|
| Alarm text contains the same prefix as another alarm (typo in HMI engineering) | Filter matches both; display order depends on sort field | Enforce a unique prefix via the HMI alarm export / Excel import check |
| Alarm bit flutters faster than the dwell time | Filter still rotates on the FB schedule, so transient bits may be displayed only briefly | Increase PLC-side debounce (e.g. min 500 ms "stable active" flag) |
| Operator clears all alarms while line is showing 0003 | Filter goes to ''; line empties on next refresh |
Acceptable, but a 1 s "hold last" timer can be added for visual comfort |
| More than 64 active alarms | Cycle period becomes long (4 s × N) - operator perception drops | Raise iAlarmCount; or fall back to a full Alarm View screen |
| Project migrated to V17 / V18 | Same FB works; HMI alarm buffer API still closed on Comfort | No code change required |
| WinCC Advanced target instead of Comfort | Native VBScript is available - can solve the same problem with a script and an internal tag | Prefer script-based cycler on Advanced; keep PLC method as fallback |
| S7-1200 (no SCL timers as comfortable) | Workaround still works, use TP on a TON in SCL |
Test cycle time on the specific CPU; firmware ≥ V4.2 recommended |
| PROFIBUS HMI connection at 1.5 Mbit/s | String transmission adds ~40 ms latency | Use PROFINET where possible; raise dwell time to 3 s |
11. Alternative Approaches on Comfort Panels
The PLC-driven filter method is the most robust, but three other techniques are sometimes considered. The table below ranks them for a TP900/TP1900 Comfort target.
| Approach | Pros | Cons | Verdict |
|---|---|---|---|
| PLC-driven filter cycling (this article) | Deterministic; works on every Comfort firmware; preserves dynamic text | Requires prefix convention; adds PLC code | Recommended |
| I/O field with text list cycled by PLC | Quick to implement | Alarm list and text list drift; dynamic values lost | Acceptable for non-dynamic messages only |
| Full Alarm View screen with auto-open timer | Standard Siemens pattern; no engineering trick | Covers the process screen; not a single-line indicator | Use for operator-call screens, not title-bar indicators |
| User-defined web control hosted on the panel (V16+) | Full HTML/JS flexibility | Comfort Panels ship without a public web control in V16; only Advanced/Professional | Not available on Comfort V16 |
For projects that need to move to WinCC Professional later, the same prefix scheme is harmless - the Professional runtime ignores the filter when no other filter is configured - so no rework is required at the upgrade boundary.
12. Frequently Asked Questions
Why does the Alarm Line control in WinCC Comfort V16 not rotate through active alarms by itself?
The Alarm Line control is bound to a single record from the alarm buffer at any moment. WinCC Comfort does not expose a runtime API or system function that lets a script iterate the alarm buffer and reassign the line's source, so the only programmable handle the engineer has is the filter string, which selects which record(s) are visible.
Can I use a ladder (LAD) block instead of SCL for the cycle controller?
Yes, but it is more verbose. The key constructs - the two FOR loops and the zero-pad WHILE loop - are most compact in SCL. In LAD/FBD the equivalent requires a counter, a comparator chain, and a hand-written BCD-to-string conversion block. On an S7-1500 there is no runtime penalty difference between SCL and LAD for a block of this size.
How long can a filter string be, and how many alarms can I address uniquely?
The filter accepts up to the length of the alarm text column, usually 256 characters in WinCC Comfort. A 4-digit zero-padded prefix gives 9,999 unique filters, which is more than enough for any Comfort panel. If the project exceeds 9,999 alarms, raise the prefix width to 5 digits and resize the HMI tag to STRING[6].
Does the prefix show up in the operator's view, or can it be hidden?
The prefix is part of the alarm text and will appear in the Alarm Line. Operators rapidly learn to ignore it, and the leading-zero numeric format reads as a stable "alarm number" rather than as a label. If it must be hidden, configure a second alarm class with a transparent text field, but this is rarely worth the engineering effort on a single-line indicator.
Is the same approach valid on S7-1200 and on PROFIBUS-connected panels?
Yes. The SCL block runs on S7-1200 firmware ≥ V4.2 with no change. On PROFIBUS the string transmission adds about 30-50 ms of latency, so use a dwell time of at least 2.5-3 s to give the operator time to read the message. PROFINET is recommended for the smoothest cycling, but PROFIBUS at 1.5 Mbit/s remains acceptable for 64 alarms and a 2 s dwell.
Can the cycler handle analog alarms with a process value, e.g. "0007 Pressure = 12.4 bar"?
Yes. The filter matches the fixed prefix 0007 only; the dynamic value 12.4 is rendered by WinCC from the alarm buffer once the record is selected. The PLC only needs to set the active bit for that record - it never touches the formatted number.