S7-1200 TSEND TRCV: Resolving TCP Receive Cycle Time Failures
The Siemens SIMATIC S7-1200 (CPU 1214FC, firmware V4.x) open user communication blocks TSEND, TRCV, and TCON frequently exhibit intermittent data loss when the receive polling interval is shorter than the OB1 cycle time of the user program. This article documents a field-verified remediation for a 100-byte TCP payload polled at 25 ms from a third-party controller (Lenze C300) into an S7-1200, with an OB1 scan of 23 ms, and explains why edge-triggering the REQ input combined with a cyclic interrupt OB eliminates the symptom without changing the physical transport.
• Send direction (S7-1200 → Lenze, 100 bytes / 10 ms via
TSEND): stable, no loss.• Receive direction (Lenze → S7-1200, 100 bytes / 25 ms via
TRCV): intermittent, partial payloads, STATUS word returns transient error codes.• Reducing Lenze transmit interval to 100 ms: stable but unacceptable for the motion loop.
• OB1 maximum cycle time: 23 ms (monitored via
_OB1_CYCL in the watch table).1. Problem Description
A SIMATIC S7-1200 CPU 1214FC (article number 6ES7214-1AF40-0XB0 or later variant) exchanges 100 bytes per direction with a Lenze C300 inverter/controller over PROFINET/Industrial Ethernet using the standard TCP transport from the SIMATIC open user communication library. TSEND is pulsed at 10 ms from OB1 with no observed loss. TRCV is configured with the REQ input permanently TRUE in the same OB1 pass. The Lenze controller transmits a new 100-byte frame every 25 ms. The following conditions are observed:
- Frame loss rates of 5–15 % during steady-state operation, depending on background process load.
- Status word
STATUSofTRCVintermittently returns W#16#80C9 (connection resource in use / resource temporarily unavailable) and W#16#8180 (connection not yet established / handshake error). - Increasing the Lenze transmit interval to 100 ms eliminates loss but violates the 10 ms motion-update requirement.
- No PROFINET diagnostic alarms are raised; the physical link (link LED, port statistics) remains fault-free.
The user's hardware and configuration layout is summarized below.
| Parameter | Value |
|---|---|
| PLC | SIMATIC S7-1200, CPU 1214FC (DC/DC/DC or AC/DC/RLY) |
| Firmware | V4.4 or later (T-CON block library V3.x or V4.x) |
| Counterpart | Lenze C300 controller (Ethernet/IP/TCP socket server) |
| Transport | TCP (RFC 793), IPv4, port 502 / custom |
| Payload per frame | 100 bytes (8192 byte maximum per SIMATIC block) |
| Send interval (S7) | 10 ms (OB1-driven) |
| Receive interval (Lenze) | 25 ms |
| OB1 maximum cycle | 23 ms |
| Blocks used | TCON, TSEND, TRCV, TDISCON |
2. Root Cause Analysis
Two distinct mechanisms cause the receive-side failures described above. Both are well documented in the SIMATIC S7-1200 programmable controller system manual and the Open User Communication programming manual.
2.1 REQ Edge-Trigger Behavior of TRCV
The TRCV block (FB 84 in the standard library, also referenced as "TRCV" in TIA Portal V13+) uses an edge-triggered interpretation of the REQ input when the connection is configured with ADHOC = FALSE (i.e., when a length header is expected) and a level-triggered interpretation only when ADHOC = TRUE. For the 25 ms polling case described here, ADHOC is typically FALSE (Lenze C300 transmits a fixed 100-byte frame with no SIMATIC length header). With the REQ input tied permanently TRUE inside OB1, the block attempts to start a new receive job on every cycle. Two effects appear:
- The block re-arms the receive job before the previous job's DONE bit is observed, causing STATUS transitions that are not user-visible because the program never reaches the next network where DONE/NDR would be evaluated.
- If the OB1 cycle (23 ms) is shorter than the Lenze transmit interval (25 ms) and roughly aligned with it, multiple consecutive OB1 passes see no incoming data, while a single pass occasionally races with a TCP segment arrival. The receive is completed on a random scan, and the STATUS word can latch transient connection-resource errors.
The Siemens open user communication reference is explicit on this: TSEND, TRCV, TCON, and TDISCON detect a rising edge on REQ internally. If REQ is held at TRUE across multiple OB1 scans, the block does not see a new edge and will not retrigger reliably; furthermore, in some firmware versions the block re-enters while the internal job state machine is still in a transitional state, surfacing as 80C9/8180.
2.2 OB1 Scan vs. Interrupt OB Priority
The S7-1200 executes all user program levels under a single priority class by default. OB1 is the cyclic task with priority 1 (lowest). Any code inserted into OB1 shares CPU bandwidth with the entire machine program. If OB1 takes 23 ms, only ~43 scans per second are possible. Trying to poll a 25 ms remote transmit from a 23 ms scan introduces phase alignment that is statistically guaranteed to produce at least one missed window per second, especially when system events (PROFINET IO update, web server, HMI polling, time-of-day synchronization) extend OB1 past 23 ms.
The correct mechanism for time-deterministic I/O exchange is a cyclic interrupt OB (OB30–OB38 on S7-1200). The hardware timer that drives a cyclic interrupt is independent of OB1 and will pre-empt OB1 when its phase elapses. This decouples the TCP poll rate from the OB1 maximum cycle and ensures the REQ edge is generated at a known, repeating interval.
3. Solution: Edge-Triggered REQ with Cyclic Interrupt
The combined fix is twofold: (a) drive the REQ input of TRCV (and TSEND) with a pulse that is high for exactly one OB scan, then low for at least one OB scan; and (b) execute the TCP exchange inside a cyclic interrupt OB rather than OB1.
3.1 Edge Generation Pattern
A robust pattern is to maintain a BOOL edge tag, e.g., "tcpTick", that is set TRUE inside the cyclic interrupt OB on the first call after a new interval starts, and reset by the same OB on the next call. The pattern below works for both TSEND and TRCV:
// In OB30 (10 ms cyclic interrupt)
IF "tcpTick" = FALSE THEN
"tcpTick" := TRUE;
"sendReq" := TRUE; // pulse TSEND REQ
"recvReq" := TRUE; // pulse TRCV REQ
ELSE
"sendReq" := FALSE;
"recvReq" := FALSE;
END_IF;
The next OB30 invocation flips the tags back to FALSE, producing a clean one-cycle pulse that the open user communication blocks interpret as a rising edge.
3.2 Why a Cyclic Interrupt OB
| Attribute | OB1 (cyclic) | Cyclic Interrupt (OB30–OB38) |
|---|---|---|
| Phase source | End-of-previous-OB1 self-retrigger | Hardware timer (configurable 1–60,000 ms) |
| Jitter | High; scales with program length | Sub-millisecond on S7-1200 |
| Pre-emption | Cannot pre-empt itself | Pre-empts OB1; can be pre-empted by higher-priority OB |
| Determinism for 25 ms poll | Marginal at 23 ms scan | Excellent at 25 ms OB30 phase |
| Worst-case latency | Unbounded (OB1 overrun watchdog) | One phase minus OB30 execution time |
Configure OB30 with a phase of 10 ms (matching the S7-1200 send rate) to start. The receive can then be polled at any rate the block supports; for the Lenze 25 ms transmitter, set OB30 to 25 ms or run TRCV only every second or third OB30 invocation using a counter.
4. Protocol Selection: TCP vs UDP vs ISO-on-TCP
If the edge-trigger + cyclic interrupt fix is insufficient — for example, if the application requires lossless, high-rate exchange where every missed poll is a hard fault — consider switching the transport. The three options supported by S7-1200 open user communication are summarized below.
| Property | TCP | UDP | ISO-on-TCP (RFC 1006) |
|---|---|---|---|
| Connection-oriented | Yes (3-way handshake) | No (datagram) | Yes (TPKT/Wrapping) |
| Block size | Up to 8192 bytes | Up to 8192 bytes (1472 max practical) | Up to 8192 bytes |
| Acknowledgment | Yes (transport layer) | No (application layer) | Yes (transport layer) |
| S7-1200 support | All firmware V4.x | All firmware V4.x | Firmware V4.x with TCON connection DB |
| Header overhead | ~20 bytes IP + 20 TCP | ~20 bytes IP + 8 UDP | ~20 bytes IP + 4 TPKT |
| Determinism on S7-1200 | Good with cyclic OB | Excellent (no ACK round-trip) | Good with cyclic OB |
| Lenze C300 compatibility | Verified by user | Typically yes (check firmware) | Lenze support varies by firmware |
UDP is documented in the Siemens entry ID 20983558 as implemented for performance reasons. Because UDP has no transport-layer acknowledgment, the application must tolerate packet loss or implement its own sequence counter. ISO-on-TCP is described in the SIMATIC S7-1200 manual collection as "an efficient communications protocol closely tied to the hardware, suitable for medium-sized to large data amounts (up to 8192 bytes)" and is recommended when the partner supports RFC 1006 framing.
5. TCON Connection Configuration
Regardless of which transport is selected, the TCON connection DB must be parameterized correctly. The minimum required fields are shown below for the TCP case.
| Field (DB) | Value (TCP to Lenze C300) |
|---|---|
InterfaceId |
64 (decimal) — built-in PROFINET interface of CPU 1214FC |
ID |
1 (arbitrary, must be unique on the CPU) |
ConnectionType |
16#0B (TCP/IP, B#16#0B per TIA Portal dropdown) |
ActiveEstablished |
TRUE (S7-1200 opens the socket to Lenze) |
RemoteAddress |
Lenze C300 IP, e.g., 192.168.10.20 |
RemotePort |
Lenze listening port, e.g., 502 or vendor-defined |
LocalPort |
0 (let the stack assign an ephemeral port) |
LocalAddress |
S7-1200 IP, e.g., 192.168.10.10 |
For ISO-on-TCP, replace ConnectionType with B#16#12 and add TSAP fields. For UDP, use B#16#13. Note that on S7-1200 firmware V4.x the open user communication block library is version-locked; mismatching the library version against the firmware is itself a common source of STATUS 8180 at startup.
6. Step-by-Step Implementation
- Add a cyclic interrupt OB. In TIA Portal project tree, right-click "Program blocks → Add new block → Organization block → Cyclic interrupt". Select OB30 (priority 16 on S7-1200 by default). Set the phase time to 25 ms to match the Lenze transmit interval. Click OK.
-
Move the TCP exchange code from OB1 to OB30. Cut the
TCON,TSEND, andTRCVinstance calls and paste them into OB30. KeepTCONcalled once with edge-triggeredREQfor the connection-establish phase, then callTSENDandTRCVon every subsequent OB30 invocation. -
Implement the edge tag. Add a static BOOL tag
"tcpTick"in the same FB that owns the instances, or in a global DB. Use the pattern from section 3.1 to driveREQonTSENDandTRCVfor exactly one OB30 pass. -
Add status monitoring. In OB30, latch
TSEND_DONE,TSEND_ERROR,TRCV_NDR,TRCV_ERROR,STATUS, andRCVD_LENinto a watch-table DB so the engineering tool can graph frame loss. - Configure TCON parameters. Open the connection DB and verify all fields in section 5. Compile and download.
- Compile and download. Mark "Software (all)" → Download to device. Use "Extended download with reset" only after recording the current program; alternatively, perform a STOP → RUN cycle to re-establish the TCP socket.
-
Verify online. Open the watch table and confirm that
TRCV_NDRpulses every ~25 ms withRCVD_LEN = 100and STATUS = 0.
7. Verification
Use the following checks after the fix is in place. Each one targets a different failure mode that the original symptom could mask.
| Check | Tool / Tag | Expected Result |
|---|---|---|
| OB30 phase time | Online & Diagnostics → Cycle time | ~25 ms ± 0.5 ms |
| OB1 cycle time |
_OB1_CYCL_PREV, _OB1_CYCL_MAX
|
Remains at original ~23 ms; not extended by TCP code |
| TSEND success rate | Counter incrementing on TSEND_DONE
|
100 % over 60 s |
| TRCV success rate | Counter incrementing on TRCV_NDR
|
≥ 99 % over 60 s at 25 ms cadence |
| STATUS word | Watch table | W#16#0000 on every NDR; no 80C9, 8180, 8181, 80A1 |
| Connection status |
TCON output STATUS / TCON_BUSY
|
Busy = FALSE, DONE = TRUE after first connect |
| PROFINET port counters | Online & Diagnostics → Statistics → Port | No discards, no CRC errors, sent = sent segments matching remote ACK |
7.1 Error Code Reference
The most relevant STATUS values for the open user communication blocks on S7-1200 are listed below. Always cross-reference the STATUS value against the corresponding FB documentation in TIA Portal; values may change between firmware versions.
| STATUS (hex) | Block | Meaning | Remediation |
|---|---|---|---|
| W#16#0000 | TRCV/TSEND | No error | None |
| W#16#7000 | TRCV/TSEND | No job active | Normal after DONE/ERROR pulse |
| W#16#7001 | TRCV/TSEND | Job started, BUSY = TRUE | Continue calling block |
| W#16#7002 | TRCV/TSEND | Job accepted, executing | Continue calling block |
| W#16#80C9 | TRCV | Resource temporarily unavailable / overlapping job | Pulse REQ; do not hold TRUE; move to cyclic OB |
| W#16#8180 | TRCV/TSEND | Connection not established | Verify TCON parameters; check physical link |
| W#16#8181 | TRCV/TSEND | Connection lost during job | Verify partner; check cable/port LED |
| W#16#80A1 | TRCV/TSEND | Connection terminated by partner | Partner closed socket; re-run TCON |
| W#16#80B0 | TRCV | ADHOC mode requires LEN = 0 | Set LEN = 0 for ADHOC |
| W#16#80B1 | TRCV | ADHOC = FALSE but LEN = 0 | Set LEN = expected length |
| W#16#8085 | TRCV/TSEND | Wrong LEN parameter or pointer | Re-check ANY pointer variant |
8. Cycle Time Optimization
If after the OB30 fix the OB1 maximum cycle time still exceeds 25 ms — for example, because HMI tags are being read each scan — the application is fundamentally throughput-bound. Three mitigations are appropriate, in order of impact:
- Move all time-deterministic I/O to dedicated cyclic interrupt OBs. OB30 at 25 ms for TCP receive, OB31 at 10 ms for TCP send, OB32 at 5 ms for fast analog sampling. Each OB has its own priority and cannot be blocked by the others.
- Reduce OB1 footprint. Strip string processing, arithmetic-heavy diagnostics, and any HMI-triggered computations out of OB1. Move them to OB35 (100 ms) or a time-of-day OB.
- Tune PROFINET IO update. If PROFINET devices are configured with a 1 ms send clock, the CPU's PROFINET base cycle is 1 ms and contributes a fixed overhead per OB1 scan. Increasing the send clock to 2 ms halves the PROFINET-related OB1 increment.
9. When to Consider UDP Instead
If, after applying the edge-trigger + cyclic interrupt fix, the application still loses more than one frame per ten seconds, evaluate UDP. The tradeoff matrix below clarifies the design decision.
| Decision Criterion | Keep TCP | Switch to UDP |
|---|---|---|
| Frame loss tolerance | Zero | Up to a few % acceptable |
| Partner supports UDP | N/A | Required (Lenze C300 ≥ specific firmware) |
| Maximum payload per datagram | 8192 | 1472 (Ethernet MTU minus IP/UDP headers) |
| Sequencing | Implicit (TCP byte stream) | Application must add sequence counter |
| Implementation complexity | Lower (block-level) | Higher (custom error handling) |
The Siemens support entry 20983558 explicitly notes that the unacknowledged nature of UDP has "unfavorable consequences" — meaning the application must accept loss or compensate for it. For the Lenze C300 in this user's case, a 100-byte payload fits well within the UDP MTU; a sequence number in the first byte (0..255 rolling) is sufficient to discard duplicate or out-of-order datagrams at the S7-1200.
10. Field-Proven Checklist
- OB30 created with phase = 25 ms (or 10 ms if faster than the partner).
- TCP exchange code moved from OB1 to OB30.
- Edge tag
tcpTickimplemented;REQis a one-OB30 pulse, not a level. - TCON connection DB populated per section 5; STATUS returns 0 after first connect.
-
RCVD_LEN = 100on everyTRCV_NDR. - No STATUS 80C9, 8180, 8181, or 80A1 in any 60 s window.
- PROFINET port statistics show no discards or CRC errors.
- OB1 cycle time unaffected by TCP code;
_OB1_CYCL_MAXunchanged. - If loss persists >1 frame/10 s, evaluate UDP with sequence numbering.
Why does TRCV lose data when REQ is held TRUE in OB1?
The TRCV block interprets REQ as a rising edge for non-ADHOC connections. Holding REQ permanently TRUE either retriggers the block before the internal state machine returns to idle (STATUS 80C9) or, more commonly, races the TCP receive against the OB1 scan, producing intermittent NDR/STATUS transitions that the program cannot resolve in time. Pulse REQ for exactly one OB scan to guarantee the block sees a clean edge.
Should I run TSEND and TRCV from OB1 or a cyclic interrupt OB?
For deterministic rates faster than OB1 can scan, use a cyclic interrupt OB (OB30–OB38). OB30 at a 10 ms phase is a stable match for the 10 ms S7-1200 → Lenze send requirement and at 25 ms for the Lenze → S7-1200 receive requirement. OB1 is acceptable only if its maximum cycle is comfortably below the polling interval (rule of thumb: at least 4:1 ratio).
What does STATUS 80C9 mean on TRCV?
STATUS W#16#80C9 indicates a transient resource conflict inside the open user communication stack — typically that the block was called while a previous job had not completed, or that REQ was pulsed too rapidly for the stack to schedule. Pulsing REQ once per cyclic interrupt OB scan and verifying OB1 is not preempting the TCP code resolve this in nearly every field case.
Can I use UDP instead of TCP for the Lenze C300?
Yes, if the Lenze C300 firmware exposes a UDP socket server for the same data block. UDP removes the transport-layer acknowledgment round-trip, lowering jitter at the cost of accepting that datagrams may be dropped or reordered. Add a 1-byte sequence counter in the application payload so the S7-1200 can discard duplicates. Siemens documents UDP open user communication in entry 20983558.
What is the maximum payload per TSEND/TRCV call on S7-1200?
Up to 8192 bytes per call, provided the receiving connection is configured with sufficient buffer. For UDP, the practical Ethernet MTU limit is 1472 bytes per datagram. The SIMATIC S7-1200 manual collection describes ISO-on-TCP as suitable for "medium-sized to large data amounts (up to 8192 bytes)" using TPKT framing.