Overview
S7 communication between two SIMATIC S7-1200 CPUs (for example, CPU 1214C / DC/DC/DC or CPU 1214C / DC/DC/Rly) using the PUT and GET instructions is the fastest way to share process data without adding third-party hardware. In a typical counter-aggregation application, CPU 1 publishes its running total and CPU 2 publishes its running total; each side reads the other's value and computes a grand total. Because PUT/GET runs over the integrated PROFINET interface, you need only a switched Ethernet cable (or a shared switch) and two IP addresses in the same subnet.
This tutorial walks through every parameter required to make two CTUD-based counters visible across the link, including:
- Hardware and firmware prerequisites
- Project setup and CPU protection / security settings
- Non-optimized versus optimized data blocks
- Creating the S7 connection in the network view
- Calling PUT and GET and mapping the
REQ,ID,ADDR_1,SD_1parameters - Adding the two CTUD values and verifying the result
Prerequisites
| Item | Requirement |
|---|---|
| Controller pair | 2× SIMATIC S7-1200 CPU, any 1211C / 1212C / 1214C / 1215C / 1217C variant with PROFINET interface |
| Firmware | V4.0 or later on both CPUs (V4.2+ recommended; V4.4 / V4.5 / V4.6 add granular access protection) |
| Engineering tool | STEP 7 Basic / Professional V15.1 or later in TIA Portal |
| Ethernet | Direct cable or common switch, both CPUs in the same IP subnet, e.g. 192.168.0.1 / 192.168.0.2, mask 255.255.255.0 |
| Counter blocks | At least one instance of IEC counter CTUD (IEC_Counter or IEC_Counter_0) in each CPU |
| Programmer access | PG/PC connected to either CPU, online access password known (default-protected CPUs reject PUT/GET calls) |
Step 1: Configure IP Addresses and Subnet
- Open the TIA Portal project containing both CPUs (or add the second CPU via Devices & networks > Add new device).
- Select each CPU in the device view and open Properties > PROFINET interface [X1] > Ethernet addresses.
- Assign:
- CPU_1: IP
192.168.0.1, subnet mask255.255.255.0 - CPU_2: IP
192.168.0.2, subnet mask255.255.255.0
- CPU_1: IP
- Tick Use router only if a router is present; for direct/subnet-local PUT/GET the router field stays empty.
Step 2: Enable PUT/GET Access on Both CPUs
Modern S7-1200 firmware ships with the connection mechanism blocked by default. Without enabling it, GET returns STATUS = 0x0001 (communication error) and PUT returns STATUS = 0x0001 as well.
- Select the CPU in the project tree.
- Open Properties > Protection & Security > Connection mechanisms.
- Tick Permit access with PUT/GET communication from remote partner (PLC, HMI, OPC, ...) on both CPUs.
- Compile and download to each controller.
Step 3: Create Non-Optimized Data Blocks
PUT/GET cannot dereference symbolic, fully-qualified optimized-block addresses. Each block that holds data exchanged over S7 communication must be created with Standard access (compatible with S7-300/400) — i.e. "Non-optimized".
- In the project tree, right-click Program blocks > Add new block > Data block (DB).
- In the block creation dialog, clear the tick on Optimized block access (Standard access is selected by default for new DBs but verify it).
- Declare at least one variable of type
DInt(32-bit signed, range -2 147 483 648 to 2 147 483 647) to hold the running total. UseDIntfor CTUD accumulation;Introlls over at ±32 767.
DATA_BLOCK "DB_CounterShare"
TITLE = 'Counter share published to partner'
AUTHOR : 'AutoGen'
VERSION : 0.1
NON_RETAIN
VAR
TotalCount : DInt; // running total exposed to the partner CPU
END_VAR
BEGIN
END_DATA_BLOCK
Repeat the procedure on the second CPU. The DB number must be unique within each CPU. There is no requirement for the numbers to match between the two controllers — the partner's DB number is passed into the ADDR_1 parameter of GET/PUT.
Step 4: Program the CTUD Counters and the Total
Insert an IEC counter in each CPU. The IEC CTUD function block in TIA Portal is found in Instructions > Basic instructions > Counter operations > IEC counter > CTUD. In each CPU, on every scan cycle, copy CV (current value) into the published DB:
// OB1 - CPU_1
"CTUD_1"(CU := I0.0, // count up input
CD := I0.1, // count down input
R := I0.2, // reset
LD := I0.3, // load preset (PV)
PV := 1000);
"DB_CounterShare".TotalCount := "CTUD_1".CV; // DInt copy, non-optimized DB
Now add the math that adds the local count to the remote count. Wait until the remote count has been refreshed — call GET before the add block:
// OB1 - CPU_1 - call PUT/GET in OB1 or a cyclic OB
// Local total plus remote total
"DB_GrandTotal".GrandCount :=
"DB_CounterShare".TotalCount + "DB_RemoteShare".RemoteCount;
Step 5: Create the S7 Connection in the Network View
- Open Devices & networks and select the Network view tab.
- Drag a line from CPU_1's PROFINET port to CPU_2's PROFINET port. TIA Portal will create an S7 connection automatically with the connection ID
1on the local CPU and a corresponding partner ID on the remote. - If you prefer to call PUT/GET with a manually managed ID, right-click the line and choose Properties > General > Connection ID — typical values are 100, 200, etc., to keep IDs stable across rebuilds.
- Confirm that the connection is established: the line in the network view turns solid green when online; an orange dashed line indicates "not yet downloaded / inconsistent".
Step 6: Call the GET and PUT Instructions
GET reads data from the partner into a local DB; PUT writes data to the partner. In TIA Portal, drop the GET and PUT instructions from Instructions > Communication > S7 communication into a cyclic OB (OB1 is acceptable for slow counters; OB30+ for deterministic 100 ms cycles).
Per the S7-1200 GET and PUT reference, the function block pinout is:
| Pin | Type | Meaning |
|---|---|---|
| REQ | Bool | Trigger edge — rising edge starts the job |
| ID | S7_CONNECTION | Connection ID from the network view (e.g. 1 or 100) |
| NDR (GET) / DONE (PUT) | Bool | New data received / write completed |
| ERROR / STATUS | Bool / Word | Error flag and detailed status word |
| ADDR_1..ADDR_4 | Variant / Remote | Pointer to the partner's data area (DB, bit memory, process image) |
| SD_1..SD_4 (PUT only) | Variant / Local | Source data on the local CPU |
| RD_1..RD_4 (GET only) | Variant / Local | Destination on the local CPU |
Calling GET to read the partner's counter share:
// OB1 - CPU_1 - read partner total into DB_RemoteShare
"GET_Remote"(REQ := TRUE, // run continuously
ID := 1, // connection ID from network view
ADDR_1 := P#DB2.DBX0.0 DINT 4, // partner DB2 starts at byte 0, length 4 bytes
RD_1 := "DB_RemoteShare".RemoteCount); // local destination
Calling PUT to send the local total to the partner:
// OB1 - CPU_1 - publish local total to partner DB3
"PUT_Local"(REQ := TRUE,
ID := 1,
SD_1 := "DB_CounterShare".TotalCount, // local source
ADDR_1 := P#DB3.DBX0.0 DINT 4); // partner destination
The pointer syntax P#DB<n>.DBX<offset>.<type> <length> is mandatory on the ADDR_x pins of PUT/GET when communicating with S7-1200 partners. The compiler rejects symbolic / fully-qualified addresses for the partner side because the partner's DB numbers are only known at runtime.
Step 7: Add the Two Totals
After the GET call, DB_RemoteShare.RemoteCount holds the partner's DInt. The grand total is then a single ADD instruction:
// OB1 - CPU_1
"DB_GrandTotal".GrandCount :=
DINT_TO_DINT("DB_CounterShare".TotalCount) +
DINT_TO_DINT("DB_RemoteShare".RemoteCount);
Cast only if you have changed the source variable type. DInt + DInt is intrinsically safe and avoids implicit type warnings.
Step 8: Verification
- Download both CPU programs.
- Go online with both CPUs (each in its own TIA Portal instance or by switching the online target).
- Force
CTUD_1.CU = TRUEon CPU_1 via the watch table and monitorDB_CounterShare.TotalCounton CPU_1 andDB_CounterShare.TotalCounton CPU_2 (after its PUT completes). - Monitor
DB_GrandTotal.GrandCounton both CPUs — both totals must match the sum of the two CTUD current values. - Inspect
GET_Remote.STATUSandPUT_Local.STATUS. A healthy job reportsSTATUS = 0x0000on each completed call.
PUT/GET Status Word Reference
| STATUS (hex) | Meaning | Typical fix |
|---|---|---|
| 0x0000 | Job complete, no error | — |
| 0x0001 | Communication error (link / connection / protection) | Check cable, IP, "Permit PUT/GET" flag, connection ID |
| 0x0081 | Remote CPU in STOP, or address invalid | Place remote CPU in RUN, validate pointer |
| 0x7000 | No job in progress (REQ low or first scan) | Set REQ TRUE; cycle the call |
| 0x7001 | First call / data accepted | Informational |
| 0x7002 | Subsequent call / data accepted | Informational |
| 0x8090 | Specified DB does not exist on the partner | Create the partner DB, recompile and download |
| 0x80A1 | Access protection (PUT/GET not permitted) | Enable the connection mechanism flag on partner CPU |
| 0x80B0 | Block is optimized and cannot be addressed | Switch DB to "Standard access (non-optimized)" |
| 0x80C0 | Address area mismatch / overlap | Match ADDR_1 length to RD_1 / SD_1 length |
| 0x80D0 / 0x80D1 / 0x80D2 | Length error / area error / number error | Check pointer syntax and area limits |
Troubleshooting Matrix
| Symptom | Likely cause | Action |
|---|---|---|
GET stays in STATUS = 0x7000
|
REQ never set TRUE, or call is in wrong OB | Verify call in cyclic OB; force REQ = TRUE |
GET reports 0x0001 / 0x80A1
|
PUT/GET not permitted on partner | Enable "Permit access with PUT/GET" on the partner CPU |
GET reports 0x80B0
|
Partner DB is optimized | Recreate partner DB with non-optimized access |
GET reports 0x8090
|
DB number on partner does not exist | Verify ADDR_1 points at the correct partner DB number; download partner program |
| Totals drift / count lost | DInt overflow (>2.1 billion counts) or wrong variable type | Use DInt or LInt; add modulo logic if cyclic reset is intended |
| Counter on partner not updating | PUT not called, or ADDR_1 points to wrong offset | Check partner DB declaration order — the first DInt starts at byte 0; offsets in the pointer must match |
| Connection dashed orange | Connection not downloaded | Compile and download both CPUs; the line should turn green online |
| Compiler error "optimized block cannot be used as remote address" | ADDR_1 references an optimized DB or its variable | Use a non-optimized DB on the partner and pass the absolute pointer |
Engineering Best Practices
- Always create a dedicated non-optimized DB for S7 communication. Mixing communication variables with program variables in an optimized DB is the most common commissioning delay.
- Use
DIntfor counters; reserve the sign for direction-of-counting (count-up positive, count-down negative) and letLInthandle roll-over for very long-running applications. - Trigger PUT/GET on a rising edge of a clock bit (e.g.
Clock_1Hzor a custom slow timer) when continuous refresh is not required — this reduces PROFINET load on larger projects. - Use the same connection ID for all PUT/GET calls between the same pair of CPUs. Each S7 connection supports parallel PUT and GET operations.
- Document the partner's IP address and DB numbers in the block header comment of the local GET/PUT call so future engineers can trace the data flow.
- On firmware V4.4 and later, consider assigning a separate "S7 communication" user in Users & roles and a per-connection password to satisfy hardening policies. PUT/GET itself does not authenticate, but the partner CPU's access protection does.
Alternative Approaches
| Mechanism | When to choose it |
|---|---|
| PUT/GET over S7 communication | Small data sets, no real-time determinism needed, simplest wiring, fewer CPUs (<8 partners) |
| Open User Communication (TSEND/TRCV / TCON) | TCP/UDP partner is a non-Siemens device or a PC; ISO-on-TCP for S7-300/400 partners |
| PROFINET iDevice / iSlave | Real-time cyclic data exchange with a higher-level controller; configuration effort justified only for motion or <1 ms update times |
| Modbus TCP (client/server blocks) | Partner is a third-party PLC or a SCADA system that already speaks Modbus |
| OPC UA server (S7-1500 only) | Modern, secure, model-based, browser/SCADA-friendly; not available on S7-1200 |
Frequently Asked Questions
Do both CPUs have to be the same firmware version for PUT/GET?
No. PUT/GET works between any combination of S7-1200 CPUs from V4.0 and S7-1500 CPUs from V1.5, provided both have "Permit access with PUT/GET" enabled. Mixed firmware V4.2 / V4.4 / V4.5 deployments are common in production.
Why does my GET return STATUS 0x80B0 even though the DB exists?
The DB exists but is configured with optimized access. PUT/GET requires absolute pointers such as P#DB2.DBX0.0 DINT 4, and optimized blocks hide their offsets behind symbolic names. Recreate the DB with "Standard access" (non-optimized) and recompile.
Can I trigger PUT/GET faster than once per OB1 scan?
Yes. Place the call in a cyclic interrupt OB (OB30 at 100 ms is typical) or trigger it on a clock-memory edge. Avoid running PUT/GET on every scan in a 1 ms OB — the partner's connection resources become the bottleneck long before the network does.
What is the maximum data size per PUT or GET call?
A single PUT or GET transfers up to 160 bytes; up to four address areas (ADDR_1..ADDR_4) can be combined per call. For larger blocks, use multiple PUT/GET instances in the same OB or switch to BSEND/BRCV (up to 64 KB per call).
Is PUT/GET encrypted?
No. S7 communication PUT/GET on S7-1200/1500 is unauthenticated and unencrypted on the wire. It is acceptable on trusted, physically controlled plant networks but should be replaced by OPC UA, S7-1500 secure communication, or a VPN when crossing IT/OT boundaries.