Accessing Bytes from Symbolic Bits in S7-1200 TIA Portal

David Krause17 min read
S7-1200SiemensTechnical Reference
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

Overview

The S7-1200 CPU family and its TIA Portal engineering environment treat the TEMP section of an FB/FC block differently from the STATIC section of a function block or the global memory of a data block. When eight BOOL tags are declared as TEMP locals in an FC and addressed symbolically, the compiler will not synthesize a packed byte operand that can be picked up by an absolute address such as %LB0, L0.0, LB0, or LW0. The user-visible symptom is a compile error pointing to an illegal combination of symbolic bit declarations and absolute byte access.

This reference explains why the absolute access fails, what the TIA Portal compiler accepts in its place, and how to reconstruct a byte for comparison, masking, or bit-pattern distribution when the source data lives in TEMP symbols. The same logic also applies to the S7-1500 and to the WinAC RTX variants of TIA Portal, since the address-resolution rules are shared across the S7-1200/1500 generation. The principles are documented in the Siemens S7-1200 manual collection and in Siemens Knowledge Base article 57374718.

If the goal is to compare a packed physical input such as IB0 against a mask, use absolute addressing on the input directly and do not re-declare it as TEMP. The constraints described below only apply to symbolic local data declared in TEMP.

Symbolic Versus Absolute Addressing in S7-1200

S7-1200 firmware V4.0 and later (and all S7-1500 firmware releases) make symbolic addressing the preferred and often mandatory scheme. According to the S7-1200 manual collection entry "Using absolute addressing to access CPU data", absolute addresses are allowed only on the process image of inputs (I), outputs (Q, also QP on S7-1500), and bit memory (M). All other user-defined tags must be addressed symbolically, and that includes every tag declared inside a DB, the STAT section of an FB, the IN/OUT/IN_OUT/TEMP interface of any block, and the local TEMP tags of an FC.

Storage Area Symbolic Allowed? Absolute Allowed? Bit Slice from Byte Symbol?
Process image inputs (I / IB / IW / ID) Yes (via I/O tag table) Yes Yes, e.g. "InputByte".X0 when using a symbolic I/O tag
Process image outputs (Q / QB / QW / QD) Yes Yes Yes
Bit memory (M / MB / MW / MD) Yes (via PLC tag table) Yes Yes
Global DB static section Yes (mandatory) No (only with optimized access disabled) Yes, "DB_Name".Tag.X0
FB instance STATIC Yes (mandatory) No (optimized only) Yes
FC / FB TEMP locals Yes (mandatory) No Yes for slice access; no for reverse byte assembly

The error message generated when a developer writes %LB0 (or the older L0.0 notation) into a network that also references a symbolic TEMP tag typically reads:

The address "%LB0" is invalid.
Absolute addressing is not permitted on temporary local data.

or, depending on TIA Portal version and language locale:

Fehler: Auf TEMP-Variablen darf nicht absolut zugegriffen werden.
The symbolic address "LocalByte" could not be resolved to an absolute address.

Both messages describe the same root cause: the compiler cannot allocate a fixed absolute offset for a TEMP variable because the stack frame is rebuilt on every block call and shared with the operating system and other invoked blocks.

Why %LB0 Fails in FC Temporary Data

Three structural reasons make absolute access to TEMP illegal in S7-1200/1500:

  1. Optimized block compilation. TIA Portal V11 SP2 and later compile every new FB/FC with optimized block access by default. The compiler is free to reorder and pack local data into register spill slots, internal scratch, or non-contiguous stack locations. Because no fixed byte offset exists in the compiled image, the %LB0 address has nothing to bind to.
  2. Stack reuse. The S7-1200/1500 runtime allocates the TEMP frame from a shared stack pool. A second invocation of the same FC (or a different FC) can overwrite the same memory the moment the current call returns. Any absolute pointer would outlive the call frame and corrupt the next call. Siemens documents this constraint in section 4.3.6 "Use of temporary local data" of Hans Berger's S7-1200/1500 reference, cited by Siemens support.
  3. Typing mismatch. Eight separate BOOL tags are not guaranteed by the compiler to occupy eight consecutive bits. They may be aligned to byte or word boundaries for performance, separated by padding for diagnostic breakpoints, or assigned to distinct internal registers if the optimizer detects no cross-tag interaction. A symbolic BYTE constructed from eight BOOLs is therefore not guaranteed to be bit-equivalent to %LB0.

