Overview
Two SIMATIC S7-1200 CPUs installed in separate cabinets 30 m apart can exchange process data in both directions over the integrated PROFINET interface (port X1) without any additional hardware. The most flexible approach is the Open User Communication profile using TSEND_C and TRCV_C instructions in TIA Portal. Each of these blocks bundles three lower-level functions: TCON (connection setup), the data transfer itself, and TDISCON (connection termination). Bidirectional data flow is achieved by calling TSEND_C/TRCV_C in both CPUs, with each side playing the role of a passive listener and an active initiator on separate connection IDs.
This pattern is the same one published in the official Siemens application example CE-X17 - Ethernet communication between S7-1200 CPUs, and the implementation steps mirror the procedure described in the SIMATIC S7-1200 Programmable Controller System Manual. The cycle time and link monitoring guidance also aligns with the S7-1200 Easy Book, available at S7-1200 Easy Book (PDF).
Prerequisites, Hardware, and Network Configuration
Before commissioning, gather the following items and confirm versions:
| Item | Specification | Reference |
|---|---|---|
| CPU 1 (control cabinet) | CPU 1214C DC/DC/DC, FW V4.4 or later | Siemens 6ES7214-1AG40-0XB0 |
| CPU 2 (power cabinet) | CPU 1214C DC/DC/DC, FW V4.4 or later | Siemens 6ES7214-1AG40-0XB0 |
| Engineering software | TIA Portal V17 or V18 with S7-1200 support package | TIA Portal V18 download |
| Network cable | CAT6, 30 m, RJ45, with industrial-grade jacket | PROFINET Conformance Class B |
| Function blocks | TSEND_C / TRCV_C / TCON from standard library | Instructions > Communication > Open User Communication |
| Functional ground | Functional earth connected on both PLCs to cabinet PE | System Manual, Section 4.4.2 |
Any S7-1200 CPU with firmware V4.0 or later supports the TSEND_C / TRCV_C instructions. The newer the firmware, the more connection resources and the broader the error diagnostics, so prefer V4.4+ in new installations. The X1 PROFINET interface supports 100 Mbit/s, full duplex, with auto-negotiation and Auto-MDIX, so either a direct crossover cable or a switch can be used. A switch is recommended if any future PROFINET IO devices or HMIs will join the line.
Recommended IP plan:
- CPU 1 (control):
192.168.0.1/255.255.255.0 - CPU 2 (power):
192.168.0.2/255.255.255.0 - No router, no gateway needed for the 30 m link
- PROFINET device name:
plc-controlandplc-powerrespectively
Protocol Selection
Four protocol options are available for CPU-to-CPU traffic on S7-1200. The trade-off table below summarises engineering trade-offs for a 30 m, two-CPU link.
| Protocol | Port | Setup | Use when |
|---|---|---|---|
| TCP | 2000 (user-defined) | Easy via TCON in TIA Portal | Simple point-to-point, mixed device vendors |
| ISO-on-TCP (RFC 1006) | 102 (default) | TSAP configured per partner | Native to SIMATIC, transparent debugging |
| UDP | 2000 (user-defined) | Connectionless, no retry | Low overhead, broadcast, last-value-wins |
| S7 (PUT/GET) | 102 | Configured as S7 connection | S7-to-S7 only, no buffer handling |
For the cabinet-to-cabinet case described in the source, both TCP and ISO-on-TCP are the most common choices. ISO-on-TCP aligns best with the rest of the SIMATIC stack and lets the same connection carry a length-prefixed payload, which simplifies framing. TCP is preferable when partners from different vendors are involved. For a one-CPU-active, one-CPU-passive setup, declare exactly one side as Active connection establishment = Yes in the connection descriptor; the other side stays passive to avoid simultaneous-open race conditions on the TCP socket.
TIA Portal Configuration and Data Block Design
Each side needs a Connection descriptor in the project tree under Devices & Networks > Connections. The TIA Portal wizard creates a TCON parameter data block automatically when you drag a TSEND_C block into the editor. The connection descriptor parameters are:
| Field | CPU 1 (active) | CPU 2 (passive) |
|---|---|---|
| Connection type | TCP | TCP |
| Partner IP | 192.168.0.2 | 192.168.0.1 |
| Partner port | 2000 | 2000 |
| Local port | 2000 | 2000 |
| Connection ID | 1 | 1 |
| Active connection establishment | Yes | No (waits for partner) |
The Connection ID is a WORD with range 1-4095. Each TSEND_C/TRCV_C pair on a given CPU must reference a unique ID. For bidirectional traffic, allocate two IDs: 1 for the send direction, 2 for the receive direction. The convention used in CE-X17 is one ID per direction. The S7-1200 supports up to 16 Open User Communication connections on the PROFINET interface, shared between TSEND_C, TRCV_C, TMAIL_C, and other open blocks.
Group all process data that will travel in a single direction into a dedicated global DB. The DB must be a non-optimised block (select Attributes > Optimised block access = off) for TSEND_C to address it with an ANY pointer on firmware older than V4.4. From V4.4 onward, the symbolic VARIANT pointer is preferred and accepts optimised blocks.
Sample data layout for the control cabinet (PLC 1 sends 4 bytes, PLC 2 sends 6 bytes):
// PLC 1 -> PLC 2 (control cabinet to power cabinet)
DATA_BLOCK "DB_ControlToPower"
STRUCT
bButtons : BYTE; // %I0.0 - %I0.7 physical pushbuttons
bCommands : BYTE; // start, stop, reset, jog
wSetpointPct : WORD; // 0-1000 (= 0.0-100.0 %)
bReserved : BYTE; // pad to even
END_STRUCT;
END_DATA_BLOCK
// PLC 2 -> PLC 1 (power cabinet to control cabinet)
DATA_BLOCK "DB_PowerToControl"
STRUCT
bStatusLights : BYTE; // %Q0.0 - %Q0.7 indicator lamps
bMotorState : BYTE; // running, fault, ready, etc.
wSpeedFbk : WORD; // 0-1000 (0.0-100.0 %)
bFaultCode : BYTE; // 0 = no fault
bHeartbeat : BYTE; // 0-255 toggling counter
END_STRUCT;
END_DATA_BLOCK
The maximum payload per TSEND_C call is 8192 bytes on firmware V4.x. The recommended payload for the application described is 16-64 bytes, which keeps latency in the low single-digit milliseconds range. The single memory location that represents the status of the partner PLC is, in practice, the receive DB: every successful TRCV_C call overwrites the receive buffer, and the rest of the program reads from that buffer as if it were local I/O.
TSEND_C and TRCV_C Block Reference
Two block families are available. The table below summarises the practical difference.
| Aspect | TSEND_C / TRCV_C | TSEND / TRCV |
|---|---|---|
| Connection handling | Builds and tears down automatically | Requires separate TCON / TDISCON calls |
| Code volume | Single call per direction | Three or more calls per direction |
| Suitable for | Bidirectional cyclic traffic | Custom state machines, low-level control |
| Error reporting | STATUS contains TCON, TSEND, TRCV codes | STATUS only reports the current function |
| First call behaviour | Establishes connection on first REQ | Assumes connection already exists |
| Coding effort | Low | Higher |
For most cabinet-to-cabinet applications, TSEND_C / TRCV_C are the correct choice. Use TSEND / TRCV only when the project demands custom connection reuse patterns, for example, when the same TCP socket is shared between multiple protocols.
The two blocks share most of their interface. The input/output declarations are:
| Pin | Direction | Type | Description |
|---|---|---|---|
| REQ | IN | BOOL | Rising edge triggers send |
| CONT | IN | BOOL | 1 = keep connection open after send |
| LEN | IN | WORD | Bytes to send; 0 = full DATA area |
| DONE | OUT | BOOL | 1 = send successful (one cycle) |
| BUSY | OUT | BOOL | 1 = send in progress |
| ERROR | OUT | BOOL | 1 = error occurred |
| STATUS | OUT | WORD | Status or error code |
| ID | IN/OUT | WORD | Connection ID from TCON configuration |
| DATA | IN/OUT | VARIANT | Pointer to send/receive buffer |
For TRCV_C the equivalents are EN_R (enable receive), ADHOC (accept any packet length), and the same DONE, BUSY, ERROR, STATUS outputs.
Program Structure and C-to-PLC Concept Mapping
For engineers transitioning from C to ladder/FBD, the S7-1200 execution model is best understood with a few translations:
| PLC concept | Closest C equivalent | Notes |
|---|---|---|
| OB1 (Main) | while(1) { main_loop(); } |
One scan = one iteration of the loop |
| OB30-OB38 (Cyclic Interrupt) | Periodic timer ISR | Configured in 1 ms to 60 s steps |
| FB (Function Block) | C++ class / function with state | Persistent instance DB |
| FC (Function) | Pure function | No internal state, no instance DB |
| Global DB | Global variable or singleton | Shared by all OBs and FBs |
| Instance DB | Object instance | Bound to one FB call |
| Startup OB100 |
main() boot path |
Runs once at power up |
| OB82 / OB86 | Hardware fault ISR | Runs on PROFINET failure |
A typical Main OB1 for a bidirectional communication node looks like this in FBD. Both TSEND_C and TRCV_C are placed in OB1; nothing about Open User Communication requires a separate block, which keeps the program readable for someone migrating from C.
// OB1 "Main" - segment 1: cyclic send trigger
M0.7 (100 ms clock bit) ----[RE]----(TSEND_C REQ)
"DB_Status".bHeartbeat := "DB_Status".bHeartbeat + 1
// OB1 "Main" - segment 2: write local inputs into send DB
"DB_ControlToPower".bButtons := IB0
"DB_ControlToPower".wSetpointPct := IW2
// OB1 "Main" - segment 3: copy received data into output area
QB0 := "DB_PowerToControl".bStatusLights
// OB1 "Main" - segment 4: receive enable
TRUE ----(TRCV_C EN_R)
Calling TSEND_C in OB1 with a 100 ms clock bit as REQ gives a deterministic send rate. The receive block TRCV_C is left enabled continuously; the user code is notified that fresh data has arrived by the rising edge of DONE, which can be used to latch process values. If the project needs to wait for a partner before proceeding, the pattern is a step chain in an FB: store the wait state in the instance DB, transition on DONE rising edge, and continue execution. This is the S7-1200 equivalent of a C state machine with switch(state) { ... }.
Cycle Time and Timing Analysis
The total time from an input change in PLC 1 to a corresponding output change in PLC 2 is the sum of four contributions:
T_total = T_scan_plc1 + T_send_plc1 + T_net + T_scan_plc2 + T_apply_plc2
Typical values for a 30 m link with 16-byte payload:
| Component | Symbol | Typical value | Notes |
|---|---|---|---|
| OB1 scan on sender | T_scan_plc1 | 5-30 ms | Depends on program size |
| TSEND_C execution | T_send_plc1 | 1-3 ms | Buffer copy + TCP send |
| Network transit | T_net | 0.3-1 ms | CAT6, 30 m, full duplex |
| OB1 scan on receiver | T_scan_plc2 | 5-30 ms | Same as sender |
| Application apply | T_apply_plc2 | < 1 ms | Move block to outputs |
| Worst case end-to-end | T_total | 15-65 ms | At default 100 ms cycle |
For the cabinet-to-cabinet control application, a 100-250 ms update is fully sufficient. If a faster response is required, move the TSEND_C call to a cyclic interrupt OB (e.g., OB35 at 50 ms) and use a high-priority hardware input interrupt to pre-stage the send buffer. The PROFINET update portion on the S7-1200 is hardware-accelerated and adds no measurable application-side latency for Open User Communication.
Detecting a Disconnected LAN Cable
The question raised in the source - "How do I detect if the LAN cable is pulled out so I can re-establish when the cable is put back?" - has multiple solutions. Each has different latency and CPU overhead.
Method 1: Rely on TSEND_C automatic recovery
With CONT = 1, TSEND_C keeps the connection descriptor alive. When the physical link is broken:
- The internal TCP stack attempts retransmissions (default TCP timeout approximates 30 s, firmware dependent).
- On timeout,
ERRORrises andSTATUSreturns16#80A1(connection error). - When the cable is re-inserted, the next rising edge on
REQtriggers a freshTCONattempt.
For faster detection, place a check in OB1: if ERROR is high, set a global CommError flag, log the timestamp, and call TDISCON followed by TCON explicitly to force re-handshake.
Method 2: PROFINET diagnostic interrupts
If the partner CPU is also configured as a PROFINET IO device, you can subscribe to OB82 (diagnostic interrupt) and OB86 (rack failure). The local STATUS word inside these OBs contains the slot number and event code. A typical drop event returns 16#393C for "station failure" and 16#38A4 for "link down".
Method 3: Application-level heartbeat
Add an 8-bit counter (bHeartbeat) in the payload that increments on every send cycle. The receiver compares the new value with the last one and sets HeartbeatOK if the difference is non-zero. If two consecutive cycles show the same value, declare link failure:
// PLC 2 evaluation
IF "DB_PowerToControl".bHeartbeat <> "DB_LastHeartbeat" THEN
"HeartbeatOK" := TRUE;
"HeartbeatTimeout" := 0;
ELSE
"HeartbeatTimeout" := "HeartbeatTimeout" + 1;
IF "HeartbeatTimeout" > 5 THEN
"HeartbeatOK" := FALSE;
END_IF;
END_IF;
"DB_LastHeartbeat" := "DB_PowerToControl".bHeartbeat;
With a 100 ms send rate, five missed cycles is a 500 ms window. This is faster than the TCP retransmission timeout and works for any protocol, including UDP.
Method 4: Read the PROFINET link state register
On S7-1200 firmware V4.4 and later, the IODeviceState system data block exposes the link status of every configured PROFINET device. The state word IODeviceState[1..n].State reads 0 = not configured, 1 = OK, 2 = fault. Poll this in OB1 with a 200 ms clock bit.
Status and Error Code Reference
The STATUS output of TSEND_C and TRCV_C returns standardised codes. The most important values:
| STATUS (hex) | Meaning | Recovery |
|---|---|---|
| 16#0000 | No error, idle | No action |
| 16#7000 | No job active | No action |
| 16#7001 | First call after REQ, connecting | Wait for completion |
| 16#7002 | Job running, data transfer active | Wait |
| 16#7003 | Connection terminated | Check CONT bit; re-trigger REQ |
| 16#80A1 | Partner not reachable | Check IP, port, cable |
| 16#80A2 | Connection error, partner terminated | Re-trigger REQ |
| 16#80B0 | Connection in use by another instance | Check duplicate ID usage |
| 16#80C3 | Temporary resource error | Retry, or check max connections |
| 16#80C4 | Connection terminated by partner | Re-trigger REQ |
| 16#80D0 | Connection terminated, no resource | Free instance DBs and retry |
| 16#8085 | LEN > SEND buffer size | Reduce payload, set LEN=0 for full |
| 16#80A3 | TCP connection failed, partner rejected | Check firewall, partner status |
Detailed explanations are available in the TIA Portal information system under Instructions > Communication > Open User Communication > TSEND_C. The list above is the subset most frequently encountered on a 30 m cabinet link.
Verification and Commissioning
Follow this checklist before putting the line into production:
- Online compile: After the project is built, right-click the CPU and select Compile > Hardware and Software (rebuild all). Errors in connection descriptors only show up here.
- Download to both CPUs: TIA Portal > Online > Download to device. Confirm the IP address and PROFINET name are correct on the target.
-
Watch table: Create a watch table with the send DB, receive DB, and the
DONE/BUSY/ERROR/STATUSoutputs ofTSEND_CandTRCV_C. Force the clock bit and observe DONE toggling once per cycle. -
Toggle inputs: In the control cabinet, press each button. The corresponding
DB_ControlToPowerbyte should change, and the input should appear in the partner's receive DB. - Time the round trip: Set a known value in PLC 1, watch for the change in PLC 2, and stamp the time using a high-resolution clock. The delay should be below 100 ms for the 100 ms clock configuration.
-
Cable pull test: Disconnect the LAN cable at PLC 1. Verify
ERRORrises and theCommErrorflag goes true. Reconnect and verify the link recovers within the configured timeout without a CPU restart. -
Check PROFINET LEDs: The
LINKLED on X1 should be solid green when the cable is connected, off when disconnected. - Review diagnostic buffer: Online & Diagnostics > Diagnostics buffer. The first and last communication error event will be logged with timestamp.
Realistic engineering tips from the bench:
- Configure clock memory bytes in CPU properties before the first download. The default is empty, and a forgotten clock bit is the most common reason a cyclic
TSEND_Cnever fires. - Always add an empty OB82 and OB86 to the program. If they are missing and a PROFINET device goes offline, the CPU goes to STOP. An empty OB82 or OB86 acts as a default handler and keeps the CPU in RUN.
- For long-term reliability in industrial cabinets, use M12 D-coded PROFINET cable with IP67 connectors rather than RJ45. The 30 m run is well within the 100 m segment limit, and the heavier cable is more tolerant of cabinet vibration.
- For deterministic latency, use a managed PROFINET switch (e.g., SCALANCE XC-200) instead of an unmanaged switch. Cut-through switching reduces jitter to below 10 microseconds.
- For cyber-security, enable access protection in the CPU security settings and restrict the web server to read-only. Also disable the PUT/GET server if not in use.
Troubleshooting Matrix
| Symptom | Likely cause | Diagnostic step | Fix |
|---|---|---|---|
| DONE never rises | REQ edge not detected, REQ tied to M0.7 but M0.7 not configured | Watch M0.7 in the online monitor | Enable clock memory in CPU properties > System & clock memory |
| STATUS = 16#80A1 on both sides | Subnet mismatch or firewall | Ping partner from TIA Portal > Online > Accessible devices | Set both PLCs on the same subnet, disable PC firewall during test |
| Receive DB always 0 | TCON active/passive mismatch or wrong port | Compare Connection ID and ports in both projects | Set one side active, one side passive, match the ports |
| Communication works then drops every 30 s | TCP keep-alive or socket reuse bug | Check diagnostic buffer for repeat events | Re-trigger REQ after ERROR; add TDISCON before next TCON |
| STATUS = 16#8085 | LEN > declared buffer | Inspect LEN input value | Set LEN = 0 to send the full declared area |
| Only one direction works | Duplicate Connection ID | Verify both directions use different IDs | Use ID=1 for send, ID=2 for receive on each CPU |
| Watch table shows wild pointer values | Optimised block access on send DB | DB properties > Attributes | Disable "Optimised block access" for any DB addressed by TSEND_C, or upgrade to V4.4+ for VARIANT support |
| CPU goes to STOP after cable pull | Unhandled OB86 | Check the diagnostic buffer | Add an empty OB86 to the program |
| Heartbeat never increments | OB1 not calling send block, or LEN=0 with empty DB | Watch DONE and BUSY flags | Move call to OB35 if OB1 scan is too long; verify DB has data |
| Latency much higher than expected | OB1 scan time > clock period | Check online & diagnostics cycle time | Increase clock period or move send to cyclic interrupt OB |
FAQ
How do I detect a disconnected LAN cable on S7-1200?
Monitor the ERROR output of TSEND_C for 16#80A1 (partner not reachable), subscribe to OB82/OB86 for PROFINET diagnostic events, or implement an application heartbeat counter in the payload. The fastest method is a heartbeat: a 1-byte counter in the send buffer increments each cycle, and the receiver raises a fault flag after 3-5 missed values, which at a 100 ms send rate is 300-500 ms of detection latency.
What is the main benefit of TSEND_C compared to TSEND?
TSEND_C integrates TCON, TSEND, and TDISCON into a single call, which reduces code volume and the number of instance DBs. The STATUS output of TSEND_C reports errors from any of the three sub-functions, simplifying diagnostics. For simple point-to-point traffic the time saved is substantial; for advanced state machines that need to keep the connection open across many operations, the lower-level TSEND/TRCV may be preferable.
Can I use UDP instead of TCP for S7-1200 to S7-1200 communication?
Yes. Use TUSEND and TURCV instructions for connectionless UDP traffic. UDP has lower overhead but no retransmission or sequencing, so it is suited to broadcast-style updates where the latest value matters more than guaranteed delivery. For the cabinet-to-cabinet control application described, TCP or ISO-on-TCP is the safer choice.
What cycle time should I use for the send trigger?
A clock memory bit set to 100 ms (M0.7) is a good starting point for button and status exchanges. The trigger must be slower than the OB1 scan time to avoid missing edges. If OB1 scan time exceeds 100 ms, increase the clock period to 200-500 ms or move the call to a cyclic interrupt OB (OB35 at 50 ms, for example).
Do I need a switch between two S7-1200 PLCs 30 m apart?
No, a direct CAT6 cable is sufficient because 30 m is well within the 100 m PROFINET segment limit and the S7-1200 X1 port supports Auto-MDIX. A switch is only needed if you plan to add HMIs, additional PLCs, or PROFINET IO devices to the same network.
Can the two PLCs be configured so that only one CPU initiates the connection?
Yes. Mark exactly one CPU as the active partner in the TCON configuration and leave the other as passive. The active side opens the TCP socket on the first REQ edge; the passive side listens on the configured port and accepts the incoming connection. This is the configuration used in Siemens application example CE-X17.