1. Overview: PUT/GET in S7 Communication
Siemens S7 PLCs exchange data between CPUs over Industrial Ethernet using the S7 communication protocol. Two fundamental function blocks handle this exchange:
- SFB14 "GET" – reads data from a remote CPU into the local CPU.
- SFB15 "PUT" – writes data from the local CPU to a remote CPU.
For the classic S7-300 and S7-400 families, SFB14/15 are the standard implementation. For S7-1200 and S7-1500, the functionally equivalent instructions are the extended PUT and GET instructions available in TIA Portal under "Communication → S7 Communication." Both implement the same ANY-pointer data layout, the same connection model, and the same R_ID matching concept, so the syntax described here applies to both generations. See the TIA Portal reference at GET and PUT (Read and write from a remote CPU) for the S7-1200 implementation.
The most common reason a PUT/GET call compiles without errors but fails to write data is an incorrectly formatted ANY pointer on parameters SD_1 (source) or ADDR_1 (destination). The block validates the structure internally and may even accept a malformed pointer in some firmware versions, but the partner CPU rejects the telegram with status code W#16#0001 or W#16#0007.
2. Prerequisites
Before any PUT call can move data, the following must be in place:
- Hardware: Two S7 CPUs equipped with an Ethernet interface (CP343-1 / CP443-1 for S7-300/400; integrated PROFINET port on S7-1200/1500) connected to the same subnet.
- IP addressing: Both CPUs must have unique IP addresses in the same subnet mask. Put/Get does not require a router.
- Connection configuration: An S7 connection must be defined in NetPro (STEP 7 V5.x) or in the "Devices & Networks" editor (TIA Portal). The local CPU is the active partner for PUT/GET, the partner CPU is the passive partner.
- Access rights: On the passive CPU, "Permit access with PUT/GET communication from remote partner" must be enabled (S7-1200/1500: Properties → Protection; S7-300/400: enable the "S7 communication" checkbox in the CPU properties).
- Data blocks: Source and destination DBs must exist on their respective CPUs, with sufficient length declared in the DB structure.
3. SFB15 "PUT" Block Parameters
SFB15 occupies DB space (instance DB) and has the following interface:
| Parameter | Declaration | Type | Description |
|---|---|---|---|
| REQ | INPUT | BOOL | Rising edge triggers the PUT operation. |
| ID | INPUT | WORD | Local ID of the S7 connection (taken from NetPro/TIA connection table). |
| SD_1 | INPUT | ANY | Pointer to the local source data area. |
| ADDR_1 | INPUT | ANY | Pointer to the remote destination data area. |
| LEN | INPUT | WORD | Length of the data to send in bytes (1–160). Computed automatically if you wire a valid ANY pointer. |
| DONE | OUTPUT | BOOL | 1 for one cycle when PUT completes successfully. |
| ERROR | OUTPUT | BOOL | 1 when an error occurred. |
| STATUS | OUTPUT | WORD | Detailed error/status code (see Section 7). |
| SD_1, SD_2, SD_3, SD_4 | INPUT | ANY | Optional additional source areas (4 max, total 160 bytes). |
| ADDR_1 … ADDR_4 | INPUT | ANY | Matching destination areas. |
Note: The instance DB is created automatically by STEP 7/TIA when SFB15 is dropped into an OB. A user-defined DB with the same number is not permitted.
4. ANY Pointer Syntax: The Core of the Problem
The ANY pointer is a 10-byte Siemens-specific data type that identifies a memory area, its starting offset, and its length. The literal notation in STL/SCL is:
P#[Area][Byte address][.Bit offset] [Data type] [Length]
Format components in detail:
| Component | Valid values | Notes |
|---|---|---|
| Area prefix | P# (mandatory literal) | Indicates pointer-typed literal. |
| Area identifier | DBx, M, I, Q, T, C, L, P (S7-400 only) | DBx is most common; use the absolute DB number. |
| Byte address | Integer ≥ 0 | Byte offset within the area. |
| Bit offset | 0–7 | For BYTE/WORD/DWORD transfers the bit offset must be 0. The address tag is DBX for bit/byte, DBW for word, DBD for double word. |
| Data type | BOOL, BYTE, WORD, DWORD, INT, DINT, REAL, etc. | Specifies the element width used for the length count. |
| Length | Integer | Number of elements of the declared type, not number of bytes. |
4.1 Sending 10 Bytes (the Original Question)
The original syntax P#DB1.DBX0.0 BYTE 10 is structurally valid: 10 bytes starting at byte 0 of DB1, bit offset 0. The matching destination on the partner CPU is identical:
SD_1 := P#DB1.DBX0.0 BYTE 10; // local source: 10 bytes from DB1
ADDR_1 := P#DB4.DBX0.0 BYTE 10; // remote dest: 10 bytes into DB4
LEN := 10; // total bytes
If no syntax error is reported by the editor but no data is written, the cause is not the pointer itself — see Section 6 for the actual reasons.
4.2 Sending 5 Words (16-bit elements)
The element type changes from BYTE to WORD. A word is 2 bytes, so 5 words = 10 bytes. The address tag must be DBW and the bit offset must be 0:
SD_1 := P#DB1.DBW0 WORD 5; // CORRECT: 5 words from DBW0 of DB1
ADDR_1 := P#DB4.DBW0 WORD 5; // 5 words into DBW0 of DB4
Why P#DB1.DBW0.0 WORD 5 fails: in Siemens ANY-pointer syntax, the bit offset is appended to the address token, and the data-type token follows. The bit offset must be specified when the type is BOOL. For BYTE/WORD/DWORD the syntax DBW0 (no bit) or DBW0.0 are both valid, but they cannot be combined with a type token in a single literal the way the original author attempted. The compiler is strict: WORD is a width token, not a part of the address.
4.3 Sending 2 Double Words (32-bit elements)
SD_1 := P#DB1.DBD0 DWORD 2; // CORRECT: 2 DWords from DBD0 of DB1
ADDR_1 := P#DB4.DBD0 DWORD 2; // 2 DWords into DBD0 of DB4
2 DWORD = 8 bytes. The starting address is byte-aligned (.0 implied or explicit).
4.4 Mixed Type Summary Table
| Transfer goal | Length token | Address tag | Example literal | Bytes moved |
|---|---|---|---|---|
| 10 bytes | BYTE 10 | DBX | P#DB1.DBX0.0 BYTE 10 | 10 |
| 5 words | WORD 5 | DBW | P#DB1.DBW0 WORD 5 | 10 |
| 2 double words | DWORD 2 | DBD | P#DB1.DBD0 DWORD 2 | 8 |
| 3 real numbers | REAL 3 | DBD | P#DB1.DBD0 REAL 3 | 12 |
| 1 boolean | BOOL 1 | DBX | P#DB1.DBX0.0 BOOL 1 | 1 bit, padded to 1 byte |
5. Step-by-Step Configuration
5.1 STEP 7 V5.x (S7-300/400)
- Open NetPro and double-click the CPU on the Ethernet subnet to add a new S7 connection. Set the local CPU as the active partner, the remote CPU as the passive partner, and enter the partner IP address.
- Note the local connection ID (e.g., 1) shown in the connection table — this is the value to wire to
IDon SFB15. - On the partner CPU, in CPU Properties → Communication, enable the S7 communication checkbox and the PUT/GET access option (F-CPU and newer S7-300 CPUs require this).
- Create the source and destination DBs in each project's S7 program, declaring the full data length you will transfer.
- Drop SFB15 (PUT) into OB1. STEP 7 will offer to create the instance DB automatically — accept.
- Wire the inputs as shown in Section 4. Latch the REQ input with a rising-edge bit if you want a one-shot trigger.
- Compile, download to both CPUs (including the connection configuration), and run.
5.2 TIA Portal (S7-1200/1500)
- Open Devices & Networks, select the Ethernet interface of the local CPU, and create an S7 connection to the partner.
- In the CPU properties of the partner, open Protection & Security → Connection mechanisms and enable Permit access with PUT/GET communication from remote partner.
- Open the program block (e.g., OB1) and insert PUT from the "Communication → S7 Communication" palette. TIA generates the instance DB.
- Fill the
REQ,ID(select from the dropdown of configured connections),SD_1, andADDR_1fields using the exact pointer syntax from Section 4. - Compile and download both stations.
6. Why the Original Program Did Not Write Data
With a structurally valid pointer and a clean STEP 7 compile, the remaining failure modes are almost always one of the following:
| # | Root cause | Symptom | Fix |
|---|---|---|---|
| 1 | Connection not configured / not downloaded | ERROR=1, STATUS=W#16#8302 (no connection or wrong ID) | Download the NetPro/TIA connection table to the local CPU; verify the ID matches the value wired to the block. |
| 2 | PUT/GET access disabled on partner | STATUS=W#16#8304 (access error) | Enable "Permit access with PUT/GET" in partner CPU properties. |
| 3 | Partner CPU in STOP / rack fault | STATUS=W#16#8301 (parameter error) or transport-level reset | Check partner diagnostic buffer; bring CPU to RUN. |
| 4 | Destination DB too short | STATUS=W#16#8301 (object does not exist / wrong length) | Lengthen DB4 on PLC2 to cover the full ADDR_1 range. |
| 5 | Wrong partner IP or rack/slot | STATUS=W#16#8302 (connection failure) | In NetPro/TIA, set the partner IP, rack 0, slot 1 (S7-300) or slot 2/3 (S7-400). |
| 6 | RElD mismatch (only matters for unsolicited PUT after GET pairing on the same connection) | STATUS=W#16#0007 on S7-1200/1500 (no matching R_ID) | Use the same connection for both directions or assign distinct R_IDs and update both partners. |
| 7 | Instance DB overwritten by user code | Status values appear random | Do not write to the SFB15 instance DB; let the block manage it. |
To localise the cause, monitor the STATUS output of SFB15. A full STATUS code table is in the online help of STEP 7 under "SFB15 PUT — STATUS parameter." See also the TIA Portal reference at GET and PUT (Read and write from a remote CPU).
7. Status Code Quick Reference
| STATUS (hex) | Meaning | Action |
|---|---|---|
| W#16#0000 | No error | — |
| W#16#0001 | Communication problems (partner unreachable, timeout) | Check cabling, IP, partner RUN state. |
| W#16#0007 | R_ID mismatch / R_ID not configured | Verify the connection on the passive side accepts the active partner's R_ID. |
| W#16#0081 | Data length 0 or invalid | Recheck LEN and the length encoded in the ANY pointer. |
| W#16#8301 | Object on partner does not exist or is too short | Enlarge the destination DB on the partner. |
| W#16#8302 | Connection does not exist / wrong ID | Re-download the connection configuration. |
| W#16#8304 | Access error / PUT/GET disabled | Enable PUT/GET access on the partner. |
| W#16#8402 | CPU in STOP | Bring partner CPU to RUN. |
| W#16#8500 | No resource / too many active jobs | Throttle REQ pulses; each connection can carry a limited number of simultaneous jobs. |
8. Commissioning Verification
- From the partner CPU's online view, open DB4 and confirm the data appears in the first 10 bytes after the first successful PUT call.
- Force a one-shot REQ from the active PLC, watch
DONEpulse for one cycle, and confirmERROR=0 andSTATUS=W#16#0000. - Set a watch table on the active CPU at the source DB. Toggle a few bytes; verify on the partner that the same bytes change within a single scan cycle of the SFB call.
- Repeat with the partner in STOP and confirm that the PUT still completes (PUT can write to a partner in RUN or STOP).
- For cyclic operation, drive REQ with a low-frequency timer (e.g., 100 ms) to avoid flooding the connection.
9. Common Pitfalls and Field Notes
-
Bit offset on word/dword addresses:
P#DB1.DBW0.0 WORD 5may compile in some TIA versions but is rejected in STEP 7 V5.x. UseP#DB1.DBW0 WORD 5for portability. -
Symbolic pointers: In TIA Portal, the
SD_1/ADDR_1input can be wired with a fully qualified DB tag. The compiler then synthesises the ANY pointer — no literal required. - Multiple SD_/ADDR_ pairs: SFB15 supports up to four disjoint source ranges per call. The combined byte count across all SD_i must equal LEN, and each pair must match in length.
- Optimised DBs on S7-1200/1500: When PUT/GET is used on an S7-1200/1500 partner, the destination DB must be non-optimised (i.e., "Standard" or "Accessible from HMI/OPC UA") with the "Disable optimised block access" attribute. Optimised block access is not addressable by absolute byte offsets.
- CPU firmware ≥ V4.x on S7-1200: PUT/GET with optimized DBs is supported only on S7-1200 V4.0 and higher; older firmware requires the same non-optimised layout.
- Maximum payload: 160 bytes per PUT/GET call. For larger transfers, use multiple calls or switch to BSEND/BRCV.
- Connection resources: Each configured S7 connection consumes one connection resource on both CPUs. S7-300 has a small pool (typically 4–16); S7-400 and S7-1500 are more generous.
10. Ladder-Logic Example (STEP 7 V5.x)
// One-shot trigger for PUT
NETWORK 1
A "StartPutTrigger" // Boolean from HMI or logic
FP "PUT_OneShot" // Edge memory bit
= "PUT_REQ"
NETWORK 2
CALL "PUT" , "PUT_DB"
REQ := "PUT_REQ"
ID := W#16#1 // Connection ID from NetPro
SD_1 := P#DB1.DBX0.0 BYTE 10
ADDR_1:= P#DB4.DBX0.0 BYTE 10
LEN := 10
DONE := "PUT_DONE"
ERROR := "PUT_ERROR"
STATUS:= "PUT_STATUS"
11. FAQ
Why does my PUT program compile cleanly but never write to the partner CPU?
Almost always a missing or non-downloaded S7 connection, or PUT/GET access disabled on the partner. Check STATUS for W#16#8302 (no connection) or W#16#8304 (access blocked). Verify the connection is in NetPro/TIA and has been downloaded to the local CPU.
What is the correct ANY pointer format to transfer 5 words from DB1 to DB4?
Use P#DB1.DBW0 WORD 5 on SD_1 and P#DB4.DBW0 WORD 5 on ADDR_1. The address tag is DBW (not DBX), the bit offset is 0, and the length token counts words, not bytes.
How do I send 2 double words with PUT?
Use P#DB1.DBD0 DWORD 2 for the source and P#DB4.DBD0 DWORD 2 for the destination, with LEN := 8. Double words are 32 bits; the starting address must be byte-aligned.
Why is P#DB1.DBW0.0 WORD 5 a syntax error?
The bit offset is part of the address token and is allowed on byte (DBX) addresses. For WORD and DWORD transfers the bit offset must be 0 and is typically omitted, so the literal is simply P#DB1.DBW0 WORD 5. Older STEP 7 V5.x editors reject DBW0.0 outright.
What is the maximum data length per PUT call?
160 bytes, including the combined length of all four possible SD_i/ADDR_i ranges. For larger transfers, switch to BSEND/BRCV on the same connection or use multiple PUT calls.
Do I need a separate connection for each PUT/GET direction?
No. A single configured S7 connection can carry multiple PUT and GET calls in both directions, provided each call uses the same local connection ID. For S7-1200/1500 PUT/GET, the R_ID on each call must match between the active and passive side.