Configuring TSEND_C and TRCV_C on S7-1200 for TCP Communication

David Krause13 min read
S7-1200SiemensTutorial / How-to
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

The TSEND_C and TRCV_C instructions implement TCP/IP Open User Communication (OUC) on the integrated PROFINET interface of a SIMATIC S7-1200 CPU. The pair handles both the transport connection (via an embedded TCON/TDISCON) and the data transfer, replacing the legacy combination of TCON + TSEND + TRCV + TDISCON. Per the official Siemens S7-1200 manual collection, the minimum payload is 1 byte and the maximum payload is 8192 bytes per call. The instructions are firmware-agnostic for OUC but require the latest instruction library version installed in TIA Portal for the connected CPU firmware.

Common use cases include:

  • Communicating with a third-party device such as a Balluff BIS M RFID processor, vision sensor, scanner, or printer over TCP.
  • Building a request/response protocol with an external server (HTTP, ASCII, binary, Modbus TCP, etc.).
  • Exchanging data with a non-Siemens controller when PUT/GET is not available.
Note: For S7-1200 to S7-1200/1500 communication, prefer PUT/GET (S7 communication) instead of OUC. PUT/GET directly accesses the partner's memory and gives you a single DONE/NDR acknowledgement that confirms data was written to/retrieved from the peer. The OUC path is more CPU-efficient on the wire but requires application-level acknowledgement logic.

Prerequisites

Item Requirement
CPU S7-1200 (any firmware V4.x or V5.x for the OUC instruction set; verify the TIA Portal instruction library version matches the CPU)
PROFINET interface Integrated port of the CPU (CM/CP modules use the same blocks but separate connection IDs)
Programming software TIA Portal V16 or newer recommended; older projects may use V13/V14 with the legacy instruction library
Network Static IPv4 addresses on both endpoints; OUC does not work over DHCP on the CPU side without prior assignment
Partner Known IP address and TCP port number; explicit client/server role determined on both ends
Connection ID range 1 – 4095; must be unique per active connection
Block instance DBs Auto-generated by TIA Portal the first time the block is inserted; do not delete them

Architecture: One Connection, Two Blocks

A persistent TCP connection is described by a single Connection ID, an IP address, a remote port, and a local port. The TSEND_C and TRCV_C instructions on the same S7-1200 do not each open their own socket. They share a single TCON-managed connection if the CONNECT parameter is identical and the same ID is used at both blocks.

Field-proven practice: assign the same Connection ID to the send and receive blocks when the partner is the same device, the same IP, the same port, and the same protocol (TCP). The ID range 1 – 4095 is shared; using one ID for both directions is permitted and avoids exhausting the connection table on a CPU with limited OUC resources (small S7-1200 CPUs allow only 8 active OUC connections on the PROFINET interface).

The recommended control sequence is:

  1. Insert TRCV_C first and let it free-run. With EN_R = TRUE, TRCV_C is permanently listening and pulls any data that arrives into the receive DB.
  2. Insert TSEND_C with the same ID and matching CONNECT parameter block.
  3. Gate the REQ input of TSEND_C with the DONE/ERROR status of TRCV_C and the connection status bit of TSEND_C (STATUS = 7000 means idle/connected, 7002 means the previous job is complete). The REQ input must not be pulsed before the connection is fully established (STATUS = 7000 after the first successful connect).
  4. If the remote device drops, both blocks report STATUS = 80C5 (connection terminated) or 80C4 depending on the cause. Clear the REQ line and call a new REQ edge once the partner is reachable again; the embedded TCON will re-establish the socket.

Block Parameters Reference

Parameter TSEND_C TRCV_C Description
REQ BOOL, rising edge Start a send job on a 0→1 transition
EN_R BOOL Enable receive; tie to TRUE for free-running reception
CONT BOOL 1 = keep connection open after DONE; 0 = disconnect
LEN UINT, 0 = send from DATA via length prefix in DATA area Number of bytes to send (1 – 8192)
DATA VARIANT pointer VARIANT pointer Source (send) / destination (receive) area
ADHOC BOOL If TRUE, the length is taken from the first 2 bytes of received data; if FALSE, the length is fixed at LEN
ID WORD, 1 – 4095 WORD, 1 – 4095 Reference to the connection defined in CONNECT
CONNECT TCON_IP_v4 / TCON_IP_RFC Same Connection description DB; both blocks must reference an identical one
DONE BOOL, 1 cycle Send completed without error
NDR BOOL, 1 cycle New data received
BUSY BOOL BOOL 1 while the job is in progress
ERROR BOOL, 1 cycle BOOL, 1 cycle 1 on error; clear by re-triggering REQ
STATUS WORD WORD Detailed status; see table below

