Overview: S7-1200 Data Block Exchange over Industrial Ethernet
The SIMATIC S7-1200 controller family (CPU 1211C, 1212C, 1214C, 1215C, 1217C, plus the SIPLUS S7-1200 variants) integrates a PROFINET interface on every CPU. This interface supports the protocols required for peer-to-peer data exchange with another S7-1200, an S7-1500, an S7-300/400, or a third-party device. The four protocol paths available in TIA Portal are:
- ISO-on-TCP (RFC 1006) — connection-oriented, used by T_SEND / T_RCV and TSEND_C / TRCV_C.
- TCP native — connection-oriented, used by TSEND_C / TRCV_C in TCP mode.
- UDP — connectionless, used by TUSEND / TURCV.
- S7 Communication — used by PUT / GET, with no program code required on the partner.
When the goal is to move a structured payload — a recipe, a machine state, a measurement record — from one DB on the source CPU to a matching DB on the destination CPU, the cleanest implementation is the T_SEND / T_RCV pair over an ISO-on-TCP connection. The most common stumbling block is the compiler rejecting any pointer that points at "just a DB" with the diagnostic "Data block must be of SDT or UDT type". This article explains what that message means, why TIA Portal enforces it, and the exact steps to satisfy the compiler and ship data successfully between two S7-1200 controllers. For the broader Siemens documentation entry on this technique, see the Siemens Online Support hub and the S7-1200 system manual entry set linked from there.
What the "SDT or UDT" Error Actually Means
Open the T_SEND or TSEND_C instruction in TIA Portal and the block pin labeled DATA expects a pointer of a specific shape. The instruction's source code internally constructs a pointer of type ANY, and the editor inspects the type associated with that pointer. Two cases are accepted:
| Accepted Type | Definition | Editor Behavior |
|---|---|---|
| UDT (User-Defined Type) | A PLC data type you created in the project tree under "PLC data types". | Accepted at the DATA pin without further conditions. |
| SDT (System Data Type) | A type pre-installed by the TIA Portal installation (e.g., TCON_IP_v4, TADDR_Param, IF_CONF). |
Accepted at specific pins, e.g., the CONNECT parameter of TCON / TSEND_C. |
A plain global DB (e.g., "DB_Recipe") is treated as having the implicit type "DB", which is a container, not a structured type. The pointer P#DB100.DBX0.0 BYTE 200 carries no type information that the T_SEND compiler pass can verify, so the editor refuses to compile and reports the "SDT or UDT" message. The fix is to declare a PLC data type (UDT) that describes the payload layout, instantiate it as a tag inside a DB, and point the DATA pin at that tag.
Creating a UDT in TIA Portal
- Open the S7-1200 project in TIA Portal V15.1 or later (V17 is the current production line for firmware 4.4; V18 supports firmware 4.5 / 4.6).
- Expand the CPU in the project tree, right-click PLC data types and select Add new data type.
- Name it
UDT_RecipePayload(max 125 ASCII characters, must be unique project-wide). - Add members with fixed byte offsets. Example for a 68-byte payload:
TYPE UDT_RecipePayload
VERSION : 0.1
STRUCT
RecipeID : DINT; // 4 bytes, offset 0
BatchNumber : INT; // 2 bytes, offset 4
Spare16 : INT; // 2 bytes, offset 6
TargetTemp_C : REAL; // 4 bytes, offset 8
HoldTime_s : REAL; // 4 bytes, offset 12
Ingredients : ARRAY[1..8] OF REAL; // 32 bytes, offset 16
CRC32 : DWORD; // 4 bytes, offset 48
Reserved : ARRAY[1..16] OF BYTE; // 16 bytes, offset 52
END_STRUCT;
END_TYPE
Total length: 68 bytes. Place this UDT in both the source and the destination project — either by copying the CPU, sharing the project via TIA Portal Multiuser, or by exporting the type as XML and importing it on the partner station.
Building the Connection (TSAP and IP)
T_SEND and T_RCV require an active connection established by a TCON block. The TCON CONNECT parameter must be of type TCON_IP_v4 (an SDT) and must describe the partner IP, the partner TSAP, and the local TSAP. The TSAP for S7-1200 ISO-on-TCP uses the format 03.xx.yy where the bytes after the leading 0x03 are arbitrary as long as they match on both ends.
| Field | Type | Source PLC | Destination PLC |
|---|---|---|---|
InterfaceId |
HW_ANY | 64 (built-in PROFINET) | 64 (built-in PROFINET) |
ID |
CONN_OUC | 1 | 1 |
ConnectionType |
BYTE | 16#0B (ISO-on-TCP) | 16#0B (ISO-on-TCP) |
ActiveEstablished |
BOOL | TRUE (this side dials) | FALSE (waits for dial) |
RemoteAddress.ADDR[1..4] |
BYTE array | 192.168.0.21 | 192.168.0.20 |
RemoteTsap |
ARRAY[1..16] OF BYTE | 03.01.00 | 03.01.00 |
LocalTsap |
ARRAY[1..16] OF BYTE | 03.01.00 | 03.01.00 |
The S7-1200 CPU occupies slot 1 of rack 0 on the local PROFINET interface. If the connection is established against an external device, set the local TSAP to 01.00 in some peer implementations; with another S7-1200, the convention is 03.01 to indicate "S7 CPU slot 1". The pair on both PLCs must mirror exactly — TSAPs are byte-for-byte compared by the stack.
Wiring TCON, T_SEND, T_RCV in the Program
The send and receive sides are mirror images. Below is the minimum ST code for the active side (PLC_A) that owns the connection.
// Instance DBs
InstTCON : TCON;
InstTSEND : T_SEND;
InstTRCV : T_RCV;
InstTDISCON: TDISCON;
DATA_OUT AT %DB20 : UDT_RecipePayload; // host DB with one UDT tag
// Connection descriptor (filled in editor or in startup OB)
Cfg : TCON_IP_v4;
IF "FirstScan" THEN
Cfg.InterfaceId := 64;
Cfg.ID := 1;
Cfg.ConnectionType := 16#0B;
Cfg.ActiveEstablished := TRUE;
Cfg.RemoteAddress.ADDR[1] := 192;
Cfg.RemoteAddress.ADDR[2] := 168;
Cfg.RemoteAddress.ADDR[3] := 0;
Cfg.RemoteAddress.ADDR[4] := 21;
Cfg.RemoteTsap[1] := 16#03; Cfg.RemoteTsap[2] := 16#01; Cfg.RemoteTsap[3] := 16#00;
Cfg.LocalTsap[1] := 16#03; Cfg.LocalTsap[2] := 16#01; Cfg.LocalTsap[3] := 16#00;
END_IF;
// 1) Establish the connection once at startup
InstTCON(
REQ := "FirstScan",
ID := 1,
CONNECT := Cfg,
DONE => "Conn_Done",
BUSY => "Conn_Busy",
ERROR => "Conn_Err",
STATUS => "Conn_Status");
// 2) Trigger a send on rising edge of "SendTrigger"
InstTSEND(
REQ := "SendTrigger",
ID := 1,
LEN := SIZEOF(DATA_OUT), // 68 bytes
DATA := DATA_OUT, // UDT pointer - the pin accepts it
DONE => "Snd_Done",
BUSY => "Snd_Busy",
ERROR => "Snd_Err",
STATUS => "Snd_Status");
The passive side (PLC_B) uses the same TCON configuration with ActiveEstablished := FALSE and exposes a T_RCV block to copy the bytes into its own DB tag of the same UDT.
The T_SEND / T_RCV Pin Reference
| Pin | Direction | Type | Function |
|---|---|---|---|
| REQ | IN | BOOL | Rising edge starts the send operation. |
| ID | IN | CONN_OUC (WORD) | Reference to the connection created by TCON. Must match the TCON ID. |
| LEN | IN | UINT | Number of bytes to send. For UDT-typed DATA, the editor fills this with the UDT length; it can still be shortened for partial sends. |
| DATA | IN_OUT | UDT pointer (VARIANT in V15+) | Source tag. Must be a UDT-typed variable; a plain DB reference is rejected. |
| DONE | OUT | BOOL | One-shot TRUE on successful completion. |
| BUSY | OUT | BOOL | TRUE while the job is in progress. |
| ERROR | OUT | BOOL | TRUE if an error occurred during the job. |
| STATUS | OUT | WORD | Error / status code, see table below. |
Common STATUS Codes for T_SEND / T_RCV / TCON
| STATUS (hex) | Source | Meaning | Remedy |
|---|---|---|---|
| 0000 | T_SEND / T_RCV | No error. | — |
| 7000 | All | No job active (idle state). | Trigger REQ. |
| 7001 | All | Job in progress (first call). | Wait, poll BUSY. |
| 7002 | All | Job in progress (subsequent call). | Wait. |
| 8085 | T_SEND / T_RCV | LEN = 0 or LEN > size of DATA. | Adjust LEN to SIZEOF(DATA tag). |
| 80A1 | T_SEND / T_RCV | Connection not established; ID not found. | Run TCON first; verify ID value matches TCON.ID. |
| 80A3 | T_SEND | Internal: tried to use a connection that is being torn down. | Wait, then re-trigger after TDISCON completes. |
| 80A7 | All | Local resource issue (too many concurrent jobs). | Reduce number of parallel T_SEND / T_RCV instances. |
| 80B5 | All | CONNECT parameter has invalid value. | Inspect the TCON_IP_v4 fields; reinitialize Cfg. |
| 80C3 | T_SEND / T_RCV | Temporary lack of resources on the partner. | Retry; check partner CPU load. |
| 80C4 | T_SEND | Remote side has not yet called T_RCV / passive open. | Verify partner program is running and CPU is in RUN. |
| 80C5 | All | Connection aborted by remote. | Check partner diagnostic buffer; verify TSAP and IP. |
Alternative Path 1 — PUT / GET over S7 Communication
If the goal is to write a few contiguous bytes into the partner's DB without running any program on the partner, S7 Communication is the simpler route. PUT and GET are asymmetric (active side calls PUT, reads via GET). They are configured as follows:
- On both CPUs, enable "Permit access with PUT/GET communication" in Device configuration → Protection & Security → Connection mechanisms. This is disabled by default in firmware 4.x and is the most common cause of a silent 80C4 or 80A1 on the first deployment.
- On the active CPU, drag a PUT instruction and point its ADDR_1 / ADDR_2 pins at the partner DB area using absolute addressing (e.g.,
P#DB20.DBX0.0 BYTE 68). - PUT and GET use absolute pointers on the partner — they do not require a UDT on the partner side because the partner code is what must interpret the bytes, not the editor.
PUT/GET runs over the S7 port (TSAP 01.01 on the partner for the CPU slot) and a single S7 connection can carry up to 160 bytes per call on most CPU 121x variants. For larger payloads, fragment the transfer or use T_SEND / T_RCV instead.
Alternative Path 2 — TSEND_C / TRCV_C (TCP or ISO-on-TCP)
TSEND_C combines TCON + TSEND + TDISCON into a single FB. It accepts the same UDT-typed DATA pin and uses a TCON_IP_v4 (or TCON_IP_RFC1006 for ISO-on-TCP) SDT for the CONNECT parameter. It is the recommended block on S7-1200 firmware 4.0 and later because the TCON / TSEND split requires three FBs and three instance DBs. The TIA Portal online help for TSEND_C explicitly notes: "The DATA parameter must be a UDT or a structured tag whose structure is identical on the active and passive station."
Alternative Path 3 — UDP with TUSEND / TURCV
For broadcast-style recipes from one S7-1200 to many listeners, UDP is more efficient. TUSEND accepts a UDT-typed DATA, but with one important difference: the partner IP and port are passed at runtime through a TADDR_Param SDT, not baked into a CONNECT at startup. The trade-off is that UDP does not guarantee delivery, so a sequence number and an ACK should be implemented in user code.
Why "Send the Whole DB" Is Not a Direct Operation
There is no instruction that transmits the entire DB container — the DB is a memory object, not a transferable value. The contents of a DB are the transferable value. The S7-1200 communication stack works at byte level, so a DB1 of 200 bytes and a DB2 of 200 bytes with the same internal structure are functionally identical as far as the wire is concerned. The partner receives 200 bytes and writes them into whatever tag its T_RCV pin points at, as long as the receiving tag has the same UDT (or at least the same byte length and the receiving program knows the layout).
Diagnostic and Verification Procedure
- Online → Accessible devices: confirm both CPUs are reachable on the configured IP. If a CPU is missing, check the PROFINET cable and the IP/subnet mask in the device configuration.
- Online → Online & Diagnostics → Connection diagnostics: the S7-1200 firmware 4.4+ lists every active connection with its ID, partner IP, TSAP, state, and the count of bytes sent / received. Confirm the TCON connection shows state "Established".
-
Watch table: open a watch table on both CPUs, force
SendTrigger = TRUEon the active side, and observe the sequence:-
Snd_Busyrises, thenSnd_Donerises,Snd_Status = 0. - On the passive side,
Rcv_NewDatarises, the receive DB tag matches the send DB tag byte-for-byte.
-
- Diagnostic buffer: if any of the STATUS codes above appear, open Online → Online & Diagnostics → Diagnostic buffer on the failing CPU. The most useful events are "Connection aborted" (event ID 0x4E1B / 0x4E1C) and "ISO-on-TCP frame rejected" (0x4E25).
- Wireshark (optional): mirror the PROFINET port of one CPU on a managed switch and filter on TCP port 102. The TPDU payloads will appear with the configured TSAP. If the TCP handshake (SYN / SYN-ACK / ACK) completes but no TPDU follows, the problem is in the application (bad CONNECT, wrong ID, UDT length mismatch).
Troubleshooting Matrix
| Symptom | First-Check | Typical Root Cause | Fix |
|---|---|---|---|
| Compiler rejects DATA pin: "Data block must be of SDT or UDT type". | DATA pin source tag. | Pin points at a DB name, not a UDT tag inside a DB. | Create a UDT, declare a tag of that UDT inside a DB, point the pin at the tag. |
| TCON DONE rises, T_SEND stays in 7000 forever. | REQ trigger. | REQ is a level, not a pulse, or trigger DB tag not reset. | Use a one-shot (edge detect) on REQ; reset the trigger once DONE = TRUE. |
| T_SEND ERROR rises with STATUS = 80C4. | Partner CPU state. | Partner CPU is in STOP, or partner has not been configured for "Permit access with PUT/GET", or partner program has not yet called TCON / TSEND_C. | Put partner in RUN, verify protection setting, verify partner program code. |
| STATUS = 80A1. | ID parameter. | T_SEND.ID does not match TCON.ID. | Use a single shared constant or symbolic ID tag for both blocks. |
| STATUS = 80B5. | CONNECT fields. | RemoteAddress or Tsap arrays are zero-initialized or out of range. | Reinitialize Cfg in startup OB; check TSAP byte length (3 used bytes, the rest zero-padded). |
| Data arrives but values are scrambled. | UDT byte layout. | Source and destination UDTs are different (member order, alignment, packed BOOLs). | Re-export UDT and replace on both stations. Avoid BOOLs in UDTs destined for transmission — pack them into BYTE/WORD/DWORD to guarantee byte alignment. |
| Connection drops after a few minutes. | Keepalive / TCON timeout. | Partner CPU goes to STOP, cable is intermittent, or watch dog in the network switch. | Configure PROFINET interface "Keep-alive" time, check port security on the switch. |
UDT Best Practices for Transmission
- Avoid BOOL members in transmitted UDTs: TIA Portal packs BOOLs into byte boundaries unpredictably between firmware versions. A tag with three BOOLs followed by an INT may shift alignment between V16 and V17. Use BIT, BYTE, WORD, DWORD, INT, DINT, REAL, CHAR, or arrays of those instead.
- Pad to a 4-byte boundary: receiver code becomes more robust when the payload size is a multiple of 4. Use a "Reserved" array at the end of the UDT to round up.
- Add a CRC32: even for in-LAN traffic, a CRC32 stored in the last 4 bytes catches UDT version skew between projects.
- One UDT, two projects: store the UDT in a TIA Portal library at the project level. Both stations reference the same library copy. When the UDT is updated, accept the "Type conflict resolution" prompt in both projects at the same time.
- Symbolic access: enable "Symbolic access only" on both DBs holding UDT instances. This is the default for new DBs in V17+ and prevents the editor from accepting a DB-only pointer.
Capacity Limits by S7-1200 CPU
| CPU | Order Number (MLFB) | Max Concurrent ISO-on-TCP Connections | Max Bytes per T_SEND Call |
|---|---|---|---|
| CPU 1211C | 6ES7211-1AE40-0XB0 | 8 | 2048 |
| CPU 1212C | 6ES7212-1AE40-0XB0 | 8 | 2048 |
| CPU 1214C | 6ES7214-1AG40-0XB0 | 8 | 2048 |
| CPU 1215C | 6ES7215-1AG40-0XB0 | 16 | 2048 |
| CPU 1217C | 6ES7217-1AG40-0XB0 | 16 | 2048 |
These limits reflect the SIMATIC S7-1200 Programmable Controller System Manual values for the V4.x firmware line. The T_SEND / T_RCV payload limit is 2048 bytes per call regardless of CPU; the difference between CPUs is in how many concurrent TCON-managed connections can be open at once. For a 1:1 PLC-to-PLC link this is rarely the binding constraint, but it matters when a single S7-1200 is fanning out to several S7-1500 cells.
Cross-Platform Note: OMRON CP2E "Ethernet Send/Receive Data" FBs
The OMRON CP2E-N CPU family uses a different mechanism but the same architectural idea: a structured payload is declared once and exchanged between two controllers. The Ethernet Send/Receive Data function blocks for the CP2E-N exchange data through the built-in Ethernet port using the FINS protocol over TCP, with the payload declared as a single user-defined structure (equivalent in role to a Siemens UDT). The integration pattern is identical: one FB sends on demand, the other FB receives into a tag of matching layout. When porting a Siemens recipe-transfer routine to an OMRON CP2E-N installation, keep the payload UDT/structure length constant and replicate the data block on both controllers.
Field Commissioning Checklist
- Both CPUs online, both in RUN, both with valid IP/subnet.
- Both projects reference the same UDT version (compare hash of the type XML).
- Both DBs containing the UDT instance have "Symbolic access only" enabled (or "Accessible from HMI/OPC UA" plus "Accessible from partner" where appropriate).
- TCON block executes once at startup; DONE = TRUE; STATUS = 0.
- T_SEND triggered with rising edge; DONE = TRUE; STATUS = 0; Snd_Busy returns to FALSE.
- Partner T_RCV rises "NewData" within one OB1 scan of the send; receive DB tag matches the send DB tag byte-for-byte.
- Run a soak test: trigger 1000 sends, count DONE on each side, confirm no errors and no status codes other than 7000 / 7001 / 7002 during the operation and 0 at completion.
- Pull the network cable on the partner; confirm the source CPU flags ERROR with STATUS = 80A1 / 80C5 within the keepalive timeout; reconnect; confirm auto-recovery within the next TCON cycle.
What does the error "Data block must be of SDT or UDT type" mean in TIA Portal?
It means the T_SEND, TSEND_C, or TUSEND DATA pin is pointing at a global DB (e.g., "DB_Recipe") instead of a tag whose type is a declared UDT or SDT. The editor only accepts type-safe pointers so the receiver can guarantee a matching layout. Declare a PLC data type (UDT), add a tag of that UDT to a DB, and point the DATA pin at the tag rather than at the DB name.
Can I send the entire DB between two S7-1200 PLCs, or only its contents?
Only the contents. There is no instruction that transmits the DB container itself. A T_SEND / T_RCV pair moves a byte stream; on the source side the editor pulls bytes from the UDT tag, on the destination side it writes bytes into a UDT tag of the same length. The destination DB must already exist with the same UDT and must be at least as long as LEN.
Which protocol should I use between two S7-1200 PLCs?
For structured payloads, use ISO-on-TCP with T_SEND / T_RCV (or TSEND_C / TRCV_C). ISO-on-TCP is connection-oriented, gives you a single TCON-managed channel, and supports up to 2048 bytes per call. For simple reads/writes against the partner's DB area, use PUT / GET over S7 Communication, which does not require a partner program. For broadcast or multicast to many listeners, use UDP via TUSEND / TURCV.
What is the difference between a UDT and an SDT in TIA Portal?
A UDT (User-Defined Type) is a PLC data type you create in your project to describe a payload layout. An SDT (System Data Type) is a type pre-installed by TIA Portal — for example, TCON_IP_v4, TADDR_Param, or IF_CONF — and is used as a parameter shape for specific FBs (TCON, TUSEND, etc.). T_SEND's DATA pin accepts UDTs; TCON's CONNECT pin accepts the SDT TCON_IP_v4.
How do I monitor the connection state of T_SEND / T_RCV in the running CPU?
Open an online watch table on the instance DBs of TCON and T_SEND. TCON.DONE indicates the connection is established, TCON.BUSY indicates it is being established, TCON.ERROR plus TCON.STATUS gives the failure code. Once established, T_SEND.BUSY rises during each transfer, T_SEND.DONE indicates a successful send, and T_SEND.STATUS = 0 confirms no error. For persistent connection monitoring, use the CPU's built-in web server connection page or TIA Portal's "Online & Diagnostics" view.