S7-1200 Indirect Addressing in TIA Portal: PEEK_POKE vs Pointers

David Krause10 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

Indirect addressing on the SIMATIC S7-1200 lets a program evaluate an operand whose location is computed at runtime instead of fixed at compile time. The classical implementation in SCL is the PEEK and POKE instruction family, but Siemens indirect addressing using a pointer (S7-1200, S7-1500) also supports POINTER, ANY, and VARIANT references, and the platform's symbolic tag model allows indexed access into ARRAY of UDT elements. This reference compares the available methods, shows working SCL code, and outlines when each technique should be applied.

Engineering caveat: The Siemens basics of indirect addressing (S7-1200, S7-1500) knowledge base article explicitly recommends using indirect addressing only when the data layout is genuinely dynamic. Reusable code should rely on FB/FC parameters and symbolic tag access; absolute PEEK/POKE defeats the cross-reference tooling and makes hardware reconfiguration brittle.

1. PEEK and POKE on the S7-1200

The PEEK and POKE instructions are the most direct form of byte/bit-absolute indirect access in the S7-1200/S7-1500 SCL editor. They exist because the older PEEK/POKE STL pattern from S7-300/400 was re-implemented as system functions for the new CPU generation. The standard signatures available in the S7-1200 instruction set are:

Function Data type Area constant Parameters
PEEK_BOOL BOOL 16#81 inputs / 16#82 outputs / 16#83 merkers / 16#84 DB area, dbNumber, byteOffset, bitOffset
PEEK BYTE, WORD, DWORD Same area IDs area, dbNumber, byteOffset
POKE_BOOL BOOL Same area IDs area, dbNumber, byteOffset, bitOffset, value
POKE BYTE, WORD, DWORD Same area IDs area, dbNumber, byteOffset, value
PEEK_BLK / POKE_BLK ANY 16#84 DB Source/destination ANY, target DB, byte offset

The original poster's pattern reads a single bit from data block 5 and ORs it with another tag:

// Source: classical PEEK_BOOL pattern
#DBValue1 := PEEK_BOOL(area := 16#84, // DB area
                       dbNumber := 5,
                       byteOffset := #ByteAddr,
                       bitOffset := #BitAddr);

IF #DBValue1 AND #DBValue2 THEN
    %Q0.0 := TRUE;
END_IF;

Two structural problems show up immediately:

  1. The read result is a copy. It cannot be combined inline with another bit read because PEEK_BOOL returns a scalar, not a reference. The branch has to be evaluated twice (once for each side) or the result materialised in a temp.
  2. The data block number, byte offset, and bit offset are absolute. The TIA Portal cross-reference list cannot resolve a 16#84 access against a real symbol, so re-laying-out a DB (inserting a new tag in front of the target) silently shifts the address and corrupts the read.

2. POINTER and ANY Data Types

The Siemens indirect addressing using a pointer (S7-1200, S7-1500) reference describes two special pointer formats available in the new CPU family:

Pointer format Length Components Typical use
POINTER 6 bytes DB number (or 0), area ID, byte offset in bits Passing an absolute address to a function block
ANY 10 bytes Type ID, length, DB number, area ID, byte offset Block-move routines, generic copy/compare blocks
VARIANT dynamic Symbolic tag + type descriptor S7-1500 / S7-1200 FW 4.4+: preferred for symbolic dispatch

On the S7-1200, a POINTER can be evaluated in STL with the LAR1 / LAR2 load instructions. A complete PEEK through a pointer, in SCL on S7-1200, is built around the same PEEK_* calls. The pointer is therefore not a substitute for PEEK; it is the format that selects what PEEK should read.

2.1 Building a Pointer at Runtime

// SCL on S7-1200, FW 4.x
VAR_TEMP
    pTarget : POINTER;        // 6-byte POINTER
    bValue  : BOOL;
    wValue  : WORD;
END_VAR