Step-by-Step Configuration in TIA Portal

  1. Insert the blocks. In the project tree open Program blocks > Main (OB1). Drag TSEND_C from Instructions > Communication > Open user communication into a network. Repeat for TRCV_C. TIA Portal auto-generates the instance DBs (InstDB_TSEND_C, InstDB_TRCV_C) and a TCON parameter DB on first use.
  2. Configure the connection description. Open the TCON DB and set:
Field Value (S7-1200 as TCP client example)
InterfaceId 64 (decimal) for the CPU's PROFINET interface; value 64 is for port 1 of the local interface
ID 1 (must match the ID input of both blocks)
ConnectionType 16#0B = TCP (10 = ISO-on-TCP, 11 = TCP, 12 = UDP)
ActiveEstablished TRUE if the S7-1200 initiates the socket; FALSE if the partner initiates
RemoteAddress IPv4 of the partner, e.g. 192.168.0.50
RemotePort Partner's listening port, e.g. 2000
LocalPort 0 = assign any free local port; set explicitly if a firewall requires it
  1. Wire the receive path. Set EN_R of TRCV_C to TRUE constantly. Create a receive DB (e.g. DB_RCV) with at least 8192 bytes. Point DATA of TRCV_C to DB_RCV. Decide between:
  • Fixed length (ADHOC = FALSE): LEN defines the expected packet size. Use this when the partner sends constant-size frames.
  • AdHoc mode (ADHOC = TRUE): The first two bytes of the received frame are interpreted as the length (big-endian UINT). Use this when the partner sends variable-length frames, e.g. a Balluff RFID with responses that vary in size. Allocate the receive area for the largest expected frame; the block reads the actual length and updates RCVD_LEN.
  1. Wire the send path. Set CONT = TRUE to keep the socket open between calls. Set LEN either explicitly to the byte count or to 0 if the length is stored in the DATA pointer (with SENDLEN defined). Latch a rising edge on REQ only after the connection has been established.
  2. Apply connection gating. A minimal ST snippet to gate REQ on connection status:
// TRCV_C permanently listening
InstDB_TRCV_C(EN_R := TRUE, ID := 1, ADHOC := TRUE, LEN := 0, DATA := P#DB_RCV.DBX0.0 BYTE 8192);

// Gate TSEND_C REQ on the receive instance being idle and previous send DONE/ERROR
"tag_Send_OK" := InstDB_TRCV_C.NDR OR InstDB_TRCV_C.ERROR;
IF (InstDB_TSEND_C.STATUS = 16#7000) AND "tag_Send_OK" THEN
    InstDB_TSEND_C(REQ := "tag_Trigger", ID := 1, LEN := "tag_Len",
                   DATA := P#DB_SND.DBX0.0 BYTE "tag_Len", CONT := TRUE);
END_IF;
  1. Compile and download the program and the connection description DB to the CPU. Go online and watch the instance DBs; the connection must show STATUS = 7000 (idle, connected) before the first send.

Status Code Reference

STATUS (hex) Meaning Recommended Action
7000 No job in progress / connection idle Safe to start a new send job
7001 Job processing (send or receive in progress) Wait; do not retrigger REQ
7002 DONE — send job complete, data is in the partner's TCP receive buffer This is not an application-level acknowledgement that the partner retrieved the data
7006 Data is currently being received (TRCV_C intermediate state) Wait; check RCVD_LEN for partial size when NDR becomes true
80C5 Connection terminated by remote or by local TDISCON Clear REQ, re-arm send after reconnection, verify partner is online
80C4 Connection could not be established (partner unreachable, wrong port, firewall) Verify IP, port, ActiveEstablished flag, and physical link
80A1 Connection or port already in use Pick a different local port or close a duplicate socket on the CPU
8085 LEN parameter or ADHOC header out of range Verify 1 ≤ LEN ≤ 8192; ADHOC header is big-endian UINT
Critical: DONE on TSEND_C means the bytes have left the CPU and entered the partner's TCP receive buffer. It does not mean the partner's application read them. To obtain an application-level acknowledge, the partner must send a reply and TRCV_C must report NDR with the expected reply bytes.

AdHoc Mode Deep Dive

AdHoc mode is the most common cause of "TRCV_C stays at 7006 forever" or "I receive the first two bytes of every frame as length and the rest of the data looks wrong." The contract is:

  • The partner prepends a 2-byte big-endian length to every TCP segment it sends.
  • TRCV_C strips the length prefix, copies the payload into the receive DB, and writes the actual number of bytes into RCVD_LEN in the instance DB.
  • The receive DB must be sized to the largest single frame the partner will send, not to the cumulative stream size.

If the partner does not send a length prefix, AdHoc must be FALSE and LEN must match the partner's fixed frame size. Mixing the two is the leading cause of garbage on the receive side.

Verification Procedure

  1. Loopback test with Hercules. Install Hercules SETUP utility on a Windows PC. Set it to TCP Server on the partner IP/port. Connect the S7-1200 to the PC. Confirm the CPU's TCON DB reports STATUS = 7000 and the embedded TCON completes (you can monitor it in the instance DB's CONNECT structure). Send a known ASCII string from the PLC with TSEND_C; verify Hercules echoes it. Send a reply from Hercules; verify TRCV_C reports NDR and the bytes land in the receive DB at the expected offset.
  2. Wireshark confirmation. Capture the link on a managed switch with port mirroring. You should see the TCP three-way handshake from the active side, the data segments, and the FIN on disconnect. If no SYN appears, the CPU is not initiating — the partner is the active side and the S7-1200 ActiveEstablished flag is wrong.
  3. Online watch on instance DBs. In TIA Portal go online, open the TSEND_C and TRCV_C instance DBs, and add the instance DB to a watch table. Right-click on STATUS, BUSY, ERROR, NDR, and RCVD_LEN and update them continuously.
  4. Partner diagnostic tool. Most third-party devices (Balluff BIS M, Cognex, Keyence, Sick, etc.) ship with a Windows configuration tool that lets you exercise the TCP socket and the application protocol independently. Always verify the partner first; if the partner diagnostic cannot talk to itself or to a known good client, the problem is not the PLC.

Troubleshooting Matrix

Symptom STATUS pattern Likely cause Fix
TSEND_C alternates 7000 → 7002 → 80C5 every few seconds Connection repeatedly drops Partner closes the socket after each message, or firewall idle-timeout Set CONT = TRUE on TSEND_C; verify partner keeps the socket open; check firewall/IDS
TRCV_C stuck at 7006 with no data in DB Receive never completes AdHoc mismatch: partner sends fixed-length frame, AdHoc is TRUE and waits for length prefix Switch ADHOC to FALSE and set LEN to the partner's exact frame size, or fix the partner to prepend a length
STATUS = 80C4 repeatedly No connection Wrong IP, wrong port, or partner is server and S7-1200 is also configured as server Toggle ActiveEstablished; verify port with netstat on partner
DONE on TSEND_C, but NDR never fires on TRCV_C 7000 on both, no NDR Partner is not sending a reply, or reply is on a different connection Capture with Wireshark; verify partner's reply path
First send works, second send returns ERROR with 8085 7002 → 80C5 → 8085 LEN larger than the area pointed to by DATA Resize DB_SND or use 0 for LEN with explicit length in DATA
STATUS alternates 7001, 7002, 80C5 on TRCV_C Two connect attempts, two failures Duplicate instance DBs or two blocks with the same ID; or partner rejects the SYN Audit all TCON DBs; ensure exactly one block owns each Connection ID
No NDR, no error, RCVD_LEN = 0 7006 forever Partner behind NAT or partner firewall drops unsolicited inbound Open the partner's firewall rule; put both endpoints in the same subnet during commissioning

Connection ID and Port Sizing

Each S7-1200 CPU has a hard limit on simultaneous OUC connections on the integrated PROFINET port. Smaller CPUs (CPU 1211, 1212) allow 8 active OUC connections; CPU 1214 and 1215 allow 16. The 4-byte ID field accepts values 1 – 4095 and must be unique per active connection regardless of protocol. UDP, TCP, ISO-on-TCP, and SMTP each count against the budget.

For local port selection: setting LocalPort = 0 asks the CPU's TCP/IP stack to bind any free ephemeral port (typically 49152 – 65535 on Windows-style stacks; Siemens uses similar ranges). If your firewall expects a specific source port for outbound rules, set it explicitly to a value outside the ephemeral range — common practice is 2000, 2500, 3000 to keep firewall rules readable.

PUT/GET vs. Open User Communication

For S7-1200 to S7-1200/1500 data exchange, PUT/GET (S7 communication) is almost always the better choice because the protocol carries an application-level acknowledgement:

  • PUT writes the partner's DB/M/inputs/outputs; DONE = success.
  • GET reads the partner's areas; NDR = success and data valid in the receive DB.
  • No TCP socket setup, no ADHOC flag, no LEN arithmetic.

The trade-off is throughput: PUT/GET adds protocol overhead and limits job size to the S7 protocol PDU size (typically 480 bytes in older CPUs, 960 bytes in newer ones). For a 1 Hz status exchange of 50 bytes, that overhead is irrelevant. For a 1 kHz streaming of 8 KB blocks, OUC with TSEND_C/TRCV_C is materially faster on the wire, but you must implement the application-level ack yourself.

Field-Proven Tips

  • Always let TRCV_C free-run with EN_R = TRUE. Trying to call TRCV_C conditionally to "save CPU" is a common source of lost frames — the TCP buffer is finite and the partner will retransmit only if its protocol does retries; many do not.
  • Use one Connection ID per partner, not per direction. The direction is selected inside the same TCON by the send/receive role of the block.
  • Set CONT = TRUE on TSEND_C if the conversation is request/response, otherwise the socket closes on every DONE and the partner must reconnect.
  • Never store the REQ signal in a latch. Use a single-cycle pulse from a rising-edge detector or a state machine; otherwise TSEND_C re-sends on every scan.
  • For a third-party partner, obtain the exact framing specification (length prefix? delimiter? fixed length? binary or ASCII?). Guesswork on framing is the #1 cause of "I see only the first two bytes and then garbage."
  • For the Balluff BIS M family, the parameter protocol uses fixed-length ASCII frames with a CR/LF terminator; disable AdHoc and set LEN to the documented frame size, or use a small custom parser that scans for the terminator inside a 1024-byte rolling window.

FAQ

Do TSEND_C and TRCV_C need to be active at the same time on an S7-1200?

Yes, for a persistent TCP connection to a third-party device, call TRCV_C with EN_R = TRUE on every cycle and call TSEND_C with REQ rising-edge when you have data to send. They share a single TCON connection when the ID and CONNECT parameter block are identical, so there is exactly one socket between the CPU and the partner.

What does TSEND_C status 7002 actually mean?

7002 = DONE. The bytes have been transmitted to the partner's TCP receive buffer. It is not an application-level acknowledgement that the partner's program has read or processed the data; you need the partner to send a reply and TRCV_C to report NDR for that.

Why does TRCV_C stay at status 7006 and never produce NDR?

Most often an AdHoc mode mismatch: the partner is sending fixed-length frames but the block is configured for AdHoc = TRUE and is waiting for a 2-byte length prefix. Either set ADHOC = FALSE and LEN to the partner's exact frame size, or modify the partner to prepend a big-endian UINT length to every message.

What is the maximum payload per TSEND_C/TRCV_C call?

1 byte minimum, 8192 bytes maximum. For longer streams you must fragment on the application side and reassemble in a DB.

Should I use PUT/GET or TSEND_C/TRCV_C between two Siemens PLCs?

Use PUT/GET. It provides an application-level DONE/NDR acknowledgement in a single block call, requires no socket setup, and is supported on every S7-1200/1500 firmware. Choose TSEND_C/TRCV_C only when the partner is a non-Siemens device or you need higher wire throughput for large streaming blocks.

Back to blog