Even in the legacy S7-300/400 environment, where absolute addressing of L stack locals is legal in STL, the eight BOOL symbols still do not form a contiguous byte unless they are deliberately declared as a STRUCT, BYTE, or WORD with bit slice notation.

Reading Bits Out of a Symbolic Byte

The reverse direction - extracting individual bits from a symbolic BYTE, WORD, DWORD, or LWORD - is fully supported. The syntax is the bit-slice operator .Xn for single-bit access, .Xa..b for range slicing in SCL, and standard bit logic in LAD/FBD.

// SCL example: byte declared as STATIC or TEMP
#StatusByte : BYTE;    // packed 8 status flags

IF "StatusByte".X0 THEN          // bit 0 (LSB)
    // motor running
END_IF;
IF "StatusByte".X7 THEN          // bit 7 (MSB)
    // fault active
END_IF;

#MaskWord.%X0 := TRUE;           // set bit 0
#MaskWord.%X4 := FALSE;          // clear bit 4
#UpperNibble := #StatusWord.%X8..11;

The same syntax works in LAD/FBD using the == comparator and the bit slice on the operand box, or in STL using the legacy DB1.DBX0.0 style for non-optimized blocks. The bit-slice operator is documented under the S7-1200 manual collection entry Using absolute addressing to access CPU data and in Siemens FAQ 57374718.

Workaround 1: Explicit Set / Reset Coils

The simplest workaround, valid in any TIA Portal version from V11 SP2 onward and across all S7-1200 firmware releases, is to abandon the byte-as-comparison-target idea and instead compare each individual BOOL symbolic tag. Eight booleans become eight explicit coil networks.

// LAD / FBD approach
// All tags are TEMP BOOL, defined symbolically in the FC interface.

      "MyFC".FaultOverload      // BOOL
      "MyFC".FaultEarth          // BOOL
      "MyFC".FaultOverTemp       // BOOL
      "MyFC".FaultPhaseLoss      // BOOL
      "MyFC".FaultCommLoss       // BOOL
      "MyFC".WarningService      // BOOL
      "MyFC".WarningReset        // BOOL
      "MyFC".WarningHold         // BOOL

// Compare the desired pattern by chaining the comparisons:
#PatternMatch := "MyFC".FaultOverload      // bit 0
            AND NOT "MyFC".FaultEarth      // bit 1 must be 0
            AND "MyFC".FaultOverTemp       // bit 2
            AND "MyFC".FaultPhaseLoss      // bit 3
            AND NOT "MyFC".FaultCommLoss   // bit 4 must be 0
            AND "MyFC".WarningService      // bit 5
            AND NOT "MyFC".WarningReset    // bit 6 must be 0
            AND "MyFC".WarningHold;        // bit 7

This method is zero-allocation, generates compact STL, and survives every firmware upgrade. The drawback is verbose code when more than a few flags must be matched. Use it for short pattern tests or safety-relevant comparisons where readability is more important than density.

Workaround 2: Build a Byte with Logic Operations

When the eight booleans represent independent flags whose combined bit pattern must be tested (for example, a GSD-defined status word that arrives already split into BOOL tags by an I-device PN slice), reconstruct a BYTE explicitly using AND, OR, and shift primitives. The resulting #StatusByte is still a TEMP symbol, but it now has a known value and can be used in ==, <>, masking, or further bit slicing.

// SCL: rebuild a packed status byte from 8 symbolic BOOL TEMPs
#StatusByte := 0;                                // clear accumulator
IF "MyFC".FaultOverload    THEN #StatusByte.%X0 := TRUE; END_IF;
IF "MyFC".FaultEarth        THEN #StatusByte.%X1 := TRUE; END_IF;
IF "MyFC".FaultOverTemp     THEN #StatusByte.%X2 := TRUE; END_IF;
IF "MyFC".FaultPhaseLoss    THEN #StatusByte.%X3 := TRUE; END_IF;
IF "MyFC".FaultCommLoss     THEN #StatusByte.%X4 := TRUE; END_IF;
IF "MyFC".WarningService    THEN #StatusByte.%X5 := TRUE; END_IF;
IF "MyFC".WarningReset      THEN #StatusByte.%X6 := TRUE; END_IF;
IF "MyFC".WarningHold       THEN #StatusByte.%X7 := TRUE; END_IF;

