S7-1200 to PC Data Transfer: TCON, Modbus TCP, OPC UA Setup

David Krause10 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

Exchanging process data between a Siemens SIMATIC S7-1200 CPU and a PC is one of the most common integration tasks in factory automation. The platform supports several native and standardized paths, but the right answer depends on three engineering variables:

  • Data type and volume – INT, WORD, REAL, STRING, DB payloads, and update rate
  • Firmware generation – V4.0 … V4.4 unlocks different feature sets (notably OPC UA Server requires V4.4+)
  • PC-side stack – Python, C#/.NET, LabVIEW, or off-the-shelf HMI/SCADA

This reference covers the three production-grade paths most engineers use with a CPU 1214 DC/DC/RLY on firmware V4.3 with TIA Portal V15.1:

  1. Open User Communication (OUC) over TCP/IP using TCON / TSEND / TRCV
  2. Modbus TCP (S7-1200 as Modbus TCP Client or Server)
  3. OPC UA Server (firmware V4.4+)

Each path is documented with a working configuration, a parameter table, a fault matrix, and a PC-side integration hint.

Prerequisites

Item Recommended Value Notes
CPU S7-1200 CPU 1214 DC/DC/RLY (6ES7214-1AG40-0XB0 or later) Integrated PROFINET interface
Firmware V4.3 for TCP/Modbus; V4.4+ for OPC UA Server Verify in Online & Diagnostics > Module Information
TIA Portal V15.1 Update 5 or later (V16/V17 for OPC UA modeling) Step7 V13.0.2 → V18.0.0 supports SCL export compatibility
PC stack Python 3.8+ (Windows 7/10/11) or .NET 4.6+ S7.Net / S7NetPlus for C#; python-snap7 or s7 library
Network 192.168.0.0/24, CPU 192.168.0.1, PC 192.168.0.10 Same subnet, no VLAN routing issues
Firewall Allow TCP 102 (S7), 502 (Modbus), 4840 (OPC UA) Windows Defender or corporate GPO
License note: OPC UA Server on S7-1200 is part of the standard firmware for V4.4+; no additional license is required for the server role. Modbus TCP also ships with the firmware without an extra license.

Path Selection Matrix

Criterion TCON/TSEND/TRCV Modbus TCP OPC UA Server
Min. firmware V4.0 V4.0 V4.4
Data types Raw bytes (manual encode INT/REAL/STRING) Holding/Input registers, Coils (16-bit or 1-bit) Native INT, REAL, BOOL, STRING, DateTime
Topology S7-1200 active or passive partner Client/Server model Publish/Subscribe or Client/Server
Typical payload 1 – 8192 bytes per call 1 – 125 registers per request Up to 100k nodes (practical: 200 – 2 000)
Cycle 10 ms – 1000 ms (event-driven) 50 ms – 5000 ms (polled) 100 ms – 5 000 ms (subscription)
PC stack effort Medium (parse byte stream) Low (mature libraries) Low (semantic info auto-mapped)
Security None (TCP only) None (TCP only) Sign + Encrypt (X.509)

Method 1 – Open User Communication (TCON / TSEND / TRCV)

Open User Communication (OUC) is the leanest path. The S7-1200 acts as a TCP client or server and exchanges raw byte buffers; the user program is responsible for the data layout. This is the path the original integrators selected for a 1214 DC/DC/RLY on firmware V4.3.

Hardware Configuration in TIA Portal

  1. Open Devices & Networks and select the CPU 1214 DC/DC/RLY.
  2. In the Properties > PROFINET interface pane, set:
    • IP address: 192.168.0.1
    • Subnet mask: 255.255.255.0
    • Router: not used for direct PC link
  3. Enable Permit PUT/GET communication from remote partner only if you also use PUT/GET – not required for OUC.

Connection Description (TCON)

Add an TCON instance DB (e.g. TCON_DB) and configure the connection block as follows:

