S7-1200 TSEND_C: Sending Real, Int, and String Data Over TCP/IP to a PC Application
Engineers integrating a SIMATIC S7-1200 PLC with a PC-based data acquisition or test application (e.g. NI LabWindows/CVI, LabVIEW, Python, .NET) frequently hit the same wall: the example program in TIA Portal uses a 10-element Array of Char, the data round-trips perfectly through HyperTerminal, and the moment the developer switches the data type to Real, Int, or String, the receiver reports "junk". This is not a bug in TSEND_C — it is a misunderstanding of what the instruction actually moves on the wire. TSEND_C is a transparent byte-pipe: it copies the contiguous byte image of the DATA tag to the TCP socket and tells the receiver the length. The CPU performs no conversion. If the PC application expects ASCII characters but the PLC is shipping the IEEE-754 bit pattern of a floating-point value, the terminal will display garbage. The fix is to convert on the PLC side, convert on the PC side, or move to a higher-level protocol.
This reference walks through the engineering discipline required to make S7-1200 ↔ PC communication deterministic, including firmware prerequisites, TSEND_C/TRCV_C parameter layouts, byte-order conventions, conversion instruction selection, and a working STL/SCL pattern that ships Real, Int, and String values to a LabWindows/CVI TCP client without data loss.
1. What TSEND_C Actually Does
Per the SIMATIC S7-1200 manual, TSEND_C ("Send and receive data using Ethernet") establishes a TCP or ISO-on-TCP connection, transmits a data block, and (optionally) terminates the connection in a single call. Its companion TRCV_C accepts bytes from the partner and writes them into a configured data tag. Both instructions sit on top of the TCON connection description block (specified through the IDB/connection parameters), and both copy raw bytes — not typed values — into the transmit buffer.
The instruction signature is:
| Parameter | Direction | Type | Description |
|---|---|---|---|
| REQ | Input | Bool | Rising edge initiates the send/receive operation |
| CONT | Input | Bool | TRUE = keep the connection open after transmission completes |
| LEN | Input | UInt | Number of bytes to send; set to 0 for automatic length detection on String tags |
| DATA | InOut | VARIANT | Pointer to the tag to transmit (any elementary or complex type) |
| ID | InOut | CONN_OUC | Reference to the TCON connection (HW ID) |
| DONE | Output | Bool | Set for one scan when the job completes successfully |
| BUSY | Output | Bool | TRUE while the job is in progress |
| ERROR | Output | Bool | TRUE if the job terminated with an error |
| STATUS | Output | Word | Detailed error/status code (see Section 8) |
The crucial point: DATA is a VARIANT pointer. The instruction does not inspect the element type; it reads LEN bytes starting at the address of DATA. Therefore a Real tag occupies 4 bytes of IEEE-754 little-endian, an Int occupies 2 bytes, a DInt 4 bytes, and a String[20] occupies 22 bytes (2-byte length header + 20-byte payload). The PC must reconstruct the value from those exact bytes.
2. Firmware and Software Prerequisites
| Component | Minimum | Recommended | Notes |
|---|---|---|---|
| CPU firmware (S7-1200) | V4.0 | V4.5 / V4.6 | TSEND_C/TRCV_C fully supported from V4.0; V4.4+ adds enhanced ISO-on-TCP keep-alive |
| TIA Portal | V13 SP1 | V17 / V18 | Sample program in source uses V11 SP2; modernise to V16+ for current conversion libraries |
| STEP 7 Basic/Professional | Same as TIA Portal | — | Required for SCL or LAD/FBD editing |
| PC TCP stack | Winsock 2.0 | OS-native | LabWindows/CVI uses TCP/IP Library (ni5289) or standard Berkeley sockets |
| Ethernet cabling | Cat 5e | Cat 6A | Direct or via managed switch; PLC and PC must share subnet |
TSEND_C/TRCV_C instruction set under "Communication > Open User Communication". Firmware V3.0 supports only a subset (no keep-alive, no ISO-on-TCP for TSEND_C — TCON is required as a separate block). Always verify the firmware version under Online & Diagnostics > CPU Information before commissioning.3. The Data-Type Problem: Why the PC Sees "Junk"
The S7-1200 stores values in little-endian byte order, with the following on-wire footprints:
| TIA Data Type | Bytes on Wire | Example (Decimal 1234.5) | Hex (LE) on Wire |
|---|---|---|---|
| Bool | 1 | TRUE | 0x01 |
| Int (16-bit) | 2 | 1234 | 0xD2 0x04 |
| DInt (32-bit) | 4 | 1234 | 0xD2 0x04 0x00 0x00 |
| Real (32-bit IEEE-754) | 4 | 1234.5 | 0x00 0x00 0x9A 0x45 |
| LReal (64-bit) | 8 | 1234.5 | 0x00 0x00 0x00 0x00 0x00 0x4D 0x94 0x40 |
| String[20] | 22 | "Hello" | 0x05 0x00 [0x48 0x65 0x6C 0x6C 0x6F] + 0x00 padding |
| Char / Array of Char | n | 'A' 'B' 'C' | 0x41 0x42 0x43 |
When the PC application uses HyperTerminal or a generic printf/recv call, the C runtime interprets each received byte as an ASCII code point. 0x00 0x00 0x9A 0x45 (the IEEE-754 image of 1234.5) becomes four NUL, NUL, non-printable, 'E' characters — exactly the "junk" reported in the source thread. Three options resolve the issue:
-
Convert on the PLC to ASCII before sending. Use
S_CONV/REAL_TO_STRING/INT_TO_STRINGinstructions to produce aStringtag, then transmit the string. The PC receives printable characters and parses withatof()orstrtod(). -
Transmit raw bytes and decode on the PC. Send a
REALtag directly withLEN=4; the PC reads 4 bytes and reinterprets them as a little-endian IEEE-754 float usingmemcpy. - Switch to a higher-level protocol. Use Siemens Open User Communication framing, OPC UA, Modbus TCP, or the S7 communication protocol — each carries typed data without manual byte manipulation.
4. Recommended Approach: ASCII Framing for Maximum Compatibility
For a PC application written in C (LabWindows/CVI, Visual Studio) or Python, the lowest-friction solution is to convert every numeric value to a fixed-width ASCII string on the PLC, optionally append delimiters, and let the PC parse the line with standard strtok / sscanf. This mirrors how industrial serial protocols (Modbus RTU ASCII, NMEA 0183) have worked for 40 years and avoids endian, padding, and alignment concerns.
4.1 PLC Data Block Definition
DATA_BLOCK "DB_Comms"
STRUCT
bTriggerSend : Bool; // Pulse to start a send cycle
rTemperature : Real; // 22.5 °C
rPressure : Real; // 1013.2 mbar
iCount : Int; // 1234 pulses
sSerial : String[20]; // "SN-A12345"
sPayload : String[64]; // Assembled ASCII line
sReceiveBuf : String[128]; // TRCV_C target
iRxLen : UInt; // Bytes actually received
END_STRUCT;
END_DATA_BLOCK
4.2 SCL Build Code for the ASCII Payload
// "DB_Comms".sPayload := 'T=' + REAL_TO_STRING("DB_Comms".rTemperature)
// + 'P=' + REAL_TO_STRING("DB_Comms".rPressure)
// + 'N=' + INT_TO_STRING("DB_Comms".iCount)
// + 'S=' + "DB_Comms".sSerial
// + '$R$L';
Note that REAL_TO_STRING returns a string of length up to 14 characters (sign, 10 digits, decimal point, exponent). For a deterministic byte count, convert the real to a fixed-format string using String format instructions (TIA V16+) or, if unavailable, use S_CONV with explicit scaling:
iTempInt := REAL_TO_INT("DB_Comms".rTemperature * 10.0);
"DB_Comms".sPayload := 'T=' + INT_TO_STRING(iTempInt) + '/10;';
4.3 Sending the ASCII String
| Parameter | Value | Reason |
|---|---|---|
| REQ |
"DB_Comms".bTriggerSend (rising edge) |
One-shot per trigger |
| CONT | TRUE | Keep TCP connection alive between sends |
| LEN | 0 | Auto-detect String length (header + chars) |
| DATA | "DB_Comms".sPayload |
String VARIANT pointer |
| ID | "DB_Comms".iConnId |
From TCON parameter block |
String data, set LEN = 0 in TSEND_C so the instruction transmits only the populated characters (current length from the String header), not the entire 66-byte buffer. The PC receives exactly what was written.5. Alternative Approach: Raw Binary Transfer
If you need maximum throughput (e.g. 1 kHz waveform streaming) and the PC application can handle a binary protocol, the most efficient path is to concatenate a fixed-format frame and transmit it with a known length. Define a structure with no padding (use STRUCT with packed layout) and send it raw.
TYPE "UDT_Telemetry"
STRUCT
rTemperature : Real; // 4 bytes
rPressure : Real; // 4 bytes
iCount : DInt; // 4 bytes
byStatus : Byte; // 1 byte
sId : String[8]; // 10 bytes (length + 8 chars)
END_STRUCT;
END_TYPE
Transmit with LEN := SIZEOF("UDT_Telemetry"). On the PC side (C example):
struct Telemetry {
float temperature; // 4 bytes, little-endian
float pressure; // 4 bytes, little-endian
int32_t count; // 4 bytes, little-endian
uint8_t status; // 1 byte
uint16_t id_len; // 2 bytes
char id[8]; // 8 bytes
};
// Total = 23 bytes per frame
// recv(sock, &frame, sizeof(frame), MSG_WAITALL);
memcpy/recv pattern works without htonl/ntohl swaps. If the PC target is big-endian (rare, e.g. some network processors), use Siemens SWAP instructions or PC-side bswap.6. Receiving on the PC: LabWindows/CVI Pattern
LabWindows/CVI provides the TCP/IP Library. A minimal client loop that parses ASCII frames from the S7-1200:
int CVICALLBACK TCPClientCallback(unsigned handle, int event,
int error, void *callbackData) {
char rxBuf[256];
int nRead = 0;
switch (event) {
case TCP_DATAREADY:
nRead = ClientTCPRead(handle, rxBuf, sizeof(rxBuf)-1, 1000);
rxBuf[nRead] = '\0';
// Parse payload e.g. "T=22.5;P=1013.2;N=1234;S=SN-A12345\r\n"
float T, P; int N; char S[32];
sscanf(rxBuf, "T=%f;P=%f;N=%d;S=%31s", &T, &P, &N, S);
// Update GUI / log
break;
case TCP_DISCONNECT:
// Reconnect with ConnectToTCPServer
break;
}
return 0;
}
For binary telemetry, replace the sscanf line with a memcpy into a C struct and read sizeof(struct Telemetry) per frame using the MSG_WAITALL flag.
7. TCON Connection Configuration (TIA Portal Step-by-Step)
- Open the device view of the S7-1200 CPU and double-click the PROFINET interface.
- Set the IP address and subnet mask (e.g. 192.168.0.10 / 255.255.255.0). Match the PC subnet.
- Add a new Open User Communication connection block (right-click the CPU → Properties → Open User Communication). Configure:
- Connection type: TCP
- Partner IP address: 192.168.0.20 (PC running LabWindows)
- Partner port: e.g. 2000
- Local port: any (e.g. 0 = dynamic)
- Active connection establishment: enable on the PLC side (TSEND_C is an active client)
- Compile and download the hardware configuration.
- Create a global DB holding the connection ID (Word) and a TCON_PARAM instance, or use the system-supplied instance DB created when you drop
TSEND_Cinto a network.
8. Status and Error Codes
TSEND_C/TRCV_C return a 16-bit STATUS word. The high byte (bits 15…8) maps to the protocol class, the low byte to the actual error. The most common values encountered when sending wrong data types:
| STATUS (hex) | Meaning | Remedy |
|---|---|---|
| 0x0000 | No error | — |
| 0x7000 | No job active | Trigger a rising edge on REQ |
| 0x7001 | Job starting | Wait for DONE/BUSY cycle |
| 0x7002 | Job in progress | Normal during transmission |
| 0x8085 | LEN = 0 and DATA is not a String | Set LEN to byte count, or change DATA to String |
| 0x80A1 | Connection not established / partner refused | Verify partner TCP port is open and firewall rule |
| 0x80C3 | All connection resources in use | Reduce simultaneous TCON count (max 8 on S7-1200) |
| 0x80C4 | Temporary communications error, partner reset | Implement retry logic on REQ |
| 0x80B1 | Invalid DATA pointer (VARIANT = NULL) | Check pointer to DB / tag |
| 0x80BB | LEN exceeds available data | Reduce LEN; for Strings use 0 |
9. Verification Procedure
-
Online watch table: Place
"DB_Comms".sPayload,TSEND_C.DONE, andTSEND_C.STATUS in a watch table with trigger onbTriggerSend. -
Wireshark capture: Run Wireshark on the PC with filter
tcp.port == 2000. Confirm the byte count equals the populated String length. -
HyperTerminal round-trip: A 22-character string with
LEN=0must produce exactly 22 bytes in the capture. -
Raw-Real verification: Send a
Realtag with value1234.5andLEN=4. The 4-byte payload must be00 00 9A 45on the wire. -
Connection state: Monitor
TCON.DONE andTCON.STATUS after a CPU RUN↔STOP cycle to confirm auto-reconnect logic.
10. Troubleshooting Matrix
| Symptom | Likely Cause | Resolution |
|---|---|---|
| PC receives only printable ASCII, no numeric values | DATA is String after conversion — expected behaviour | Parse with sscanf / strtok on the PC |
| PC sees non-printable characters instead of Real | DATA is Real; PC is interpreting bytes as ASCII | Convert to String on PLC, or use memcpy into a float on the PC |
| TSEND_C reports STATUS 0x8085 | LEN=0 used on a non-String tag | Set LEN to exact byte count |
| STATUS 0x80A1 after power-up | Partner TCP listener not yet started | Add retry / delay before first REQ |
| Data length received mismatches expected | LEN parameter includes the 2-byte String header | Use LEN=0 for String to send only populated chars |
| CPU goes to STOP on bad reception | TRCV_C writing into a String with LEN > declared length | Clamp LEN to max String capacity - 2 |
| Communication works for 1 hour then drops | Keep-alive not configured; switch/firewall closes idle socket | Set "Keep-alive" option in TCON to 30 s |
11. When to Use a Higher-Level Protocol
If the PC application needs typed data without parsing, the engineering effort of conversion on both sides — the original complaint in the field report — becomes unjustified. For greenfield projects, prefer:
-
OPC UA: The S7-1200 firmware V4.4+ supports OPC UA server. The PC consumes typed variables directly via any OPC UA client SDK (Siemens, .NET, Python
opcua, LabVIEW DataFinder). -
Modbus TCP: Use the
MB_CLIENTinstruction (S7-1200 firmware V4.0+). Register mapping handles integer and float values natively; dozens of PC libraries exist. - S7 Communication: The PC uses libnodave, Snap7, or the official Siemens S7 protocol to read DBs directly without a custom byte protocol.
For the specific use case of "send a few strings of size 20 and a few real values" described in the source, the ASCII-framing approach in Section 4 yields the shortest development time and the fewest moving parts. For 100+ tags or sub-100 ms update rates, move to OPC UA or S7 communication.
12. Frequently Asked Questions
Can TSEND_C on the S7-1200 send Real, Int, and DInt data types directly, or only Char?
TSEND_C is byte-transparent and accepts any VARIANT pointer, including Real, Int, DInt, and String. The instruction does not convert the value — it copies the raw memory image to the socket. The PC application must know the byte layout (little-endian IEEE-754 for Real, 2 bytes for Int) and decode accordingly, or the PLC must convert the value to an ASCII string first.
Why does my PC application display garbage characters when I send a Real value?
The PC is interpreting the 4-byte IEEE-754 little-endian representation as ASCII code points. For a value of 1234.5, the wire bytes are 0x00 0x00 0x9A 0x45, which appear as two NUL characters, a non-printable glyph, and 'E'. Convert the Real to a String on the PLC using REAL_TO_STRING or by scaling to an Int (e.g. × 10 for one decimal) and converting with INT_TO_STRING, then send the String with LEN=0.
What value should I use for the LEN parameter when sending a String tag?
Set LEN = 0 in the TSEND_C block. The instruction then reads the current length field of the String header and transmits exactly that many characters, excluding the 2-byte length header itself and any trailing pad bytes. This prevents the PC from receiving garbage from the unused String capacity.
What is the maximum number of TSEND_C connections an S7-1200 can handle simultaneously?
Up to 8 Open User Communication connections (TCP or ISO-on-TCP) can be active concurrently on a standard S7-1200 CPU, depending on firmware and CPU type. Each TCON consumes a connection resource; STATUS 0x80C3 indicates the limit has been reached. Consolidate tags into one frame per direction when possible.
Is TSEND_C a TCP client or server, and how does that affect the LabWindows CVI side?
TSEND_C is the active partner and initiates the TCP connection. In TIA Portal, enable "Active connection establishment" on the TCON configuration. The LabWindows/CVI PC application must therefore act as a TCP server using the RegisterTCPServer / TCPServerCallback functions from the TCP/IP Library, listening on the partner port defined in the PLC connection block (e.g. port 2000).