// Now use the byte:
IF #StatusByte = 16#A5 THEN
    // pattern 1010 0101 matched
END_IF;

The intermediate #StatusByte is itself a TEMP, so it is not accessible via %LB0. That is acceptable because all subsequent code within the same FC call uses #StatusByte symbolically. The advantage over the explicit-coil method is that any further byte-wide operation (mask, compare, checksum, CRC, transmission over PUT/GET) becomes trivial.

Workaround 3: Promote to a Global Data Block

If the byte representation is needed beyond the lifetime of one FC call, promote the eight BOOLs to a global DB (or to the STAT section of an FB instance). Both targets support direct symbolic bit-slice and byte/word/dword access without any compilation error.

// Global DB "StatusDB"
DATA_BLOCK "StatusDB"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
NON_RETAIN
  STRUCT
    FaultOverload   : BOOL;   // bit 0
    FaultEarth      : BOOL;   // bit 1
    FaultOverTemp   : BOOL;   // bit 2
    FaultPhaseLoss  : BOOL;   // bit 3
    FaultCommLoss   : BOOL;   // bit 4
    WarningService  : BOOL;   // bit 5
    WarningReset    : BOOL;   // bit 6
    WarningHold     : BOOL;   // bit 7
    StatusByte      : BYTE;   // consolidated mirror
    StatusWord      : WORD;   // consolidated 16-bit mirror
  END_STRUCT;
END_DATA_BLOCK

Inside an FC, write to the DB explicitly:

"StatusDB".FaultOverload := #FaultOverload;
"StatusDB".FaultEarth    := #FaultEarth;
// ... and so on for all eight bits

"StatusDB".StatusByte.%X0 := "StatusDB".FaultOverload;
"StatusDB".StatusByte.%X1 := "StatusDB".FaultEarth;
"StatusDB".StatusByte.%X2 := "StatusDB".FaultOverTemp;
"StatusDB".StatusByte.%X3 := "StatusDB".FaultPhaseLoss;
"StatusDB".StatusByte.%X4 := "StatusDB".FaultCommLoss;
"StatusDB".StatusByte.%X5 := "StatusDB".WarningService;
"StatusDB".StatusByte.%X6 := "StatusDB".WarningReset;
"StatusDB".StatusByte.%X7 := "StatusDB".WarningHold;

IF "StatusDB".StatusByte = 16#A5 THEN
    // pattern matched, now visible to HMI, OPC UA, etc.
END_IF;

This approach also unlocks the value to be displayed on a WinCC Unified faceplate, published through an OPC UA server, exchanged with a remote partner via PUT/GET, or archived into a data log. None of that is possible with a TEMP-only design.

If the global DB holds safety-related data, confirm with TÜV that the consolidation step does not violate the safety signature. On F-CPU variants (CPU 1214FC, CPU 1515F, etc.), use the F-DB provided by the safety program and never mirror F-tag values into a standard DB inside the standard program.

Workaround 4: Use a STRUCT with Bit Packing

The S7-1200/1500 compiler guarantees bit-packed layout for members of a STRUCT when the non-optimized access mode is disabled or when the {S7_Optimized_Access := 'FALSE'} attribute is applied. Inside an FB instance with disabled optimization, the STRUCT occupies a fixed byte offset, and the symbolic bit-slice works as expected.

// FB with non-optimized access
FUNCTION_BLOCK "FaultAggregator"
VERSION : 1.0
{ S7_Optimized_Access := 'FALSE' }
VAR_INPUT
    iRawBits : BYTE;            // packed input
END_VAR
VAR_OUTPUT
    qFault   : BOOL;
    qWarning : BOOL;
