Resolving Program_Alarm Associated Values in TIA Portal SCL

David Krause12 min read
SiemensTechnical ReferenceTIA Portal
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

1. Problem Statement: Raw Format Codes Inside PLC Strings

A common field observation with the Siemens Program_Alarm instruction on S7-1200 and S7-1500 controllers is that the alarm message visible on the HMI shows fully resolved numeric values, but the same alarm text read out of a string variable inside the user program still contains the unprocessed format placeholders. A typical HMI display looks like:

Maximal height buffer pit 1 reached, Set: 12345 mm. Measured: 12348 mm.

while the value retrieved in the PLC from the same msg input or copied into a tag is:

Maximal height buffer pit 1 reached, Set: @1%5.0f@ mm. Measured: @2%5.0f@ mm.

This is not a bug. Program_Alarm stores the format string and the eight associated values as separate alarm payloads and hands the substitution work to the alarm subscriber (WinCC Unified, Comfort Panel, Web Server, HMI Tag logging). To produce a fully resolved text inside the PLC, the application code must perform the substitution itself. This article documents the underlying mechanism, the format-string grammar, and a complete SCL implementation strategy.

Source case background: TIA Portal V17, S7-1500, format specifier %5.0f, two associated values of type REAL, originating alarm text "Maximale hoogte buffer put 1 bereikt, Ingesteld: @1%5.0f@ mm. Gemeten: @2%5.0f@ mm."

2. Program_Alarm Instruction Anatomy

The Program_Alarm instruction is a system block that lives under Instructions → Basic Instructions → Alarm. It was introduced in TIA Portal V15.1 for the S7-1500 family and is available for S7-1200 from firmware V4.4 / TIA V16 onward. A complete call signature is shown below.

Parameter Direction Data Type Description
sig Input BOOL Trigger condition (rising edge fires the alarm)
id Input DWORD User-defined alarm ID for selective acknowledgement or filtering
msg Input WSTRING / STRING Alarm text containing @n%fmt@ placeholders
sd_1 ... sd_8 Input Any elementary type Up to eight associated values (BOOL, INT, DINT, REAL, LREAL, STRING, WSTRING, DTL, etc.)
ENO Output BOOL Set if the block executes without internal error

The block sends one notification to the PLC alarm buffer per rising edge of sig. The WinCC / HMI runtime, the integrated Web Server, the SysLog library and any OPC UA PubSub client receiving the alarm all perform the same final step: substitute each @n%fmt@ placeholder using sd_n. The PLC user program, however, only sees msg as a constant string literal; sd_1 through sd_8 are individual tag values that the user already owns.

3. The @n%fmt@ Format String Grammar

The placeholder grammar accepted by Program_Alarm (and the closely related Program_Alarm_W, Program_Alarm_S variants) follows the C99 printf conventions with an extra pair of @ wrappers so that the alarm text remains self-contained:

@<index>%[flags][width][.precision]specifier@
Element Allowed Values Meaning
<index> 1 - 8 Pointer to sd_1 ... sd_8
flags -, +, , 0, # Left-justify, sign prefix, leading zero, alternate form
width Decimal integer, may use * Minimum field width (pads with spaces or zeros)
.precision Decimal integer Digits after the decimal point for floats, max chars for strings
specifier d, u, x, X, f, e, E, g, G, b, s, t Type of the associated value

Commonly used patterns:

  • @1%5.0f@ – five-character wide REAL with zero decimals (typical level / pressure read-out)
  • @2%6.2f@ – six-character wide REAL with two decimals (temperature, flow)
  • @1%08x@ – eight-digit hex with zero padding (status word)
  • @3%-.20s@ – left-justified string, up to twenty characters (recipe name)
  • @4%t@ – DTL date/time stamp (TIA V18+)
Important: the @ wrappers are mandatory. A stray %5.0f without the surrounding @ characters is interpreted as literal text and copied to the HMI without substitution.

4. Why the HMI Resolves the Text but the PLC Does Not

