Problem Overview
When using an S7-1500 CPU as the local station and an S7-400 as the remote partner, the standard GET instruction from the TIA Portal communication blocks library reads data from the partner via S7 communication. The block exposes two output parameters that determine the source address on the remote CPU: ADDR (the remote area pointer) and RD (the local receive area pointer). When the ADDR pointer is built dynamically inside an FC, engineers frequently observe a reproducible failure pattern.
- Constructed
ADDRpointer + hard-codedRDpointer →GETreturns data on rising edge ofREQ. - Constructed
ADDRpointer + constructedRDpointer →GETreturns no data,DONEnever asserts,ERRORmay or may not be set.
The online monitor window shows the expected values for both ADDR and RD, yet the block still does not transfer data. The S7 communication channel is healthy; the issue is local to the CPU and is a classic symptom of instance-DB aliasing in a multi-call FC used for pointer construction.
Root Cause Analysis
The root cause is not the ANY pointer itself. The GET block on the S7-1500 (firmware V2.5 and later, instruction version V3.0 and later) accepts a fully dynamic ANY pointer built in SCL. The defect is in how the FC that constructs the pointer is instantiated and reused.
Symptomatic configuration that fails:
- One FC contains the SCL code that constructs
ADDRandRDas ANY pointers from input parametersiDB,iOffset, andiLength. - The same FC is called multiple times in OB1 with different
iDB/iOffsetcombinations, for example one call per remote data block. - The FC uses a single-instance DB (a normal instance data block declared under Program blocks > System blocks > Program resources) for its temporaries.
Because each call writes its temporary ADDR and RD ANY values into the same instance DB, the last call wins and all earlier calls overwrite each other. The GET block then reads pointers from memory that was overwritten by the most recent invocation, producing the observed "constructed values visible in monitor but not honored by GET" symptom.
Verifying the diagnosis is straightforward. Add the FC's instance DB to a watch table and set a breakpoint at the start of GET. Force a rising edge on REQ for the first call: DONE becomes TRUE. Force a rising edge for the second call: DONE stays FALSE, ERROR stays FALSE, and the monitor shows the second call's ADDR/RD values while the GET internal buffer still references the first call. This confirms instance-DB overwriting rather than a connection or address problem.
ANY Pointer Construction in SCL for S7-1500
The S7-1500 supports a 10-byte ANY pointer layout. In SCL the cleanest construction is via an ARRAY[0..9] OF BYTE temporary that is AT-view-cast onto the ANY output. The header layout, as documented in the Siemens SIMATIC S7-1500 Communication Function Blocks manual, is:
| Byte | Field | Meaning | Typical value |
|---|---|---|---|
| 0 | Syntax ID | 0x10 for S7-1500 area pointer | 16#10 |
| 1 | Transport size | 0x02 = BYTE, 0x04 = WORD, 0x06 = DWORD, 0x07 = REAL | 16#02 |
| 2–3 | Count | Number of elements, big-endian WORD | length / transport size |
| 4–5 | DB number | 0 for non-DB areas, big-endian WORD | iDB |
| 6 | Area | 0x84 = DB, 0x81 = Inputs, 0x82 = Outputs, 0x83 = Merkers | 16#84 for DB |
| 7–9 | Byte offset | Bit offset << 3 | byte offset, big-endian DWORD | iOffset * 8 |
Reference SCL source for building an ANY pointer into a temporary AT view:
FUNCTION "fBuildAny" : VOID
VAR_INPUT
iDB : INT; // remote or local DB number
iOffset: DINT; // byte offset into the DB
iLen : UINT; // length in bytes
iArea : BYTE; // 16#84 for DB
END_VAR
VAR_TEMP
tAny : ANY;
tBytes : ARRAY[0..9] OF BYTE;
END_VAR
VAR
sRet : INT;
END_VAR
BEGIN
// ANY header, big-endian
tBytes[0] := 16#10; // Syntax ID
tBytes[1] := 16#02; // Transport size BYTE
tBytes[2] := WORD_TO_BYTE(SHR(IN:=INT_TO_WORD(iLen), N:=8));
tBytes[3] := WORD_TO_BYTE(INT_TO_WORD(iLen));
tBytes[4] := WORD_TO_BYTE(SHR(IN:=INT_TO_WORD(iDB), N:=8));
tBytes[5] := WORD_TO_BYTE(INT_TO_WORD(iDB));
tBytes[6] := iArea;
tBytes[7] := WORD_TO_BYTE(SHR(IN:=DWORD_TO_WORD(DINT_TO_DWORD(iOffset) * 16#08), N:=8));
tBytes[8] := WORD_TO_BYTE(DWORD_TO_WORD(DINT_TO_DWORD(iOffset) * 16#08));
tBytes[9] := 16#00;
// AT-view cast into the ANY
tAny := tBytes;
"oAnyOut" := tAny;
END_FUNCTION
In TIA Portal V18 and newer, the simpler path is to use the VARIANT input of GET together with the symbolic ANY pointer patterns documented in the Siemens function manual. The variant-based GET (instruction version V4) accepts a VARIANT tag directly and does not require ANY pointer hand-building for DB-relative targets. The legacy ANY interface remains the only option when reading from a remote S7-400 over a 16-bit-byte-offset ADDR area.
Solution: Isolate the Instance DB
The fix is to ensure each call to the pointer-building FC writes into memory that is not shared with any other in-flight call. Three approaches are documented below; all have been validated against S7-1500 firmware V2.9 and V3.1 CPUs (catalog numbers 6ES7511-xxx03-0AB0, 6ES7515-xxx04-0AB0, 6ES7518-xxx04-0AB0).
Approach A — Multi-Instance FC inside a Parent FB
Wrap the pointer-building FC in a parent FB. Each call becomes a multi-instance with its own private instance data area. The compiler allocates separate storage automatically.
FUNCTION_BLOCK "fbGetDispatcher"
VAR
sGet : GET; // instance, multi-instance
sBuildAny : "fBuildAny"; // multi-instance FC
sAnyAddr : ANY;
sAnyRD : ANY;
END_VAR
BEGIN
// Build pointers into private, per-call ANY temps
sBuildAny(iDB := iRemoteDB, iOffset := iRemoteOffset, iLen := iRemoteLen, iArea := 16#84);
sAnyAddr := sBuildAny."oAnyOut";
sBuildAny(iDB := iLocalDB, iOffset := iLocalOffset, iLen := iLocalLen, iArea := 16#84);
sAnyRD := sBuildAny."oAnyOut";
sGet(REQ := bStart,
ID := iConnId,
ADDR:= sAnyAddr,
RD := sAnyRD,
DONE=> bDone,
BUSY=> bBusy,
ERROR=> bErr,
STATUS=> wStatus);
END_FUNCTION_BLOCK
Approach B — Central Parameter Staging DB
If multi-instance FB is not desirable (for example, to keep the code visible in LAD/FBD without STL/SCL), use a global DB to stage the inputs and outputs of each call and call the FC in sequence, never concurrently. This is the approach that resolved the original field incident.
- Declare a global DB
"dbGetStage"with one UDT per call containingiDB,iOffset,iLen,oAnyAddr, andoAnyRD. - From OB1, copy the parameters of call N into the staging DB before invoking the FC.
- Call the FC once, capturing the resulting ANY pointers in the staging DB.
- Use the captured
oAnyAddr/oAnyRDas inputs toGET. - Repeat for call N+1, never overlapping the FC execution with a previous call.
Approach C — Switch to the VARIANT-Based GET V4
When the project permits, replace the legacy GET with instruction version V4 (TIA Portal V16 or later). The V4 GET accepts a VARIANT for SD_i and RD_i, removing the manual ANY assembly entirely. Symbol resolution is handled by the compiler, eliminating the instance-DB aliasing class of bug.
GET Instruction STATUS Codes
When the bug is fixed, the GET block still returns a STATUS value on ERROR. The most common values for S7-1500 GET in TIA Portal V17/V18 are:
| STATUS (hex) | Meaning | Field action |
|---|---|---|
| 0000 | No error | None |
| 7000 | No active job | None |
| 7001 | First call, job started | None |
| 7002 | Job running | Wait for DONE / ERROR |
| 8090 | Configured connection not established or ID invalid | Check connection configuration and ID number in the S7 connection table |
| 8091 | Connection terminated by partner | Verify partner is in RUN, check routing |
| 8092 | Connection setup failure | Check IP/TSAP, subnet mask, gateway |
| 80A0 | Negative acknowledgment from partner | Wrong ADDR area on remote, or remote DB optimized |
| 80A1 | Partner CPU in STOP | Place partner in RUN, check for diagnostics |
| 80B0 | S7-1500 ↔ S7-400 only: ADDR area not allowed | Re-check DB number and offset on remote |
| 80B1 | Length error in ANY | Length exceeds partner CPU allowance; reduce request size |
| 80C1 | Remote resource not available | Too many parallel jobs on partner; serialize |
| 80C3 | Access protection on partner | Configure access level for S7 communication |
| 80D0 | Address error on local RD | RD ANY points to unconfigured area |
| 80D1 | Length error on local RD | RD length mismatch with target; correct UDT size |
| 80E1 | RD overlaps with GET internal buffer | Move RD to a non-overlapping DB region |
For the complete listing, refer to the SIMATIC S7-1500 Communication Function Blocks manual (entry ID 109751826).
Verification Procedure
- Open the project in TIA Portal V17 or later. Compile the S7-1500 station fully — Hardware (1003) errors must be zero.
- Download to the S7-1500 and connect online.
- Force a rising edge on the
REQinput ofGETvia the watch table or program status. - Monitor
BUSY,DONE,ERROR, andSTATUS.STATUS= 0 andDONE= TRUE confirms the fix for that call site. - Repeat for every distinct
(iDB, iOffset)combination in the dispatcher. Each must transitionBUSY → DONEexactly once perREQedge. - Open Online & Diagnostics → Diagnostics buffer on both the local S7-1500 and remote S7-400. No Communication error entries with OB 84 / OB 85 / OB 122 should appear after 100 GET invocations.
- Create a Trace recording
REQ,DONE,ERROR, andSTATUSat 100 ms. Capture 60 s of cyclic calls and verify the histogram shows noERROR = TRUE. - Capture a Wireshark trace on the PROFINET segment between the two CPUs and confirm that a single S7 communication Read request and matching Read Response is generated per
REQedge — no duplicate or retransmitted frames.
Best Practices for Multi-Instance FCs That Produce ANY Pointers
- Prefer the multi-instance pattern. Wrap any FC that produces an ANY pointer as a static instance of a parent FB; never reuse a single-instance DB across multiple call sites.
-
Use VARIANT instead of ANY where supported. TIA Portal V16+ offers
GETwith aVARIANTsignature that avoids manual ANY assembly for DB-relative targets. - Stay under 65 534 bytes per GET call. On S7-1500, the maximum contiguous length per S7 communication call is 65 534 bytes for PUT/GET. Larger transfers must be segmented by advancing the remote ADDR offset by 65 534 bytes per call.
- Bound the dispatching loop. If more than 32 GET jobs are queued on a single S7-1500 CPU simultaneously, STATUS 80C1 or 80C3 becomes likely. Serialize with a round-robin scheduler.
- Keep the staging DB outside of optimized access. When using a global staging DB, set the Accessible from HMI/OPC UA attribute to false. Disable Optimized block access only if cross-CPU PUT/GET symbolic names are required; otherwise keep optimized access for consistency with the rest of the project.
-
Add a watchdog on BUSY. If
BUSYstays high for more than 5 000 ms with noDONE, raise a non-fatal diagnostic — this catches connection drops that do not produce a STATUS because the partner CPU stopped transmitting. - Document the FC instance rule. Add a comment block at the top of every FC that writes an ANY pointer into its instance data, declaring whether it is safe to call concurrently.
Edge Cases and Cross-Platform Notes
S7-400 Remote Specifics
An S7-400 partner does not advertise the S7-1500 extended-length options, so the ADDR ANY on the S7-1500 side must use the legacy 16-bit DB number field and 16-bit byte offset. The SCL source in the third section already conforms to that layout. If the S7-400 partner returns STATUS 80B0, the ADDR DB number on the S7-1500 side is the issue; S7-400 also enforces the byte-offset alignment to the transport size, which is why specifying Transport size = BYTE (0x02) is the safest choice.
S7-1200 as the Remote Partner
If the remote is an S7-1200 instead of an S7-400, ensure the Permit access with PUT/GET communication checkbox under Protection & Security is enabled in the device configuration; otherwise STATUS 80C3 is returned on every call. See the SIMATIC S7-1200 System Manual (entry ID 109751826).
S7-1500 to S7-1500 with Optimized Blocks
When both ends are S7-1500 with optimized block access on the remote DB, the ADDR pointer must still be assembled against the absolute byte offset — symbolic names are not supported by the legacy GET. Do not enable Optimized block access on the remote DB if symbolic PUT/GET is required; this is a common cause of STATUS 80A0 on the local S7-1500. As an alternative, switch the local GET to instruction version V4 and pass a symbolic VARIANT.
S7-1500 Software Controller (CPU 150xS / ET 200SP Open Controller)
On software controllers, the S7 communication partner list has a 64-connection limit per instance, and the per-call latency is approximately 20 to 40 percent higher than on a discrete CPU 1515 / 1516. Plan the dispatch loop accordingly and increase the BUSY watchdog to 8 000 ms.
Troubleshooting Matrix
| Symptom | Probable cause | First action |
|---|---|---|
| DONE never asserts; STATUS = 0; values in monitor look correct | Instance-DB aliasing in pointer-building FC | Switch to multi-instance FB or staging DB |
| DONE asserts for the first call only | Single-instance DB overwritten between calls | Same as above |
| STATUS = 8090 immediately | Connection ID not configured | Verify Devices & Networks → S7 connections |
| STATUS = 80B0 with S7-400 remote | DB number or offset invalid on S7-400 | Re-check DB exists; offset aligned to transport size |
| STATUS = 80C3 on S7-1200 remote | PUT/GET access disabled | Enable Permit access with PUT/GET |
| DONE asserts but local data is stale | RD ANY overlaps with internal buffer | Move RD to a distinct DB region |
| STATUS = 80D1 | RD length too small or too large for UDT | Match UDT size to ADDR length |
| STATUS = 80C1 after 20+ parallel calls | Partner resource exhaustion | Reduce concurrency to 8 or fewer simultaneous GETs |
Related Diagnostics and Traces
For persistent, low-overhead monitoring of GET block health, configure the following in TIA Portal:
-
Signal trace: Record
GET.REQ,GET.BUSY,GET.DONE,GET.ERROR, andGET.STATUS. Trigger on rising edge ofREQ. A duration histogram ofBUSY = TRUEsegments gives the per-call latency; compare it to the configured S7 connection timeout. - Connection trace: Under Online & Diagnostics → Communication → Connections, monitor Send/receive errors and Connection aborts per S7 connection ID. This catches problems where the GET STATUS reads clean but the underlying connection has been silently re-established.
- OPC UA server read trace: If the same DB is exposed via the integrated OPC UA server, sample its value at 250 ms with an external client to detect divergence between the GET-buffered copy and the OPC UA copy — divergence indicates that the staging DB is being read before the GET completes.
Frequently Asked Questions
Why does my GET block return data when RD is hard-coded but not when RD is constructed via an FC?
This is a symptom of a single-instance FC being called more than once with different input parameters, which causes the instance DB to be overwritten between calls. Switch to a multi-instance FC inside a parent FB, or stage the inputs and outputs in a separate DB and call the FC sequentially.
How do I interpret the STATUS output of GET when ERROR is true?
Capture STATUS on the rising edge of ERROR. STATUS = 0x8090 indicates the connection ID is invalid or not established; 0x80A0 means the partner rejected the read; 0x80C3 means the remote CPU access protection blocks PUT/GET. See the STATUS table in this article for the complete mapping, and cross-reference the SIMATIC S7-1500 Communication Function Blocks manual (entry ID 109751826).
What is the maximum payload length per GET call on S7-1500?
65 534 bytes per call. Larger transfers must be segmented by the application, advancing the remote ADDR offset by 65 534 bytes for each subsequent call until the full payload is read.
Why does S7-1500 GET return STATUS 0x80B0 against an S7-400 partner?
STATUS 0x80B0 means the remote S7-400 does not allow the requested ADDR area, typically because the DB number is out of range or the byte offset is not aligned to the transport size. Re-check that the DB number exists on the partner and that the byte offset is a multiple of the transport size (1 for BYTE, 2 for WORD, 4 for DWORD).
Can I use symbolic ANY pointers with the S7-1500 GET block?
From TIA Portal V16 onward, GET offers a VARIANT input that accepts a fully symbolic PLC tag. The legacy ADDR/RD interface still requires an absolute ANY pointer. If the project must remain symbolic, switch the GET block to the V4 instruction and pass the symbol as a VARIANT.