END_VAR
VAR
    sFlags : STRUCT
        b0_FaultOverload  : BOOL;
        b1_FaultEarth     : BOOL;
        b2_FaultOverTemp  : BOOL;
        b3_FaultPhaseLoss : BOOL;
        b4_FaultCommLoss  : BOOL;
        b5_WarningService : BOOL;
        b6_WarningReset   : BOOL;
        b7_WarningHold    : BOOL;
    END_STRUCT;
END_VAR
BEGIN
    sFlags.b0_FaultOverload  := iRawBits.%X0;
    sFlags.b1_FaultEarth     := iRawBits.%X1;
    sFlags.b2_FaultOverTemp  := iRawBits.%X2;
    sFlags.b3_FaultPhaseLoss := iRawBits.%X3;
    sFlags.b4_FaultCommLoss  := iRawBits.%X4;
    sFlags.b5_WarningService := iRawBits.%X5;
    sFlags.b6_WarningReset   := iRawBits.%X6;
    sFlags.b7_WarningHold    := iRawBits.%X7;

    qFault   := sFlags.b0_FaultOverload
             OR sFlags.b1_FaultEarth
             OR sFlags.b2_FaultOverTemp
             OR sFlags.b3_FaultPhaseLoss
             OR sFlags.b4_FaultCommLoss;
    qWarning := sFlags.b5_WarningService
             OR sFlags.b6_WarningReset
             OR sFlags.b7_WarningHold;
END_FUNCTION_BLOCK

With non-optimized access, the sFlags structure starts at the instance DB's data area at a known offset, and the individual BOOL members are guaranteed to occupy bits 0-7 of the first byte. The instance DB can then be referenced symbolically and the byte as a whole read or written using "DB_Instance".sFlags when the tag is typed as a BYTE alias - though the cleaner pattern is to declare an explicit BYTES_MIRROR tag in the same VAR section and assign it.

Non-optimized blocks on S7-1500 are restricted to firmware V1.8 and earlier conventions in some scenarios. Verify that the panel/HMI driver still expects non-optimized layout if you switch from optimized to non-optimized access purely to enable byte-packing.

Workaround 5: POKE / PEEK (Legacy STL Only)

The POKE and PEEK instructions allow direct absolute access to any memory area, including the local stack, by specifying a byte offset. They are available in STL on S7-300/400 and remain available in S7-1200/1500 STL source sections.