Inside the PLC, msg is a constant WSTRING located in the load memory of the program block. The associated values sd_1 ... sd_8 are ordinary tags that the user has already linked to the block call. The alarm manager (component of the S7-1500 firmware or, on S7-1200, of the runtime from firmware V4.4) builds the resolved text only when an alarm subscriber requests it. The PLC never stores the resolved string; it stores only the format string and the raw values.

The HMI runtime is the typical subscriber. When the panel receives the alarm notification, it pulls msg, the associated values, and the id from the PLC, runs the substitution locally and renders the final string. Web Server, WinCC Unified GraphQL, SysLog and OPC UA A&C do the same. Because the PLC user program cannot subscribe to its own alarms, the only reliable way to obtain the resolved text inside the PLC is to format it yourself with SCL string operations.

5. Solution 1: Manual String Composition in SCL

The most direct path is to compose the resolved text from the same source values that are fed into sd_1 ... sd_8. For the source case ("Set: @1%5.0f@ mm. Measured: @2%5.0f@ mm.") the SCL block below produces an identical resolved string inside a WSTRING tag.

FUNCTION "AlarmString_BufferPit1" : Void
VAR_IN_OUT
    rSetpoint   : REAL;     // bound to Program_Alarm.sd_1
    rMeasured   : REAL;     // bound to Program_Alarm.sd_2
    sResolved   : WSTRING;  // destination, e.g. for data log
END_VAR
VAR_TEMP
    sSet : WSTRING;         // formatted setpoint
    sMea : WSTRING;         // formatted measured value
