S7-1200 Bidirectional Communication with TSEND_C and TRCV_C

David Krause17 min read
S7-1200SiemensTechnical Reference
Licensed PE Working through this on a live machine? A Maine-licensed engineer can take it from here — included with IMD hardware, by the hour for everything else. Book an engineer

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).

Open User Communication refers to TCP, ISO-on-TCP, or UDP transport protocols managed by the application program. It is distinct from S7 communication, which uses the SIMATIC S7 protocol on top of ISO transport. Both are usable for S7-1200 to S7-1200 data exchange, but the open protocols offer more transparency for engineers coming from embedded C backgrounds.

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-control and plc-power respectively
PLC 1 - Control CPU 1214C DC/DC/DC 192.168.0.1 plc-control PLC 2 - Power CPU 1214C DC/DC/DC 192.168.0.2 plc-power CAT6 30 m PROFINET / TCP port 2000

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.

IDLE 16#7000 CONNECTING 16#7001 SENDING 16#7002 RECEIVING 16#7002 ERROR 16#80xx REQ ERROR Auto-retry on next REQ (CONT=1)

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 risk: If OB1 scan time is greater than the 100 ms clock period, you will miss send triggers. Verify by monitoring the OB1 scan time in the online diagnostics (right-click the CPU > Online & Diagnostics > Cycle time). Increase the clock period or move the call to a cyclic interrupt OB if necessary.

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:

  1. The internal TCP stack attempts retransmissions (default TCP timeout approximates 30 s, firmware dependent).
  2. On timeout, ERROR rises and STATUS returns 16#80A1 (connection error).
  3. When the cable is re-inserted, the next rising edge on REQ triggers a fresh TCON attempt.

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:

  1. 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.
  2. Download to both CPUs: TIA Portal > Online > Download to device. Confirm the IP address and PROFINET name are correct on the target.
  3. Watch table: Create a watch table with the send DB, receive DB, and the DONE/BUSY/ERROR/STATUS outputs of TSEND_C and TRCV_C. Force the clock bit and observe DONE toggling once per cycle.
  4. Toggle inputs: In the control cabinet, press each button. The corresponding DB_ControlToPower byte should change, and the input should appear in the partner's receive DB.
  5. 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.
  6. Cable pull test: Disconnect the LAN cable at PLC 1. Verify ERROR rises and the CommError flag goes true. Reconnect and verify the link recovers within the configured timeout without a CPU restart.
  7. Check PROFINET LEDs: The LINK LED on X1 should be solid green when the cable is connected, off when disconnected.
  8. 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_C never 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.

Back to blog