// STL: read the local stack as a byte
LAR1  P##LocalBytePointer        // points to TEMP offset
L     B [AR1, P#0.0]             // PEEK into the stack
T     #SnapshotByte              // store as TEMP BYTE

This is not recommended on S7-1200/1500 because the optimizer may have moved the source value to a register or a different stack slot. The read may return stale or random data. Use this only on S7-300/400 where the stack layout is fully deterministic, and document the offset with a stack diagram generated from the STL source.

Workaround 6: POKE_BLK with a Marker Byte

A safer alternative is to declare a dedicated BYTE in bit memory M (or in a global DB) at a known offset, copy the eight symbolic BOOLs into it, and then use the marker byte for comparison. This guarantees an absolute address without sacrificing the symbolic source of truth.

// SCL
IF "MyFC".FaultOverload THEN
    %MB100.%X0 := TRUE;
ELSE
    %MB100.%X0 := FALSE;
END_IF;
// ... repeat for bits 1-7 ...

IF %MB100 = 16#A5 THEN
    // pattern match
END_IF;

This is acceptable for legacy migration projects that already use MB areas, but the modern best practice is to keep everything symbolic and use a global DB BYTE tag such as "StatusDB".StatusByte instead.

Comparison Matrix: Workarounds at a Glance

Method Firmware Requirement Allows Byte Compare? HMI / OPC UA Visible? Code Density Safety Compatible?
Explicit Set/Reset coils Any Indirect (8 comparators) Per-bit only Low Yes, F-capable
Reconstructed TEMP BYTE V11 SP2+ (S7-1200/1500) Yes No (TEMP lifetime) Medium Yes, F-capable
Global DB mirror Any Yes Yes Medium Standard program only
STRUCT with non-optimized FB Any Yes Yes High Standard program only
POKE/PEEK into L stack STL only; S7-300/400 recommended Yes (deterministic only on legacy) No Low Not recommended
Marker byte %MB Any Yes Yes Medium Standard program only

Firmware and Software Compatibility

CPU Family Firmware Version TIA Portal Version Bit Slice .Xn Optimized Block Default Notes
S7-1200 (CPU 1211C - 1215C) V4.0 - V4.6 V13 SP1 - V18 Yes Yes (since V4.0) Original deployment used TIA V11 SP2 Update 5
S7-1200 G2 (CPU 1212G, 1214G, 1215G, 1217G) V5.0 V18 - V20 Yes Yes (mandatory) Optimized-only; non-optimized blocks not supported
S7-1500 (CPU 1511 - 1518) V1.8 - V3.1 V13 SP1 - V20 Yes Yes (mandatory) Non-optimized blocks only allowed for legacy reasons
ET 200SP CPU V2.5 - V3.1 V15.1 - V20 Yes Yes Same restrictions as S7-1500
S7-300/400 V3.x - V3.5 STEP 7 V5.x, TIA V13 - V16 Yes (also legacy STL L 0.0) No Absolute L access legal in STL

The S7-1200 G2 manual collection documents the optimized-only rule on the page Using absolute addressing to access CPU data. The Siemens third-party connectivity reference for symbolic tags, used by HMI/SCADA vendors such as Weintek, is summarized at the Siemens S7-1200 Symbolic Addressing Ethernet guide.

Error Codes and Compiler Diagnostics

TIA Portal Diagnostic Typical Text Cause Remediation
Error 1: Address invalid The address "%LB0" is invalid Absolute access on TEMP Use symbolic or promote to DB
Error 16#03010003 Address could not be resolved Typo or missing tag declaration Verify tag exists in interface
Error 16#03020004 Absolute addressing of TEMP not allowed Compiler rule violation Switch to symbolic or DB
Warning 16#03080012 Implicit type conversion BOOL to BYTE Comparing BOOLs with = to a BYTE literal Cast explicitly or use .X0 slicing
Warning 16#0308001A Symbolic address is used inconsistently Same name in TEMP and DB Qualify with block name or rename
Online: SF LED on, diagnostics buffer entry 16#72E2 Stop by programming error - invalid address POKE/PEEK to freed stack Reboot, remove POKE/PEEK, recompile

Verification Procedure

  1. Compile. In TIA Portal, right-click the project tree → Compile → Software (rebuild all). Confirm zero errors and zero warnings. If the previous %LB0 error is still present, clear the Compile/Archive folder under the project directory and rebuild.
  2. Download. Connect online to the CPU (CPU 1214C DC/DC/DC, firmware V4.6 in the typical deployment case). Use Download to device → Extended download to overwrite the existing program blocks.
  3. Monitor. Open the FC in the editor, right-click the tag table → Monitor all. Force the eight symbolic BOOLs to known values 0/1 using the force table. Verify that the consolidated byte shows the expected hex value in the online view.
  4. Trigger comparison. Set all eight bits to 0xA5 (binary 10100101) by toggling the symbolic booleans. Confirm that the comparator output goes TRUE within one OB1 cycle (typically 2-10 ms on a CPU 1214C).
  5. Cross-check via watch table. Add a watch table with the global DB StatusDB. Right-click the consolidated StatusByte → Monitor/modify and verify the same 0xA5 pattern when the symbolic inputs are forced.
  6. HMI/OPC UA round-trip. If the byte is published to WinCC Unified or to an OPC UA client, navigate to the tag and confirm the same value, including subscription update latency (target < 500 ms on a local PN connection).
  7. Safety check. For F-CPU programs, run the safety program printout under Safety Administration → Printout and confirm no safety tag has been mirrored into a non-safety DB.

Edge Cases and Field-Proven Caveats

  • OB1 startup behavior. On the first OB1 cycle after a CPU restart, all TEMP locals are undefined until written. If the byte comparison runs before the eight booleans are written, the reconstructed byte may contain stack garbage. Initialize #StatusByte := 0; at the top of the FC.
  • Re-entrancy. S7-1200 FCs are not re-entrant by design, but an FC used as a multi-instance or inside a cyclic OB with priority changes can have stale TEMP values if a higher-priority OB pre-empts it. Use a global DB mirror if the byte must be preserved across pre-emption.
  • Bit order confusion. S7-1200 bit numbering inside a BYTE follows big-endian bit ordering: .X0 is the LSB (value 1), .X7 is the MSB (value 128). When mapping from a vendor-specific protocol (e.g. Modbus, PROFIBUS DP-V0), confirm whether the remote device sends LSB-first or MSB-first. Some PROFIdrive status words use the inverse convention.
  • Bool to byte promotion. Implicit promotion of BOOL to BYTE in SCL sometimes generates a warning, not an error. Always cast explicitly with BOOL_TO_BYTE or use the bit-slice assignment #StatusByte.%X0 := #MyBool; to keep the diagnostic clean.
  • STEP 7 V5.x coexistence. If a legacy STEP 7 V5.x project is migrated to TIA Portal V13 or later, the original L stack references appear as compilation errors. Use TIA Portal's automatic migration of STL blocks to SCL where possible, or convert the offending networks to symbolic access manually.
  • Web server access. The S7-1200 standard web pages can display symbolic DB tags but not TEMP locals. Promote to a global DB if the byte must be visible on the web API.
  • Data log impact. A consolidated byte writes one entry per scan instead of eight. This reduces data log size and improves trend resolution in WinCC Professional.

Best-Practice Recommendation

For new development on TIA Portal V16 and later, the recommended pattern is:

  1. Declare the eight booleans in a global DB with optimized access enabled.
  2. Use the global DB tags as the source of truth, accessible by every block symbolically.
  3. Define a parallel BYTE tag in the same DB that is written from the eight booleans at the end of the originating FB.
  4. Perform all byte-wide operations on the BYTE tag. All bit-wide operations continue to use the individual booleans.
  5. Disable the bit mirror if HMI/OPC UA traffic is heavy, and rely on the consolidated byte to reduce the number of subscriptions.

This pattern satisfies the symbolic-only rule of TIA Portal, scales to WinCC Unified and OPC UA, and survives any firmware upgrade from V4.0 through the latest S7-1200 G2 firmware V5.0.

Frequently Asked Questions

Why does the S7-1200 compiler reject %LB0 inside an FC TEMP area?

Optimized block compilation on S7-1200/1500 places TEMP locals in register spill slots whose addresses can change every scan. The compiler therefore rejects absolute addressing such as %LB0, L0.0, LB0, or LW0 because no fixed byte offset exists. See Siemens Knowledge Base 57374718 for the full rule set.

Can I read a single bit out of a symbolic BYTE in TIA Portal?

Yes. Use the bit-slice syntax "MyTag".X0 through .X7 in SCL, or place the symbolic tag directly on a coil/contact in LAD/FBD. The same syntax applies to WORD (%X0..15), DWORD (%X0..31), and LWORD. The behavior is documented in the S7-1200 G2 manual collection.

Is it possible to use a marker byte (%MB100) instead of a global DB?

Yes. Declare %MB100 (or any unused MB), write the eight symbolic booleans into %MB100.%X0..%X7, and use %MB100 for byte-wide comparison. This is acceptable for legacy migration projects but is considered outdated on TIA Portal V16+ because it bypasses symbolic addressing and the OPC UA server ignores M area tags by default.

Does the same rule apply to FB STATIC tags and to global DB tags?

No. STATIC FB tags and global DB tags are stored at fixed offsets in the instance or global DB and can be addressed both symbolically and absolutely (when optimized access is disabled). Only the TEMP area of an FC/FB forbids absolute addressing in TIA Portal V11 SP2 and later.

What is the safest way to mirror safety flags into a status byte for HMI display?

Use the safety program's own F-DB to expose the F-tag values through standard tag mapping in the safety administration editor. Do not write to a standard DB from inside the standard program if the source is a safety tag; the safety signature will not cover the standard DB and TÜV certification may be invalidated. Use a standard data record only for non-safety status bits.

Back to blog