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.
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:
- The read result is a copy. It cannot be combined inline with another bit read because
PEEK_BOOLreturns a scalar, not a reference. The branch has to be evaluated twice (once for each side) or the result materialised in a temp. - The data block number, byte offset, and bit offset are absolute. The TIA Portal cross-reference list cannot resolve a
16#84access 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);
&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
ANYpointer 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.
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
- 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.
- 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.
- 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 exceededonly on bad jumps;Range violationin the diagnostic buffer for ARRAY access). ForPEEKaccesses no diagnostic is raised, the read silently returns zero. - Re-organise the target DB by inserting a new tag in front of the PEEK target, recompile, and run. With
PEEKthe value is wrong; withARRAY/UDTaccess the value is correct. - For
VARIANTblocks, place a breakpoint inside the FB and verify theTypeOf()result matches the symbolic type of the call-site argument.
10. Migration Path From S7-300/400 PEEK-POKE to S7-1200 Symbolic
- For each
PEEK/POKEcall, list the DB number, byte offset, bit offset, and a symbolic name (taken from the legacy STL source comments or the data block declaration). - Introduce a UDT whose fields match the structure of the legacy DB region.
- Replace the DB with a symbolic data block containing an
ARRAY[..] OF UDT. - Update each call site to index the array symbolically. Replace
PEEK_BOOL(16#84, 5, #b, #i)with"DB_New".Motor[#b].Bit[#i]. - 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.