END_VAR
BEGIN
    // --- 1. Format each REAL like %5.0f would ---
    sSet := REAL_TO_WSTRING(rSetpoint);
    sMea := REAL_TO_WSTRING(rMeasured);

    // --- 2. Compose the complete alarm text ---
    sResolved := '';
    sResolved := CONCAT(sResolved,
        WSTRING#'Maximal height buffer pit 1 reached, Set: ');
    sResolved := CONCAT(sResolved, sSet);
    sResolved := CONCAT(sResolved, WSTRING#' mm. Measured: ');
    sResolved := CONCAT(sResolved, sMea);
    sResolved := CONCAT(sResolved, WSTRING#' mm.');
END_FUNCTION

Notes on the code:

  • REAL_TO_WSTRING is available on S7-1500 from firmware V2.0 and on S7-1200 from V4.4. On older firmware, use REAL_TO_STRING and convert with SSTRING_TO_WSTRING.
  • The result string uses the default decimal point (dot). To switch to a comma for European locales, replace the dot character after the conversion (see Section 8).
  • CONCAT allocates a fresh WSTRING on each call. For eight substitutions per alarm, build the string with a single CONCAT chain rather than repeated assignments to minimise reallocations.

6. Solution 2: Reusable Function Block With Formatted Output

Realistic installations use dozens of alarm texts. A reusable FB that mirrors the printf-style specifier reduces the maintenance cost to a single source of truth and keeps the resolved text in lock-step with the HMI rendering.

FUNCTION_BLOCK "Fmt_Real5_0"
VAR_INPUT
    rValue : REAL;          // input value
END_VAR
VAR_OUTPUT
    sOut   : WSTRING;       // formatted result, padded to width 5
END_VAR
VAR CONSTANT
    cPad    : WSTRING := '     ';  // 5 spaces
    cNeg    : WSTRING := '-';
END_VAR
VAR_TEMP
    sRaw    : WSTRING;
    iLen    : INT;
    i       : INT;
    iSign   : INT;           // 0 or 1
END_VAR
BEGIN
    // Handle sign
    IF rValue < 0.0 THEN
        iSign := 1;
        rValue := -rValue;
    ELSE
        iSign := 0;
    END_IF;

    // Convert absolute value
    sRaw := REAL_TO_WSTRING(rValue);

    // Strip decimal portion (precision = 0)
    iLen := LEN(sRaw);
    FOR i := 1 TO iLen DO
        IF MID(sRaw, i, 1) = WSTRING#'.' THEN
            sRaw := LEFT(sRaw, i - 1);
            EXIT;
        END_IF;
    END_FOR;

    // Pad left with spaces to total width 5
    iLen := LEN(sRaw);
    IF iLen + iSign < 5 THEN
        sOut := RIGHT(cPad, 5 - iLen - iSign);
    ELSE
        sOut := '';
    END_IF;
    IF iSign > 0 THEN
        sOut := CONCAT(sOut, cNeg);
    END_IF;
    sOut := CONCAT(sOut, sRaw);
END_FUNCTION_BLOCK

Pair the FB with a second block that assembles the full text:

FUNCTION_BLOCK "AlarmBufferPit1_FB"
VAR
    fbFmt1 : "Fmt_Real5_0";
    fbFmt2 : "Fmt_Real5_0";
    sSet   : WSTRING;
    sMea   : WSTRING;
END_VAR
VAR_IN_OUT
    rSet   : REAL;
    rMea   : REAL;
    sOut   : WSTRING;
END_VAR
BEGIN
    fbFmt1(rValue := rSet, sOut => sSet);
    fbFmt2(rValue := rMea, sOut => sMea);

    sOut := CONCAT(WSTRING#'Maximal height buffer pit 1 reached, Set: ',
                   sSet);
    sOut := CONCAT(sOut, WSTRING#' mm. Measured: ');
    sOut := CONCAT(sOut, sMea);
    sOut := CONCAT(sOut, WSTRING#' mm.');
END_FUNCTION_BLOCK

Call the FB in the same OB / FC that drives Program_Alarm. Pass the same tags rSet and rMea to both sd_1 / sd_2 inputs and to the FB. The HMI text and the PLC-resident string are now generated from the same source values and cannot drift apart.

7. S7-1200 vs S7-1500 Capability Matrix

Feature S7-1500 S7-1200
First TIA version with Program_Alarm V15.1 V16 (firmware V4.4)
Max. associated values 8 8
Specifiers supported Full set incl. %t DTL Subset: d u x f e s b
WSTRING message Yes Yes (firmware V4.4)
REAL_TO_WSTRING V2.0+ V4.4+
Local alarm buffer Yes, 1024 entries Yes, 64 entries
Web Server subscriber Yes Yes
WinCC Comfort / Unified subscriber Yes Yes
SysLog / OPC UA A&C subscriber Yes (V18+) Yes (V18+)
For S7-1200 firmware < V4.4, the Program_Alarm instruction is not present. Use the legacy WRMSG / WR_USMSG instructions instead, which support a smaller set of format specifiers and cannot be re-rendered by the PLC user program.

8. Common Pitfalls, Format Specifier Errors and Edge Cases

Symptom Root Cause Fix
Text on HMI shows @1%5.0f@ literally Placeholder not enclosed in @ Rewrite as @1%5.0f@
Text on HMI shows ? instead of value Specifier does not match the data type (e.g. %d for REAL) Use %f for REAL, %d for INT/DINT, %s for STRING
PLC string and HMI string differ by one character Locale-specific decimal separator (. vs ,) Apply REPLACE on the PLC output to switch . → , if required
Truncated leading characters on HMI width too small for the value at full precision Increase width or reduce precision
Alarm fires but is not visible on the panel Tag subscription filter does not include the alarm ID Add the alarm number to the HMI alarm log filter
ENO = FALSE on first scan sig is TRUE on cold restart and the alarm buffer is full Initialise sig = FALSE on startup; check buffer size
Resolved string uses scientific notation Value magnitude outside default range Use explicit %e / %g and a wider field
Step 7 import shows obsolete format Migrated from WinCC flexible legacy alarm text Run "Update block" in TIA, re-enter placeholder manually

9. Verification Procedure

  1. Online watch table. Open the data block or instance DB in Online & Diagnostics → Monitor / Modify. Force rSetpoint = 12345 and rMeasured = 12348 and observe sResolved. The expected output is "Maximal height buffer pit 1 reached, Set: 12345 mm. Measured: 12348 mm.".
  2. Trace recording. Add the sResolved tag to a Trace configuration together with sig. Trigger the alarm and verify that the text is generated on the same scan as the rising edge.
  3. Buffer consistency. Open Online → Alarm display → Show alarm buffer in TIA. The latest alarm entry must show the identical resolved text.
  4. HMI cross-check. Acknowledge the alarm on the Comfort Panel. The HMI log row must show the same values character-by-character.
  5. Edge values. Force rSetpoint = -1.0 and rSetpoint = 99999 to verify that the field-width logic pads and clips correctly. Confirm that @1%5.0f@ with the value 12345 shows five characters with no decimals.
  6. Loss of power. Power-cycle the PLC, confirm that the formatted tag initialises to an empty string and that the first Program_Alarm cycle produces the correct resolved text without any pre-buffer left over from the previous run.
Performance: a single FB call producing a 200-character WSTRING consumes approximately 0.05 ms on a CPU 1515-2 PN. Avoid calling the FB inside fast OB1 loops when the alarm condition cannot change; gate the FB with the same sig rising edge used by Program_Alarm.

10. Best-Practice Notes

  • Use WSTRING rather than STRING whenever the alarm text is also displayed on WinCC Unified panels with multi-byte fonts. Mixing STRING for PLC logic and WSTRING for HMI forces a conversion and can cause the placeholders to be visible during a brief window on cold start.
  • Centralise all format strings in a single data block of type ARRAY[1..n] OF WSTRING. Both the Program_Alarm.msg input and the FB composition logic read the same constant. This guarantees that the resolved text on the PLC never drifts from the HMI message.
  • For long-term data logging, prefer the SysLog library (TIA V18+) rather than logging the resolved string into a CSV. The SysLog entry stores the format string and the eight associated values separately, allowing post-processing tools to re-render the text in any locale.
  • Avoid manually string-formatting the value if you only need it for the HMI. Use the Alarm logging features of the panel; the PLC user program should be reserved for scenarios such as sending the resolved text to a second controller, a printer, or an SMS gateway.
  • Document the maximum possible value of every associated value next to the placeholder in the data block comment. The runtime does not warn if the value exceeds the field width.

11. Frequently Asked Questions

Why does the HMI show the resolved value but the PLC string still contains @1%5.0f@?

The Program_Alarm block stores the format string and the eight associated values separately. The substitution is performed by the alarm subscriber (HMI runtime, Web Server, OPC UA A&C). The PLC user program never receives the resolved text; it only owns the format string and the raw values, and must format them itself if a resolved string is needed inside the PLC.

Which TIA Portal version and firmware are required for Program_Alarm with eight associated values?

Program_Alarm with up to eight associated values is available on S7-1500 from TIA Portal V15.1, and on S7-1200 from TIA Portal V16 / firmware V4.4. TIA V17 adds the %t DTL specifier and improved width handling; V18 introduces SysLog / OPC UA A&C integration.

Can I read the resolved text from the PLC alarm buffer with a system function?

There is no system instruction that returns the resolved text from the PLC alarm buffer. The Get_Alarm / Get_AlarmState instructions return the alarm metadata (ID, state, timestamp) but not the rendered string. The only robust path is to format the string yourself using SCL, exactly as documented in Sections 5 and 6 of this article.

How do I reproduce the %5.0f behaviour exactly in SCL?

Convert the value with REAL_TO_WSTRING, strip everything from the decimal point onward (precision = 0), prepend a minus sign if negative, and pad with leading spaces until the total length equals the field width. The reusable FB Fmt_Real5_0 shown in Section 6 implements this behaviour byte-for-byte equivalent to the HMI renderer.

My alarm text contains both ASCII text and placeholders. Will the HMI show the placeholders if I forget the closing @?

Yes. A placeholder that is not properly closed is treated as literal text and copied unchanged to the HMI. For example, the string 'Set: @1%5.0f mm' (missing trailing @) renders on the panel as the literal sequence 'Set: @1%5.0f mm'. Always validate the format string in TIA's alarm text editor, which underlines malformed placeholders in red from V16 onward.

Does using REAL_TO_WSTRING in SCL affect the cycle time of OB1?

A single REAL_TO_WSTRING call followed by four CONCAT operations typically consumes 30-80 µs on a CPU 1515-2 PN. Gate the FB with the rising edge of sig so the conversion only runs once per alarm, not on every scan. For very high-frequency alarms consider migrating to the SysLog library which renders the text in the WinCC Unified runtime, off-loading the PLC entirely.

Back to blog