// Compose a pointer to DB5.DBX (variable byte, fixed bit 2)
// Layout per Siemens manual: P##DB5.DBX20.2
pTarget := &DB5.DBX[#iByte, 2];

// PEEK via the pointer's area/byte fields
wValue := PEEK(area  := 16#84,
               dbNumber := 5,
               byteOffset := #iByte);
Compatibility: Symbolic pointer construction with &Tag was introduced in TIA Portal V14 for the S7-1200. In TIA V13 UP5 (the version the original question was written against) the address must be assembled by hand or loaded in STL.

3. Symbolic Array Indexing: The Preferred Path

The cleanest replacement for PEEK/POKE on the S7-1200 is indexed access into a symbolically addressed ARRAY. The runtime cost is the same as direct symbol access because the compiler emits the same load/store; the developer cost is dramatically lower because every address is a real symbol with cross-reference, watch, and force support.

3.1 Two-Dimensional Boolean Matrix Example

Mirroring the discussion thread's pattern for an array of ten devices with two control bits each:

FUNCTION_BLOCK FB_DeviceMatrix
VAR
    // 2-D matrix: rows 0..9 = device index, columns 0..1 = control bits
    OutMatrix : ARRAY[0..9, 0..1] OF BOOL;
    // Lookup tables populated by the calling code
    Device   : ARRAY[0..15] OF INT;   // device number for slot i
    Function : ARRAY[0..15] OF INT;   // 0=run/slow, 1=rev/forward
END_VAR

BEGIN
    // Outputs are written symbolically, then mapped to the matrix
    %Q0.0 := #OutMatrix[ #Device[0],   #Function[0] ];
    %Q0.1 := #OutMatrix[ #Device[1],   #Function[1] ];
    // ... up to %Q1.7 driven from #OutMatrix[ #Device[15], #Function[15] ]
END_FUNCTION_BLOCK

The Device[] and Function[] arrays carry the indirect selection. The matrix itself is a normal symbol, so TIA Portal can trace every OutMatrix[i,j] assignment through the cross-reference list. To change a device-to-output mapping, the operator edits the table values, not the program.

3.2 Indexed ARRAY of UDT (Recommended for S7-1200 FW 4.x)

Combine a UDT with an indexed array to address a complete record (e.g. a motor run-time, encoder, status word) by index alone.

TYPE UDT_Motor :
    STRUCT
        RunTime_ms   : DINT;     // commanded run time
        EncoderCnt   : DINT;     // feedback from HSC
        SensorInput  : BOOL;     // prox / home switch
        Output       : BOOL;     // mapped to %Qx.y
        Inhibit      : BOOL;     // interlock
    END_STRUCT;
END_TYPE

DATA_BLOCK "DB_Motors"
    STRUCT
        Motor : ARRAY[0..7] OF UDT_Motor;   // 8 motors
    END_STRUCT
END_DATA_BLOCK

// Indirect symbolic access from an FB
FUNCTION_BLOCK FB_Sequencer : FB_SequencerBase
VAR_IN_OUT
    iMotor : INT;   // 0..7, supplied by the calling sequencer
END_VAR
BEGIN
    // No PEEK, no POKE, no absolute offset
    IF "DB_Motors".Motor[#iMotor].Inhibit = FALSE
       AND "DB_Motors".Motor[#iMotor].SensorInput THEN
        "DB_Motors".Motor[#iMotor].Output := TRUE;
    END_IF;
END_FUNCTION_BLOCK

Every load/store is a symbolic instruction, the cross-reference list is intact, and re-ordering elements in UDT_Motor updates every consumer automatically.

4. VARIANT-Based Generic Functions (S7-1500 and S7-1200 FW 4.4+)

From firmware V4.4 the S7-1200 supports the VARIANT type in FB inputs. A VARIANT carries a symbolic pointer plus a runtime type descriptor, so a generic block can be written once and reused for any compatible tag.

FUNCTION_BLOCK FB_GenericRead
VAR_INPUT
    pSource   : VARIANT;          // any BOOL/INT/REAL tag at the call site
    bEnable   : BOOL;
END_VAR
VAR_OUTPUT
    bDone     : BOOL;
    diValue   : DINT;             // numeric interpretation of pSource
END_VAR
VAR
    bBusy     : BOOL;
END_VAR
BEGIN
    IF #bEnable AND NOT #bBusy THEN
        #bBusy := TRUE;
        CASE pSource.TypeOf() OF
            BOOL:  #diValue := BOOL_TO_DINT(pSource);
            INT:   #diValue := INT_TO_DINT(pSource);
            REAL:  #diValue := REAL_TO_DINT(pSource);
            DINT:  #diValue := pSource;
        END_CASE;
        #bDone := TRUE;
        #bBusy := FALSE;
    END_IF;
END_FUNCTION_BLOCK

This is the mechanism the S7-1500 uses to expose fully symbolic indirect addressing. On the S7-1200 it requires firmware V4.4 or higher and TIA Portal V15.1+.

5. Reusable Function Block with InOut Parameters

Another technique the discussion thread surfaces is to flip the indirection: instead of computing an address inside the FB, let the caller pass the symbol itself as an IN_OUT tag. The FB then has a stable interface across motors, valves, and cylinders.

FUNCTION_BLOCK FB_ActuatorDrive
VAR_INPUT
    iRun_ms    : DINT;        // commanded duration
    bSensorOk  : BOOL;        // permissive
END_VAR
VAR_IN_OUT
    bOutput    : BOOL;        // caller supplies the actual %Qx.y or DBX
END_VAR
VAR
    tonRun : TON;
END_VAR
BEGIN
    #tonRun(IN := TRUE, PT := INT_TO_TIME(#iRun_ms));
    IF #bSensorOk AND NOT #tonRun.Q THEN
        #bOutput := TRUE;
    ELSE
        #bOutput := FALSE;
    END_IF;
END_FUNCTION_BLOCK

Call site:

"FB_Motor1"(iRun_ms := 5000,
            bSensorOk := "DB_Motors".Motor[0].SensorInput,
            bOutput   => "DB_Motors".Motor[0].Output);

"FB_Motor2"(iRun_ms := 3000,
            bSensorOk := "DB_Motors".Motor[1].SensorInput,
            bOutput   => "DB_Motors".Motor[1].Output);

The block is reusable, the wiring is symbolic, and the address of bOutput lives in the caller where it belongs.

6. STL Indirect Addressing (S7-1500)

For engineers maintaining legacy STL code, the Siemens indirect addressing in STL (S7-1500) reference documents the register-based forms. The S7-1200 supports the same LAR1 / LAR2 load-address-register instructions in STL, but in practice the SCL symbolic methods above are preferred because STL pointer math is not type-safe and produces no compiler diagnostics when an offset overflows the data block.

// STL fragment - S7-1500 / S7-1200 FW 4.x
LAR1  P##DB_Motors.Motor[0].Output  // load symbolic address
L     W [AR1,P#0.0]                 // load word at offset 0
T     MW 100                        // transfer to marker

7. Comparison of Methods

Criterion PEEK / POKE POINTER + PEEK ARRAY index UDT in ARRAY VARIANT (FW 4.4+)
Cross-reference in TIA No (absolute) No (absolute) Yes Yes Yes
Type safety None None Yes Yes Yes (runtime check)
Watch / Force in HMI Limited Limited Full Full Full
Compiler diagnostics None on offset error None on offset error Range check online Range check online Range check online
Code reusability Poor Poor Good Excellent Excellent
S7-1200 minimum FW FW 4.0 FW 4.0 FW 4.0 FW 4.0 FW 4.4
Performance (relative) 1.0× 1.0× 1.0× 1.0× 1.05× (type dispatch)

8. When PEEK / POKE Is Still Appropriate

There are a small number of cases where absolute byte/bit access cannot be replaced by symbolic indexing:

  • Generic data-exchange blocks that must accept any ANY pointer from a HMI or third-party device.
  • Recipes loaded from a non-Siemens source that provide a raw byte offset and length.
  • Self-modifying code patterns (rare; not supported on S7-1200 anyway).
  • Migration of S7-300/400 STL blocks that compute offsets in accumulator 1 before a load.
For all of the above, encapsulate the PEEK/POKE call inside a single FB with a clear name, code-comment the area IDs, and add a comment block that lists the symbolic tag each dbNumber/byteOffset pair corresponds to. This restores a layer of the documentation that absolute access removes.

9. Verification Checklist

  1. Open the block in TIA Portal, switch to Cross-references, and confirm every tag inside the FB resolves. If PEEK is used, the cross-reference will show no usage for the target.
  2. Insert a Watch table on the destination tag. Modify the index variable online and confirm the read/write follows the new element within one OB1 cycle.
  3. Force the index to its declared maximum and minimum. The S7-1200 raises a range-check error (SF LED + diagnostic buffer entry OB cycle time exceeded only on bad jumps; Range violation in the diagnostic buffer for ARRAY access). For PEEK accesses no diagnostic is raised, the read silently returns zero.
  4. Re-organise the target DB by inserting a new tag in front of the PEEK target, recompile, and run. With PEEK the value is wrong; with ARRAY / UDT access the value is correct.
  5. For VARIANT blocks, place a breakpoint inside the FB and verify the TypeOf() result matches the symbolic type of the call-site argument.

10. Migration Path From S7-300/400 PEEK-POKE to S7-1200 Symbolic

  1. For each PEEK/POKE call, list the DB number, byte offset, bit offset, and a symbolic name (taken from the legacy STL source comments or the data block declaration).
  2. Introduce a UDT whose fields match the structure of the legacy DB region.
  3. Replace the DB with a symbolic data block containing an ARRAY[..] OF UDT.
  4. Update each call site to index the array symbolically. Replace PEEK_BOOL(16#84, 5, #b, #i) with "DB_New".Motor[#b].Bit[#i].
  5. Build, download, and run the cross-reference check. No target should appear in the unresolved column.

11. Frequently Asked Questions

Can the S7-1200 use the same POINTER arithmetic as the S7-300/400?

Yes. The S7-1200 supports the 6-byte POINTER format in STL (LAR1 / LAR2) and as a POINTER-typed tag in SCL from TIA Portal V14 onward. The pointer still resolves to an absolute address, so the cross-reference list does not follow it; combine it with PEEK/POKE only when the symbolic alternatives (ARRAY, UDT, VARIANT) are not viable.

What is the minimum firmware for VARIANT inputs on the S7-1200?

VARIANT-typed VAR_INPUT and VAR_IN_OUT parameters require S7-1200 firmware V4.4 and TIA Portal V15.1 or later. Earlier firmware accepts only POINTER and ANY.

Why does PEEK_BOOL not appear in the cross-reference list?

PEEK_BOOL takes a numeric area code (e.g. 16#84), DB number, byte offset, and bit offset as integers. The compiler emits a generic load instruction; the TIA Portal cross-reference engine cannot bind the call back to a specific tag. The PEEK target is therefore invisible to Go to usage, Watch, and Force.

Can I use VARIANT to read any tag including DB, M, I, Q areas?

Yes. A VARIANT can point to any symbolically accessible tag including %I, %Q, %M, and DB elements. The caller wires the symbolic tag, and the FB receives a typed handle. This is the recommended replacement for legacy PEEK patterns on S7-1500 and on S7-1200 from firmware V4.4.

Does indexed ARRAY access run at the same speed as a direct tag read?

Yes. The S7-1200 SCL compiler lowers arr[i].field into a single indexed load/store on the data block. There is no measurable runtime penalty versus a fixed offset access, and the symbolic form is fully cross-referenceable. PEEK calls do not offer any performance advantage on the S7-1200.

Back to blog