Parameter Value (Active Client Example) Value (Passive Server Example)
Block TCON (FB65 in STEP 7 V15.1) TCON (FB65)
Interface PN interface (built-in) PN interface (built-in)
Connection type TCP (16#0B) TCP (16#0B)
Active/Passive Active connection establishment Passive connection establishment
Local port 0 (auto-assign) 2000
Remote IP 192.168.0.10 0.0.0.0
Remote port 2000 0 (any)

Data Buffer Block (SCL)

Create a global DB DataBuffer for the send and receive arrays. Reserve at least 4 bytes for INT, 4 bytes for REAL, 2 bytes for WORD, and N+2 bytes for STRING (max length byte + actual length byte + N characters).

DATA_BLOCK "DataBuffer"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
NON_RETAIN
VAR
  SendBuf    : ARRAY[0..255] OF BYTE;   // raw outbound buffer
  RcvBuf     : ARRAY[0..255] OF BYTE;   // raw inbound buffer
  i          : INT;                     // scratch
  rSpeed     : REAL;                    // example REAL value
  iCount     : INT;                     // example INT value
  wStatus    : WORD;                    // example WORD value
  sLabel     : STRING[20];             // example STRING value
END_VAR
END_DATA_BLOCK

Encode INT, REAL, WORD, and STRING to a Byte Buffer (SCL)

// Pack a REAL, INT, WORD, and STRING into SendBuf starting at offset 0
"DataBuffer".SendBuf[0]  := USINT_TO_BYTE(SHR(IN:=REAL_TO_DWORD("DataBuffer".rSpeed), N:=24) AND 16#FF);
"DataBuffer".SendBuf[1]  := USINT_TO_BYTE(SHR(IN:=REAL_TO_DWORD("DataBuffer".rSpeed), N:=16) AND 16#FF);
"DataBuffer".SendBuf[2]  := USINT_TO_BYTE(SHR(IN:=REAL_TO_DWORD("DataBuffer".rSpeed), N:=8)  AND 16#FF);
"DataBuffer".SendBuf[3]  := USINT_TO_BYTE(        REAL_TO_DWORD("DataBuffer".rSpeed)         AND 16#FF);

"DataBuffer".SendBuf[4]  := USINT_TO_BYTE(SHR(IN:=INT_TO_WORD("DataBuffer".iCount), N:=8) AND 16#FF);
"DataBuffer".SendBuf[5]  := USINT_TO_BYTE(        INT_TO_WORD("DataBuffer".iCount)        AND 16#FF);

"DataBuffer".SendBuf[6]  := USINT_TO_BYTE(SHR(IN:="DataBuffer".wStatus, N:=8) AND 16#FF);
"DataBuffer".SendBuf[7]  := USINT_TO_BYTE(        "DataBuffer".wStatus        AND 16#FF);

// STRING layout: byte 8 = max len, byte 9 = actual len, bytes 10..(10+actual-1) = chars
"DataBuffer".SendBuf[8]  := 20;   // max length
"DataBuffer".SendBuf[9]  := INT_TO_BYTE(LEN("DataBuffer".sLabel));
FOR "DataBuffer".i := 0 TO LEN("DataBuffer".sLabel) - 1 DO
    "DataBuffer".SendBuf[10 + "DataBuffer".i] := CHAR_TO_BYTE(MID("DataBuffer".sLabel, "DataBuffer".i + 1, 1));
END_FOR;

Send / Receive Sequence in OB1 (SCL)

// One-shot: establish the connection
IF "FirstRun" THEN
    "TCON_DB".REQ       := TRUE;
    "TCON_DB".ID        := 1;
    "TCON_DB".CONNECT   := "ConnParam";   // connection description VARIANT/DATA_BLOCK
    "FirstRun"          := FALSE;
END_IF;
"TCON_DB">(REQ := "TCON_DB".REQ, ID := 1);

// Trigger TSEND every 100 ms using a cyclic interrupt OB
IF "TSEND_DB".DONE OR "TSEND_DB".ERROR THEN
    "TSEND_DB".REQ  := FALSE;
END_IF;
"TSEND_DB"(REQ := NOT "TSEND_DB".BUSY,
           ID  := 1,
           LEN := 30,                  // total payload bytes
           DATA := "DataBuffer".SendBuf);

// TRCV – receive any length up to 256 bytes
"TRCV_DB"(EN_R := TRUE,
          ID   := 1,
          LEN  := 0,                   // 0 = accept any length
          DATA := "DataBuffer".RcvBuf,
          NDR  => ,
          LEN  => ,
          STATUS => "rcvStatus",
          BUSY  => ,
          ERROR => );
Cycle placement: Drive TSEND from a Cyclic Interrupt OB (e.g. OB35 at 100 ms) rather than OB1 to avoid stalling the main scan. TRCV should run every OB1 pass to flush the receive buffer quickly.

Method 2 – Modbus TCP

Modbus TCP rides on the same PROFINET port (X1) but uses port 502 and the Modbus Application Protocol. The S7-1200 can act as a Modbus TCP Server (one client) from firmware V4.0, and as a Modbus TCP Client from V4.1+ via the MB_CLIENT instruction.

Procedure

  1. Insert Instructions > Communication > Modbus TCP and choose MB_SERVER or MB_CLIENT.
  2. For the server role, configure:
    • Local port: 502
    • MB_HOLD_REG: pointer to a data block sized to map the registers (e.g. P#DB100.DBX0.0 WORD 100 for 100 holding registers)
  3. Set the IP access list under Properties > Connection > Access list to limit which PCs can attach.
  4. Compile and download. MB_SERVER runs in OB1 automatically once started with MB_MODE := 1.

Register Map Example

Modbus Address S7-1200 Tag Type Notes
40001 "ModbusDB".Speed_RPM REAL (2 registers) Holding register pair, big-endian word swap
40003 "ModbusDB".Count INT (1 register) 0 – 32767 / –32768 negative
40004 "ModbusDB".StatusBits WORD (1 register) Bit-packed flags
40005 – 40014 "ModbusDB".Label STRING[20] (10 registers) First register = max len, second = actual len
Byte/word order: Modbus is big-endian. A 32-bit REAL occupies two consecutive holding registers where the higher-order word is at the lower address. Use SWAP in SCL or rely on the PC library to do the byte swap.

Method 3 – OPC UA Server (Firmware V4.4+)

When firmware V4.4 is loaded on the S7-1200, the integrated PROFINET interface exposes an OPC UA Server with semantic information. This is the cleanest path if the PC stack supports OPC UA (Unified Automation, Ignition, Kepware, Node-RED, etc.).

Activation

  1. Update firmware to V4.4 or later. Use TIA Portal Online > Firmware Update; ensure the PLC is in STOP.
  2. Open Properties > OPC UA Server on the CPU and tick Activate OPC UA Server.
  3. Define a server interface by drag-and-drop of DB tags into the OPC UA Companion Specification view.
  4. Set the security policy – None for lab, Basic128Rsa15 or Basic256Sha256 with a server certificate for production.

Endpoint URL

opc.tcp://192.168.0.1:4840

Client browsing will then expose each tag with its native type (INT, REAL, BOOL, STRING, DateTime, structured record). No byte swapping or length prefixes are needed on the PC side.

PC-Side Integration

Python (TCP via OUC)

import socket, struct

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('192.168.0.1', 2000))
sock.send(b'\x00')   # request frame or trigger

raw = sock.recv(256)
real_val, int_val, word_val = struct.unpack('>fiH', raw[:10])
str_len = raw[9]
text    = raw[10:10+str_len].decode('ascii')
print(real_val, int_val, word_val, text)

Python (Modbus TCP with pymodbus 3.x)

from pymodbus.client import ModbusTcpClient
c = ModbusTcpClient('192.168.0.1', port=502)
rr = c.read_holding_registers(0, 14, slave=1)
print(rr.registers)

.NET (S7-1200 native S7 protocol)

Use S7NetPlus. This uses the Siemens S7 protocol on TCP/102 – a fourth option not based on OUC. It is suitable when the PC reads the same data blocks the HMI sees, but it must be enabled in TIA Portal: Properties > Connection > Permit access with PUT/GET.

Simulating the Link with PLCSIM

PLCSIM Advanced targets the S7-1500/ET 200SP CPU simulation and is not a fit for a 1214. For an S7-1200, use PLCSIM V13 – V18 (the regular PLCSIM) which can simulate the 1214 with firmware V4.x. If you need to expose PLCSIM to the host Ethernet adapter (e.g. for Python tests), NetToPLCSim bridges the simulated PLC to the PC's network card. Limitations:

  • Only one simulated CPU per NetToPLCSim instance
  • Connection drops every time you stop PLCSim – re-establish the partner connection
  • OPCU UA Server activation is supported in PLCSIM V17+

Troubleshooting Matrix

Symptom Likely Cause Diagnostic Fix
TSEND stays BUSY = 1 forever TCON connection not established Inspect STATUS of TCON – 16#80C4 means remote partner unreachable Ping the PC; check firewall on TCP 2000; verify IP/subnet
TRCV errors with STATUS 16#8085 Length 0 with new firmware variants Set LEN := 256 and evaluate RCV_LEN Use a fixed length for the first 200 ms, then switch to LEN=0
Modbus PC sees garbage REAL Word order swap on multi-register values Use pymodbus decode_32bit_float Use word_order='big' or apply SWAP in PLC
OPC UA browse returns empty Server interface not published Right-click PLC > Compile > Download hardware configuration Add tags under OPC UA > Server interface and recompile
Connection refused on TCP 502 Another SCADA already opened it Run netstat -ano | findstr 502 Kill the other client or change MB_SERVER port
Data arrives in wrong sequence PC stack uses Nagle algorithm Disable Nagle: sock.setsockopt(IPPROTO_TCP, TCP_NODELAY, 1) Add length prefix in payload to frame messages
String truncated to one character Forgot to write the actual length byte Inspect byte 9 of payload Update SCL code to write LEN(s) before the chars
Python winsocket error 10060 PC firewall blocks reply Disable Windows Defender Firewall temporarily Create inbound rule for python.exe or open the TCP port

Performance and Cycle-Time Notes

  • OUC: TSEND/TRCV on a 1214 takes ~3 – 5 ms per call for 100 bytes. 100 ms OB35 cycle is safe up to 1 kB payload.
  • Modbus TCP: A MB_CLIENT poll of 100 registers takes 8 – 12 ms round-trip on a 100 Mbit/s link.
  • OPC UA: Subscription with 200 tags and 100 ms publishing interval consumes roughly 15 – 20 % of OB1 time on a 1214 – avoid sampling fast counters through OPC UA.
  • For >1 kB per cycle, use TSEND_C (FB1904) with the Send with length prefix option to fragment the payload transparently.

Security Hardening Checklist

  • Disable Permit PUT/GET when using OUC or Modbus – it is not required for either.
  • Restrict Modbus TCP access list to the engineering PC IP.
  • For OPC UA, deploy a server certificate signed by the plant CA and enable Basic256Sha256.
  • Isolate the engineering network in a VLAN; never expose port 102/502/4840 to the corporate LAN.

Recommended Path by Scenario

Scenario Recommended Path Why
Lab / prototype, ≤ 200 tags, Python on PC TCON/TSEND/TRCV Lowest hardware dependency; firmware V4.0 is enough
Production SCADA, multi-vendor, polling 100 – 500 registers Modbus TCP Mature libraries, deterministic, easy to firewall
IIoT gateway, semantic info required, future-proof OPC UA Server (V4.4+) Native types, security, no manual encoding
PC runs existing Siemens HMI / WinCC on the same subnet Native S7 over TCP/102 (S7NetPlus) Reuses HMI variables, no extra configuration

What is the minimum firmware for OPC UA Server on S7-1200?

OPC UA Server on the integrated PROFINET interface requires firmware V4.4 or later. Earlier versions expose the S7 protocol on TCP/102 only; they do not support OPC UA.

Do I need the PUT/GET option enabled for TCON/TSEND/TRCV?

No. Open User Communication uses the TCON/TSEND/TRCV instruction family and is independent of PUT/GET. Leave PUT/GET disabled to reduce the attack surface.

Can PLCSIM Advanced simulate a 1214 for this task?

No. PLCSIM Advanced is limited to S7-1500 and ET 200SP CPUs. Use the regular PLCSIM V13–V18 for the 1214, and bridge it to the host network with NetToPLCSim if the PC must connect to a real TCP port.

What is the largest payload per TSEND call on a 1214 firmware V4.3?

Up to 8 192 bytes per call on the integrated PROFINET interface. The send/receive DB must be at least that size; for shorter messages the PC reads the actual length from the first two bytes of the payload.

Why does my REAL value arrive corrupted over Modbus TCP?

Modbus is big-endian and S7-1200 holding registers are word-addressed. A 32-bit REAL spans two registers with the high word at the lower address; the PC library must perform a word swap before treating the 32-bit value as a float. In pymodbus use client.convert_from_registers(rr.registers[:2], data_type=ModbusClient.DATATYPE.FLOAT32).

Back to blog