Detecting whether at least one alarm is currently active in a Siemens S7-1200 data block is a recurring requirement: a single aggregated status bit is needed for an HMI header indicator, a horn output, a dispatcher e-mail trigger, or a higher-level SCADA summary tag. The S7-1200 system library (Libraries → Standard Library) ships function blocks for diagnostic buffer retrieval and process diagnostics, but it does not contain a generic, reusable FB that returns AnyAlarmActive := TRUE when at least one entry in a user-defined alarm DB is set. The detection must be built from first principles using the UDT (User-Defined Type) that defines the alarm record. This reference shows the supported design patterns on S7-1200 CPU FW 4.5 / TIA Portal V17.0.0.6 and the variants that also work on S7-1500 and on the S7-1200 FW 4.6 / TIA V18 generation.
1. Why No Public Library Function Exists
The standard S7-1200 / S7-1500 programming environment provides no library FB that operates over a user-defined array of UDT instances, because the firmware cannot introspect the contents of an arbitrary DB. The CPU has no symbol table at runtime; it only sees absolute memory offsets. Any "is alarm X active" evaluation must be authored by the programmer against a known UDT layout. The reference approach is therefore:
- Define a single UDT
"UDT_Alarm"that contains a BooleanActivebit (and typicallyAck,Priority,Tag,Timestamp). - Instantiate an array DB of that UDT (e.g.
"DB_Alarms".Alarm[1..200]). - Add a scanning routine — in SCL, STL, or LAD — that walks the array, OR-reduces the
Activebits, and writes a singleAnyAlarmActivebit into a global DB or a bit memory byte for the HMI.
This pattern is identical in concept on S7-1500 (where the IEC timer/counter instructions can be used) and on S7-300/S7-400 with STEP 7 V5.x, although the exact instruction set differs.
2. Prerequisites
| Item | Requirement |
|---|---|
| CPU | S7-1200 (tested on 1214C / 1215C / 1217C) FW 4.5 or later; S7-1500 also supported |
| Engineering | STEP 7 Professional V17 (V17.0.0.6) or V18 for FW 4.6 CPUs |
| Language | SCL (preferred) or LAD/FBD; STL optional on S7-1500 |
| Libraries | Standard Library → "IEC 61131-3 Elements" (FOR, EXIT), "Bit Logic" |
| DB space | Reserve at least 64 bytes for the aggregator DB and 32 bytes/UDT instance |
| Cycle budget | < 2 ms for 200 alarms; tested on CPU 1214C with 0.4 ms typical |
FOR ... TO ... DO ... END_FOR construct with an array index variable bound at runtime in optimized DBs. If you must support FW 4.2, switch the alarm DB attribute to non-optimized (or use the equivalent indirect addressing in STL). The recipes in this article assume FW 4.5 + optimized access, which is the standard configuration for new TIA V17 projects.3. UDT Definition for the Alarm Record
Open PLC data types in the project tree and create UDT_Alarm with the following structure. The Active field is the only one required for the aggregator; the others enable proper HMI alarm view rendering.
TYPE UDT_Alarm
STRUCT
Active : BOOL; // Set by detection logic; cleared on ack
Ack : BOOL; // HMI acknowledgment bit
Priority : BYTE; // 0..15 (used for filtering)
Class : USINT; // 0=Info, 1=Warning, 2=Fault (per VDI 3690 mapping)
Tag : DWORD; // 32-bit identifier / tag reference
Timestamp : DTL; // Date_And_Time, 8 bytes
END_STRUCT
END_TYPE
Size of UDT_Alarm = 1 + 1 + 1 + 1 + 4 + 8 = 16 bytes. An array of 200 alarms occupies 3 200 bytes plus the array header. For most S7-1200 CPUs this is negligible against the 50 KB work-memory budget.
4. Alarm Storage DB
Create DB_Alarms with optimized block access and the Array property:
DATA_BLOCK DB_Alarms
STRUCT
Alarm : ARRAY[1..200] OF UDT_Alarm;
END_STRUCT
BEGIN
END_DATA_BLOCK
Set the DB attributes:
- Optimized block access: enabled (default in V17)
- Accessible from HMI/OPC UA: enabled (so the HMI alarm control can subscribe)
- Retain: enabled if alarms must survive a power cycle
5. Method 1 — SCL Any-Bit Scan with Early Exit
The canonical pattern. The FOR loop walks the array, sets AnyActive := TRUE on the first hit, and EXITs to avoid wasting cycle time. This is the recommended implementation when the alarm count is high and most cycles evaluate a quiet plant.
// FB_ScanAlarms (SCL)
FUNCTION_BLOCK "FB_ScanAlarms"
VAR
AnyActive : BOOL;
ActiveCount : UINT;
HighestPrio : BYTE;
ScanIndex : INT;
END_VAR
BEGIN
AnyActive := FALSE;
ActiveCount := 0;
HighestPrio := 0;
FOR ScanIndex := 1 TO 200 DO
IF "DB_Alarms".Alarm[ScanIndex].Active THEN
AnyActive := TRUE;
ActiveCount := ActiveCount + 1;
IF "DB_Alarms".Alarm[ScanIndex].Priority > HighestPrio THEN
HighestPrio := "DB_Alarms".Alarm[ScanIndex].Priority;
END_IF;
// Early exit: we only need "at least one" for AnyActive
// but the count is still useful for HMI — keep scanning
END_IF;
END_FOR;
// Publish results to the global aggregator DB
"DB_Aggregate".AnyAlarmActive := AnyActive;
"DB_Aggregate".AlarmCount := ActiveCount;
"DB_Aggregate".HighestPrio := HighestPrio;
END_FUNCTION_BLOCK
Call FB_ScanAlarms() in OB1 (main cyclic) or, for large arrays, in a cyclic OB with a longer time base (e.g. OB35 at 100 ms) to keep the fast OB1 lean.
6. Method 2 — SCL OR-Mask Reduction (Bit-Flattened Arrays)
If the UDT is reduced to a single bit per alarm (e.g. a "flat" alarm word array used as a status summary), the scan can be implemented as a word-wise OR fold. This is the fastest path because the CPU performs the OR in the binary layer; the loop in SCL becomes a small constant bound.
// 200 bits = 13 words (WORD). One word per 16 alarms.
// Use IEC 61131 bit-shift OR pattern (no STL needed).
VAR_TEMP
i : INT;
Fold : DWORD;
END_VAR
Fold := 0;
FOR i := 0 TO 12 DO
Fold := Fold OR SHL_DWORD( WORD_TO_DWORD( "DB_AlarmsFlat".Word[i] ), i*16 );
END_FOR;
"DB_Aggregate".AnyAlarmActive := ( Fold <> 0 );
"DB_Aggregate".AlarmCount := DWORD_TO_UINT( __POP_COUNT( Fold ) );
The __POP_COUNT intrinsic is available on S7-1500 (FW 2.0+) and S7-1200 FW 4.4+. It returns the number of set bits, eliminating the need for a second scan pass to compute the active count. On older firmware, build the count with the Method 1 pattern.
7. Method 3 — Counter-Only Scan (Statistical Use)
When the HMI only needs the numeric count of active alarms (for example, a badge showing "12 active"), drop the boolean aggregator and store the count in a single UINT tag. This avoids the BOOL <> 0 comparison and reduces HMI polling load because the HMI reads one variable instead of one bit.
VAR
i : INT;
Cnt : UINT;
END_VAR
Cnt := 0;
FOR i := 1 TO 200 DO
IF "DB_Alarms".Alarm[i].Active THEN
Cnt := Cnt + 1;
END_IF;
END_FOR;
"DB_Aggregate".AlarmCount := Cnt;
"DB_Aggregate".AnyAlarmActive := ( Cnt > 0 );
8. Method 4 — Edge Detection for State Transitions
Many applications need to trigger an event on the rising edge of the "any active" flag — for example, sounding a horn the moment a new alarm appears, then latching until acknowledged. Implement a one-cycle pulse in the same FB.
VAR
sAnyActive : BOOL; // static, used to detect edge
RisingEdge : BOOL;
FallingEdge : BOOL;
END_VAR
// Update sAnyActive from scan output
sAnyActive := "DB_Aggregate".AnyAlarmActive;
// Rising edge = transition from "no alarms" to "at least one alarm"
IF sAnyActive AND NOT "DB_Aggregate".sAnyActivePrev THEN
RisingEdge := TRUE;
ELSE
RisingEdge := FALSE;
END_IF;
// Falling edge = transition to "all clear" (all alarms acked or cleared)
IF (NOT sAnyActive) AND "DB_Aggregate".sAnyActivePrev THEN
FallingEdge := TRUE;
ELSE
FallingEdge := FALSE;
END_IF;
"DB_Aggregate".sAnyActivePrev := sAnyActive;
"DB_Aggregate".NewAlarm := RisingEdge;
"DB_Aggregate".AllClear := FallingEdge;
Wire DB_Aggregate.NewAlarm to a one-shot TP (IEC pulse timer) for the horn latch. The AllClear pulse is suitable for resetting a yellow beacon or sending a process-clear notification to SCADA.
9. Method 5 — Reusable FB with Multi-Instance and Indirect Access
For plants with several alarm DBs (per area, per machine), encapsulate the scan in a parameterized FB. The instance DB then becomes the per-area aggregator. On S7-1500, pass the alarm DB number as an INPUT of type BLOCK_DB for indirect access. On S7-1200 (optimized access only), pass the UDT array symbol directly — indirect DB addressing is not allowed with optimized blocks.
FUNCTION_BLOCK "FB_AreaAlarmScan"
VAR_INPUT
i_AlarmCount : UINT; // length of the array (e.g. 200)
END_VAR
VAR_IN_OUT
io_AlarmArray : ARRAY[*] OF "UDT_Alarm"; // Variablen Index 1..i_AlarmCount
END_VAR
VAR_OUTPUT
o_AnyActive : BOOL;
o_Count : UINT;
o_HighestPrio : BYTE;
END_VAR
VAR
i : DINT;
END_VAR
BEGIN
o_AnyActive := FALSE;
o_Count := 0;
o_HighestPrio := 0;
FOR i := 1 TO UINT_TO_DINT(i_AlarmCount) DO
IF io_AlarmArray[i].Active THEN
o_AnyActive := TRUE;
o_Count := o_Count + 1;
IF io_AlarmArray[i].Priority > o_HighestPrio THEN
o_HighestPrio := io_AlarmArray[i].Priority;
END_IF;
END_IF;
END_FOR;
END_FUNCTION_BLOCK
Call pattern from OB1:
"DB_Agg_Area1"( io_AlarmArray := "DB_Alarms_Area1".Alarm,
i_AlarmCount := 200 );
"DB_Agg_Area2"( io_AlarmArray := "DB_Alarms_Area2".Alarm,
i_AlarmCount := 80 );
10. Comparison Matrix of Detection Methods
| Method | CPU FW | Cycle (200 alarms) | HMI load | Edge detect | Use case |
|---|---|---|---|---|---|
| SCL FOR + EXIT | 4.2+ | ~0.4 ms (early exit common) | 1 BOOL + 1 UINT | Add Method 4 | General purpose, default choice |
| OR-fold (word wise) | 4.4+ | ~0.05 ms | 1 BOOL + 1 UINT | Add Method 4 | High alarm count (>1000) and tight cycle |
| Counter only | 4.2+ | ~0.4 ms | 1 UINT | No | Count badge only |
| Edge detect layer | 4.2+ | +0.01 ms | 2 BOOL | Yes | Horn, dispatch event |
| Reusable FB (variabel Index) | 4.5+ | ~0.4 ms/inst | Per-area | Yes | Multi-area plant, S7-1500 projects |
11. HMI and Program-Flow Integration
The aggregator output bits are typically consumed in three places:
-
HMI alarm control (WinCC Comfort/Professional, Unified): enable the State column to show pending vs. active; subscribe the
AnyAlarmActivebit to a status word on a header graphic. In the alarm control configuration, set the Acknowledge tag toDB_Alarms.Alarm[i].Ackso the same UDT field is written back from the HMI. -
Plant horn / beacon: drive the output from the rising-edge pulse (Method 4) latched by an SR flip-flop; clear with an HMI "Acknowledge Horn" button that resets the SR input only after
AllClear := TRUE. -
SCADA / OPC UA dispatcher: publish
AnyAlarmActive,AlarmCount, andHighestPrioas OPC UA nodes. The companion server exposes the entireDB_Alarmsarray on demand for full alarm viewer access.
On the S7-1200, the OPC UA server is available from FW 4.4 onward; on S7-1500 it is standard. Limit the publishable surface to the aggregator tag plus the Active bits to keep the address space small.
12. Verification and Commissioning Checks
-
Static state: with no alarms forced,
DB_Aggregate.AnyAlarmActive := FALSE,AlarmCount := 0,HighestPrio := 0. Verify in the watch table. -
Single forced alarm: set
DB_Alarms.Alarm[17].Active := TRUE;AnyAlarmActivemust go TRUE in the same OB1 cycle the scan is called. Reset to FALSE one cycle after clearing. -
Bulk stress: force all 200 alarms true; cycle time on CPU 1214C must stay under 5 ms;
AlarmCountmust read 200. -
Edge detection: place a breakpoint on the RisingEdge assignment; trigger one alarm, step the cycle once, confirm
RisingEdge := TRUE; step again, confirm it is FALSE. -
Power cycle: with Retain enabled on
DB_Alarmsand on the alarm bits, perform a STOP → RUN transition; the aggregator must reflect the retained state immediately. -
HMI round-trip: acknowledge an alarm from the HMI; confirm
DB_Alarms.Alarm[i].Ackis set, then the application logic clearsActive; the aggregator must drop one cycle later.
13. Troubleshooting Matrix
| Symptom | Likely cause | Fix |
|---|---|---|
AnyAlarmActive never goes TRUE despite a forced alarm |
Scan FB not called in cyclic OB, or array indexing off by one | Insert FB_ScanAlarms into OB1; verify 1 TO 200 matches array bounds |
| Compil error "Array index must be constant" on S7-1200 FW 4.2 | SCL runtime bound on optimized DBs | Switch DB_Alarms to non-optimized or upgrade to FW 4.5 |
__POP_COUNT not recognized |
FW older than 4.4 or SCL intrinsic disabled | Replace with the FOR-loop counter (Method 3) |
| Aggregator updates but HMI does not refresh | HMI connection to the DB lost, or polling cycle too long | Confirm the HMI connection in TIA → Online → Accessible nodes; set the alarm area to area-pointer based update |
| Edge bit latches permanently | Method 4 missing the Prev static variable |
Ensure the previous value variable is in the FB static area, not the aggregator DB |
| Count drifts (199, 201, 198) over time | Race condition with HMI acknowledging and application clearing the same bit | Centralize Active set/clear in a single FB and route the HMI ack to that FB |
14. Field-Proven Caveats
- Keep the scan FB on the same priority / same OB as the alarm-setting logic; mixing OB1 (alarm set) and OB35 (scan) on a 1 s OB can produce a one-second visualization lag and confuse operators.
- Reserve a "heartbeat" alarm entry (e.g. index 200) that the application pulses every cycle; the aggregator count must never stay at zero while the PLC is in RUN. This catches broken scan wiring on the commissioning screen.
- On S7-1500 with a large plant (>5 000 alarms), prefer the OR-fold pattern plus the
__POP_COUNTintrinsic; the FOR-loop variant can exceed 2 ms on a 1511-1 PN and is no longer negligible. - When migrating projects to TIA V18 with FW 4.6, retest the early-exit pattern; some compiler versions optimized the
FORdifferently and the cycle-time estimate can shift.
FAQ
Does Siemens ship a library FB that returns "at least one alarm is active in my DB"?
No. The S7-1200 / S7-1500 standard library has no generic function block that introspects a user-defined alarm DB. The detection must be written in SCL, LAD, or STL against the UDT array using a FOR-loop, OR-fold, or counter pattern.
What is the fastest way to detect any active alarm on S7-1200?
Use a word-wise OR-fold over a flat alarm bit array, then __POP_COUNT for the count. On CPU 1214C FW 4.5, a 200-alarm array scans in < 0.1 ms; on FW 4.4 or earlier replace the intrinsic with a counter loop.
Can the SCL FOR-loop work on optimized DBs?
Yes on S7-1200 FW 4.5 and S7-1500. The runtime evaluates the index variable, OR-folds the boolean, and accepts the array access in optimized access blocks. On FW 4.2 switch the DB to non-optimized access, or use an STL indirect addressing variant.
How do I get a horn pulse on the first new alarm?
Add an edge-detection layer (Method 4) that compares the current AnyAlarmActive with its previous-cycle value. The rising-edge pulse drives a one-shot TP timer that latches an output; the latch is released by an HMI acknowledge button gated on the AllClear bit.
How do I scan several alarm DBs in one project?
Wrap the scan in a reusable FB (FB_AreaAlarmScan) with a VARIANT-typed VAR_IN_OUT array parameter. Call one instance per area DB. The per-area instance DB then becomes the aggregator for that area, and a master OR in the global DB reduces them to a plant-wide AnyAlarmActive.