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.
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:
- 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. - Insert TSEND_C with the same
IDand matchingCONNECTparameter block. - Gate the
REQinput of TSEND_C with theDONE/ERRORstatus of TRCV_C and the connection status bit of TSEND_C (STATUS = 7000means idle/connected,7002means the previous job is complete). TheREQinput must not be pulsed before the connection is fully established (STATUS = 7000after the first successful connect). - If the remote device drops, both blocks report
STATUS = 80C5(connection terminated) or80C4depending on the cause. Clear theREQline and call a newREQedge 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
-
Insert the blocks. In the project tree open Program blocks > Main (OB1). Drag
TSEND_Cfrom Instructions > Communication > Open user communication into a network. Repeat forTRCV_C. TIA Portal auto-generates the instance DBs (InstDB_TSEND_C,InstDB_TRCV_C) and aTCONparameter DB on first use. - 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 |
-
Wire the receive path. Set
EN_Rof TRCV_C to TRUE constantly. Create a receive DB (e.g.DB_RCV) with at least 8192 bytes. PointDATAof TRCV_C toDB_RCV. Decide between:
-
Fixed length (ADHOC = FALSE):
LENdefines 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.
-
Wire the send path. Set
CONT= TRUE to keep the socket open between calls. SetLENeither explicitly to the byte count or to 0 if the length is stored in theDATApointer (withSENDLENdefined). Latch a rising edge onREQonly after the connection has been established. -
Apply connection gating. A minimal ST snippet to gate
REQon 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;
-
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 |
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_LENin 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
-
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 = 7000and the embedded TCON completes (you can monitor it in the instance DB'sCONNECTstructure). 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. -
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
ActiveEstablishedflag is wrong. -
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, andRCVD_LENand update them continuously. - 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:
-
PUTwrites the partner's DB/M/inputs/outputs; DONE = success. -
GETreads 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 = TRUEon TSEND_C if the conversation is request/response, otherwise the socket closes on every DONE and the partner must reconnect. - Never store the
REQsignal 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.