S7-1200 Active Alarm Detection in TIA Portal V17: UDT DB Scan

David Krause12 min read
S7-1200SiemensTutorial / 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

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:

  1. Define a single UDT "UDT_Alarm" that contains a Boolean Active bit (and typically Ack, Priority, Tag, Timestamp).
  2. Instantiate an array DB of that UDT (e.g. "DB_Alarms".Alarm[1..200]).
  3. Add a scanning routine — in SCL, STL, or LAD — that walks the array, OR-reduces the Active bits, and writes a single AnyAlarmActive bit 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
On S7-1200 FW 4.2 and earlier the SCL compiler does not accept the 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:

  1. HMI alarm control (WinCC Comfort/Professional, Unified): enable the State column to show pending vs. active; subscribe the AnyAlarmActive bit to a status word on a header graphic. In the alarm control configuration, set the Acknowledge tag to DB_Alarms.Alarm[i].Ack so the same UDT field is written back from the HMI.
  2. 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.
  3. SCADA / OPC UA dispatcher: publish AnyAlarmActive, AlarmCount, and HighestPrio as OPC UA nodes. The companion server exposes the entire DB_Alarms array 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

  1. Static state: with no alarms forced, DB_Aggregate.AnyAlarmActive := FALSE, AlarmCount := 0, HighestPrio := 0. Verify in the watch table.
  2. Single forced alarm: set DB_Alarms.Alarm[17].Active := TRUE; AnyAlarmActive must go TRUE in the same OB1 cycle the scan is called. Reset to FALSE one cycle after clearing.
  3. Bulk stress: force all 200 alarms true; cycle time on CPU 1214C must stay under 5 ms; AlarmCount must read 200.
  4. 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.
  5. Power cycle: with Retain enabled on DB_Alarms and on the alarm bits, perform a STOP → RUN transition; the aggregator must reflect the retained state immediately.
  6. HMI round-trip: acknowledge an alarm from the HMI; confirm DB_Alarms.Alarm[i].Ack is set, then the application logic clears Active; 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_COUNT intrinsic; 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 FOR differently 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.

Back to blog