Siemens S7 PUT/GET: Sequential Job Execution for Multiple PLCs
When a Siemens S7-300, S7-400, S7-1200, or S7-1500 host CPU serves the same data block to two or more partner CPUs via the GET (FB15 / TIA instruction) and PUT (FB14 / TIA instruction) functions, only one of the partner PLCs appears to read successfully while the other returns persistent errors. The fault almost always traces back to a documented constraint that is easy to miss during program design: only one PUT/GET job may be active at a time on a single configured S7 connection, and simultaneous REQ pulses on the same ID produce undefined STATUS values and lost data transfers.
This reference article breaks down the root cause, walks through the correct configuration of multiple S7 connections, explains the CPU protection flag that quietly blocks PUT/GET, and provides a complete interlock pattern using the DONE bit that restores deterministic behavior.
Problem Overview: PUT/GET Failing Between One Host and Multiple Remote PLCs
A typical topology presents itself as follows:
- PLC0 (host) owns a global data block, e.g., DB100, holding the process data to be shared.
- PLC1 and PLC2 (remote consumers) each contain their own destination DB (for example DB200 and DB300) that should mirror the host DB.
- The application code in OB1 of both PLC1 and PLC2 calls the GET function block, triggers it cyclically with a REQ pulse, and expects the remote data to be written into the local destination DB.
Symptom observed in the field:
- The GET instance in PLC1 completes reliably. STATUS returns 0000, DONE is set, and the local DB is updated.
- The GET instance in PLC2 either remains stuck with BUSY=1 indefinitely or returns ERROR=1 with a STATUS code such as W#16#80B2 or W#16#80C3.
- The host PLC0 shows no fault and reports the connection from PLC2 as established in the online connection diagnostics.
- Both remote PLCs reach the same physical network and pass normal ping / connection diagnostics.
This is not a hardware fault, not a network fault, and not a fault in the host's data. It is a software-level constraint on how PUT/GET handles concurrent calls against a configured S7 connection.
Root Cause: Single-Job-Per-Connection Constraint
The Siemens programming and operating manual for the PUT/GET instructions states the rule plainly:
"Only one job at a time can be triggered by the GET and PUT instructions over a configured S7 connection. It is not possible to trigger multiple jobs simultaneously by the GET and PUT instructions over a configured S7 connection. The GET and PUT instructions can only be called sequentially, in other words, one after the other. A job is triggered only when the previous job is completed."
The PUT and GET FBs are not designed for parallel job execution on the same connection handle. Internally, they write the job request into a per-connection control block, set BUSY=1, and wait for the partner CPU's acknowledgement over the ISO-on-TCP / TCP / S7-communication layer. If a second job request is presented to the same connection while the first is still in flight, the second one is rejected, ignored, or stomps on the first depending on the firmware revision.
The most common trigger is the standard OB1 scan pattern:
- Each CPU evaluates its OB1 every scan cycle.
- Both PLC1 and PLC2 raise REQ=TRUE on their GET instance in the same scan that the prior GET completes.
- Both jobs share the same connection identifier (because the programmer used the same ID parameter from a copied instance or a shared DB), so the second REQ collides with the first.
- Even when the IDs differ, an OB1 that calls the same FB instance twice in one cycle loses the second call's state.
The fix is twofold: give each remote PLC its own uniquely identified S7 connection, and ensure that any PUT/GET job request on the same connection is queued behind the previous one via the DONE or NDR (for GET) bit.
Prerequisites: Hardware, Firmware, and Software
Before re-engineering the PUT/GET communication, confirm the following items are in place:
- Hardware: All three CPUs are S7-300, S7-400, S7-1200, or S7-1500 models with PROFINET or PROFIBUS interfaces. PUT/GET operates over S7 connections, not raw PROFINET IO.
- Firmware: S7-300 CPUs require firmware ≥ V2.x for unrestricted PUT/GET. S7-1200 CPUs from firmware V4.0 onward and S7-1500 CPUs support PUT/GET natively. Older S7-200 CPUs do not implement PUT/GET and require NetLink/NetAnu gateways.
- Software: STEP 7 V5.5 / V5.6 for classic S7-300/400, or TIA Portal V15.1 and later for S7-1200/1500 and unified S7-300/400 programming.
- Network: All three CPUs must share an IP subnet, with routing properly configured if the topology crosses subnets. The host CPU must be reachable from both remote PLCs without NAT traversal.
- Data layout: The source DB in PLC0 and the destination DBs in PLC1/PLC2 must declare byte-identical structures where shared fields are concerned. PUT/GET is a raw byte transport; it does not interpret symbolic names.
- Access rights: The "Permit access with PUT/GET communication from remote partner" flag must be enabled on the CPU that owns the data. Without this, the CPU rejects every PUT/GET request silently.
Step 1: Configure Distinct S7 Connections for Each Remote PLC
Each remote PLC must establish its own S7 connection to the host CPU. In TIA Portal:
- Open Devices & Networks for the project.
- Select the PROFINET interface of PLC1 and drag a connection to the PROFINET interface of PLC0. In the popup, choose "S7 connection".
- Repeat for PLC2, but verify the connection appears as a second object with a unique local ID. By default, the first connection is S7_Connection_1 and the second is S7_Connection_2.
- On each connection, open Properties → General and confirm that "Establish active connection" is enabled on the partner that should initiate the GET.
- Note the Local ID (Hex) of each connection. The IDs are written into the ID input parameter of the GET/PUT call.
The local connection IDs are critical because the GET/PUT instruction references its connection through the ID parameter. If both remote PLCs use ID = W#16#0001 (the default of the first connection), they will share the same connection handle in the project's connection table, and the operating system will refuse to schedule two GET jobs against the same connection in parallel.
In STEP 7 V5.x, the equivalent setup is in NetPro → S7 Connection, where each partner pair must be defined as a separate row. The local connection ID is shown as a hex word on the connection object's properties.
| Parameter | PLC1 → PLC0 | PLC2 → PLC0 |
|---|---|---|
| Local ID (hex) | W#16#0001 | W#16#0002 |
| Partner ID (hex) | W#16#0001 | W#16#0002 |
| Active/passive | Active (PLC1 initiates) | Active (PLC2 initiates) |
| Local port / TSAP | Default 03.01 | Default 03.01 |
| Partner port / TSAP | 03.01 (PLC0 slot 1) | 03.01 (PLC0 slot 1) |
| Connection resource | PG/OP and S7 communication → 1 | PG/OP and S7 communication → 2 |
On S7-1500, connection resources are limited per CPU and must be budgeted. The default resource type "PG/OP and S7 communication" is adequate for up to ~14 S7 connections per interface, beyond which an explicit "S7 communication" resource must be configured.
Step 2: Enable Remote PUT/GET Access on the CPU
Even with a properly configured S7 connection, the CPU refuses all PUT/GET requests unless the protection flag is explicitly enabled.
- In the project tree, select the host CPU (PLC0).
- Open Properties → Protection & Security (TIA Portal) or Properties → Protection (STEP 7 V5.x).
- Under Connection mechanisms, check the box labeled "Permit access with PUT/GET communication from remote partner" (German: "Bausteinkennung PUT/GET-Verbindung erlauben").
- Compile and download the hardware configuration to PLC0.
After this change, a CPU restart is generally not required on S7-1200/1500; S7-300/400 CPUs may require STOP/RUN or a restart depending on firmware. Verify by reading the CPU diagnostic buffer; entries with ID "Communication error - PUT/GET not permitted" disappear once the flag is active. On TIA Portal V18 and later the flag is also reachable through the online protection tab.
Step 3: Implement Sequential Job Interlocking with DONE Bit
Once each remote PLC has its own S7 connection, the parallel-job problem still exists inside each CPU if the application calls the same GET/PUT instance from multiple OBs or from multiple call sites in OB1. The fix is to gate the REQ input of the next job behind the DONE bit of the previous one.
The following snippet in Structured Text shows the canonical pattern. It uses two GET instances — GET_HostData_A and GET_HostData_B — that must execute sequentially on connection ID W#16#0001 in PLC1:
// Sequential GET interlock — single connection, multiple jobs
// DB "Comm_Control" is the instance DB hosting these tags
// Job A: GET reading DB100.DBD0..DBD19 from PLC0 into DB200.DBD0..DBD19
GET_HostData_A(
REQ := NOT GET_HostData_A.BUSY AND NOT GetJobBActive AND JobARequested,
ID := W#16#0001, // unique local connection ID
ADDR_1 := P#DB100.DBX0.0 BYTE 20, // remote address in PLC0
RD_1 := P#DB200.DBX0.0 BYTE 20, // local destination in PLC1
DONE => JobADone,
BUSY => JobABusy,
ERROR => JobAError,
STATUS => JobAStatus
);
// Job B: only enabled after Job A completes
GetJobBActive := JobADone AND NOT JobABusy;
GET_HostData_B(
REQ := GetJobBActive AND JobBRequested AND NOT GET_HostData_B.BUSY,
ID := W#16#0001, // SAME connection ID — sequential, not parallel
ADDR_1 := P#DB100.DBD24.0 BYTE 40, // different slice of PLC0's DB
RD_1 := P#DB200.DBD24.0 BYTE 40, // destination in PLC1
DONE => JobBDone,
BUSY => JobBBusy,
ERROR => JobBError,
STATUS => JobBStatus
);
The ladder equivalent uses the DONE bit as a permissive contact on the second network's REQ rung:
// Network 1 — GET_A
A "JobARequested"
AN "GET_HostData_A.BUSY"
AN "GetJobBActive"
= L 0.0
CALL "GET_HostData_A"
REQ :=L0.0
ID :=W#16#0001
ADDR_1 :=P#DB100.DBX0.0 BYTE 20
RD_1 :=P#DB200.DBX0.0 BYTE 20
NDR := "JobADone"
BUSY := "JobABusy"
ERROR := "JobAError"
STATUS:="JobAStatus"
// Network 2 — GET_B only after A done
A "JobADone"
AN "GET_HostData_A.BUSY"
A "JobBRequested"
AN "GET_HostData_B.BUSY"
= L 0.0
CALL "GET_HostData_B"
REQ :=L0.0
ID :=W#16#0001
ADDR_1 :=P#DB100.DBD24.0 BYTE 40
RD_1 :=P#DB200.DBD24.0 BYTE 40
NDR := "JobBDone"
BUSY := "JobBBusy"
ERROR := "JobBError"
STATUS:="JobBStatus"
The same pattern applies to PUT, with the addition that a PUT job should generally be triggered on a data-change event rather than cyclically, because PUT overwrites memory in the partner CPU and there is no read-back guarantee. If PUT and GET must coexist on the same connection ID, interlock both directions: PUT.REQ := GetDone AND NOT PutBusy AND PutTrigger; GET.REQ := PutDone AND NOT GetBusy AND GetTrigger.
Alternative: Migrating to Send/Receive Blocks
When the application code in both the host and the remote PLCs is under the designer's control, Siemens recommends Send/Receive (BSEND/BRCV, USEND/URCV) over PUT/GET. Send/Receive offers:
- Bidirectional, explicit handshake — the application code in the host decides when to push or pull data.
- No special CPU protection flag — Send/Receive runs over the same S7 connection but is considered normal program-controlled communication.
- Larger payload sizes (up to 32 KB per BSEND segment versus a few hundred bytes for typical PUT/GET use).
- No single-job-per-connection constraint in the same way: BSEND and BRCV can be interleaved on the same connection as long as each direction is sequenced.
The cost is that the host PLC's program must contain the matching BSEND or BRCV calls — exactly what PUT/GET was designed to avoid when the host is a third-party or legacy program. If both PLCs are owned by the same project, migrating to Send/Receive is almost always the right architectural choice.
For an S7-1500, the modern instruction set renames these to TSEND/TRCV and includes TCON, TDISCON, and T_RESET for explicit connection lifecycle control. The sequencing rules are similar: the same TCON ID may only carry one TSEND/TRCV call at a time on the same direction. The TUSEND/TURCV pair provides unacknowledged datagrams when latency matters more than reliability.
Multi-Instance DB Design for Centralized GET Logic
When two remote PLCs are reading the same host CPU, the temptation is to copy the same GET FB instance and reuse it from a shared library block. The instance DB then holds the GET's per-call state — REQ edge detection, BUSY memory, DONE edge memory. If the same FB instance is called twice in the same scan, its static variables collide.
The Siemens documentation on multi-instances explains that when one FB calls another, the called FB's instance data can be stored in the instance DB of the calling FB. This avoids the proliferation of small instance DBs and keeps the call hierarchy clean. The static and temporary variables of the called FB live as a substructure inside the caller's instance DB, and each instance path (e.g., GET_HostData_A vs. GET_HostData_B) addresses a separate offset in that block.
For the PUT/GET scenario, the recommended pattern is:
- Create one parent FB, e.g., FB100 "Host_Comm_Master", with two multi-instance declarations of the GET function block: GET_To_PLC0_A, GET_To_PLC0_B.
- Both multi-instances live inside the instance DB of FB100 (e.g., IDB100), so they have separate state storage, separate REQ edges, separate BUSY/DONE bits.
- OB1 calls FB100 once per cycle; FB100 internally sequences the two GET calls using the DONE-bit interlock pattern shown above.
- Scaling to N GET jobs against the host means adding N multi-instance variables inside FB100, without polluting the program with N individual instance DBs.
This pattern also keeps the connection ID, ADDR_1, RD_1, and LEN parameters visible in one block, simplifying code review and audit.
STATUS Output Codes and Error Interpretation
The STATUS output of the GET/PUT FB returns a 16-bit word that encodes both progress information and error classification. The most useful values to recognize in this context are:
| STATUS (hex) | BUSY | ERROR | DONE/NDR | Meaning |
|---|---|---|---|---|
| 0000 | 0 | 0 | 1 | Job completed successfully; for GET, new data has been written. |
| 7000 | 0 | 0 | 0 | No job active, REQ not yet seen. |
| 7001 | 1 | 0 | 0 | First call after REQ edge; job initiated. |
| 7002 | 1 | 0 | 0 | Subsequent call; job still running. |
| 80B0 | 0 | 1 | 0 | Connection not established; S7 connection ID invalid or down. |
| 80B2 | 0 | 1 | 0 | Remote address (ADDR_1) syntax error or out-of-range pointer. |
| 80B4 | 0 | 1 | 0 | Data type or length mismatch between local and remote areas. |
| 80C3 | 0 | 1 | 0 | PUT/GET access not permitted on the remote CPU (protection flag). |
| 80C4 | 0 | 1 | 0 | Communication error during job execution (timeout, abort, partner STOP). |
| 80D0 / 80D1 | 0 | 1 | 0 | Resource bottleneck on the partner CPU (no receive resources). |
The status code alone is not enough to identify the root cause; it must be cross-referenced with the CPU diagnostic buffer of both the local and remote CPU and with the online connection diagnostics in TIA Portal. Open the diagnostic buffer via Online → Online & Diagnostics → Diagnostic buffer and look for entries with ID W#16#03F1 (communication fault) or W#16#03F2 (PUT/GET rejected).
Verification and Commissioning Procedure
After applying the interlock pattern and confirming the connections are unique, run through the following checklist:
- Download the updated hardware configuration (with the PUT/GET flag enabled) to PLC0 and restart if required.
- Download the program changes to PLC1 and PLC2.
- Switch all three PLCs to RUN.
- In TIA Portal, open Online & Diagnostics → Connections. Confirm that both S7_Connection_1 (PLC0 ↔ PLC1) and S7_Connection_2 (PLC0 ↔ PLC2) are in state "Established".
- Monitor the GET instances in PLC1 and PLC2 in a watch table. Verify that BUSY pulses TRUE for the duration of each transfer, then drops to FALSE with DONE=TRUE and STATUS=0000.
- Confirm that in PLC2, DONE alternates with PLC1's DONE without overlap — i.e., when PLC1's GET is BUSY=TRUE, PLC2's GET is idle, and vice versa if they shared an ID. With distinct IDs, both may run in parallel.
- Force a value change in PLC0's DB100 and verify that both destination DBs in PLC1 and PLC2 reflect the new value within one cycle of the interlock chain.
- Read the diagnostic buffer of all three CPUs and confirm no communication error entries.
- Stress-test by cycling a 100 ms OB35 timer that retriggers the GET every 100 ms; confirm no STATUS codes other than 0000 / 7000 / 7001 / 7002 appear over 10 minutes of operation.
If the original fault was STATUS=80C3, the verification must additionally check that the CPU protection flag survives a power cycle — some firmware revisions reset the flag to default if a configuration download is interrupted.
Troubleshooting Matrix: Common Symptoms and Fixes
| Symptom | Likely Cause | Fix |
|---|---|---|
| Only one of two remote PLCs reads data | Both PLCs share the same S7 connection ID | Create separate S7_Connection entries, assign unique local IDs (W#16#0001 vs W#16#0002) |
| GET stuck with BUSY=1 indefinitely | Concurrent REQ on the same connection | Interlock REQ behind DONE bit of the prior job |
| ERROR=1 with STATUS=80C3 on all remotes | CPU protection flag blocks PUT/GET | Enable "Permit access with PUT/GET communication from remote partner" on host CPU |
| ERROR=1 with STATUS=80B0 | Connection ID not configured or wrong project | Re-build the S7 connection in Devices & Networks, recompile, download |
| GET works once after download, then never again | REQ is level-triggered; GET needs an edge | Detect REQ edge with FP or compare to last cycle's REQ |
| Data arrives but is partially zero or garbage | RD_1 / SD_1 length mismatch or DB alignment | Match LEN to actual payload; align DB to byte boundary |
| PUT overwrites data but GET returns nothing | PUT blocks REQ on a busy connection | Sequence PUT and GET on the same connection with DONE interlock |
| GET fails after PLC0 STOP/RUN | Connection resource not released on STOP | Add reconnect logic with STATUS=80B0 → reissue REQ after 1 s delay |
| TIA Portal online shows connection OK, but GET reports STATUS=80B0 | Configured in TIA Portal but not downloaded to PLC | Re-download the connection configuration to both partners |
| S7-1200 rejects GET from S7-300/400 | S7-1200 default protection level 3 (full) since FW 4.x | Lower protection level to "Full access (no protection)" or configure a password |
Safety and Operational Notes
PUT/GET operates below the application-level safety stack (F-runtime). If the shared data influences a safety function (e.g., emergency stop, guard door, safe speed), the read path must additionally be validated by the F-CPU's safety program — never trust a single GET as the sole input to a safety decision. Use PROFIsafe for safety-relevant data exchange.
For cyclic use, size the OB1 scan budget for the GET's transfer time. A 200-byte GET over PROFINET completes in roughly 20–40 ms; a 1000-byte GET takes 80–150 ms. Triggering GET every OB1 cycle on a 200-byte payload adds <5% to scan time on a typical S7-315; triggering on every 100 ms task is recommended to bound jitter.
Network load should be profiled separately. Each active S7 connection consumes one TCP port pair on the host CPU; on S7-1500 the default connection resource pool is 14 per interface and must be expanded if more than ~12 concurrent PUT/GET partners are expected.
Frequently Asked Questions
Can PLC1 and PLC2 read the same DB in PLC0 simultaneously?
Yes — as long as each remote PLC uses its own S7 connection with a unique local ID. Each connection is an independent ISO-on-TCP / TCP session, and the host CPU serves both in parallel. The single-job constraint applies to a single connection, not to a single host CPU.
Why does my GET return STATUS=80C3 even though the connection is established?
STATUS=80C3 means "PUT/GET access not permitted on the remote CPU." The connection handshake succeeded, but the host CPU rejected the PUT/GET request because the "Permit access with PUT/GET communication from remote partner" flag is disabled in the host's Protection & Security settings. Enable the flag, recompile, and re-download the hardware configuration.
Do I need to create a separate source DB in PLC0 for each remote PLC?
No. Both remote PLCs can read the same source DB (e.g., DB100). PUT/GET is a byte-level transport; it does not distinguish readers. Each reader gets its own copy of the bytes in its local destination DB, and the source DB in PLC0 remains unchanged.
Can PUT and GET share the same S7 connection?
Yes, but only sequentially. The single-job-per-connection rule applies to both directions. If PUT is in flight (BUSY=1), a GET REQ on the same ID will be ignored. Interlock the REQ inputs so PUT runs to completion before GET is triggered, and vice versa, using the DONE and ERROR bits as the gating condition.
Should I migrate PUT/GET to BSEND/BRCV or TSEND/TRCV?
If both the host and remote PLCs are in the same project and their programs can be modified, yes. Send/Receive gives explicit control, larger payloads (up to 32 KB), and avoids the CPU protection flag. PUT/GET remains appropriate when the host program is sealed (third-party, legacy, or commissioning-only) and cannot be changed, or when read access without host cooperation is required.