Troubleshooting S7-1200 MB_CLIENT: Resolving Modbus TCP Input Register Read Failure
When an S7-1200 PLC communicates with a third-party Modbus TCP server (such as a heat-pump gateway module, energy meter, inverter, or robot controller), the MB_CLIENT instruction must be configured to a tighter set of rules than many engineers expect. One of the most common field failures is the PLC returning successful-looking status codes (7005, 7006) yet never updating the destination tags. This article walks through the root cause, the precise parameter that must be changed, and the supporting diagnostics that confirm the fix.
1. Problem Description
A heat-pump control module exposes its telemetry as a Modbus TCP server on a fixed IP address and the standard Modbus TCP port 502. The PLC is configured as a Modbus TCP client and polls a single input register every cycle:
- CPU: SIMATIC S7-1200 (any firmware that includes the
MB_CLIENTinstruction, i.e., firmware V4.0 and later; instructions also ship with the "Modbus TCP" library for V4.x and the TIA Portal Modbus library for V13 SP1+). - Function code target: 0x04 (Read Input Registers).
- Modbus address: 30001 (the first input register of the heat pump's register map).
- Connection: passive connection from the S7-1200 to the heat-pump gateway IP, port 502.
- Mode: 0 (single read, single write per call).
- Data length: 1 register, mapped to a 16-bit INT tag.
When the MB_CLIENT block is executed, the connection establishes without an error: status output 7005 (sending request) is followed by 7006 (receiving response) on every cycle. Despite the clean handshake, the destination tag never updates. A separate Modbus master running on a PC (LabVIEW or QModMaster) reads the same register, the same address, and the same function code and obtains the correct value. This asymmetry is the diagnostic fingerprint of an S7-1200 Modbus TCP configuration error, not a wiring or gateway fault.
2. System Architecture
The system consists of three nodes on a single Ethernet segment:
-
Heat-pump gateway module – embedded Modbus TCP server. The vendor documentation lists register
30001as the first 16-bit input register (function code 0x04). - SIMATIC S7-1200 CPU – Modbus TCP client. The CPU is the active connection initiator; it must open the TCP session to port 502 of the gateway.
- Engineering / diagnostic PC – used to validate the register map with a third-party Modbus master (LabVIEW, QModMaster, Modbus Poll, etc.) before bringing the PLC online.
Modbus TCP/IP itself is a thin encapsulation of classic Modbus over TCP, with a fixed destination port (502) and a 7-byte MBAP (Modbus Application Protocol) header prepended to the standard Modbus PDU. The MBAP header contains a Transaction Identifier, Protocol Identifier (always 0x0000 for Modbus), Length, and the Unit Identifier. The Unit Identifier is the field that is at the heart of this failure mode.
3. MB_CLIENT Block – Required Parameters
The MB_CLIENT instruction block is found in the TIA Portal library under Communication > Other > MODBUS TCP (or in the SIMATIC Modbus TCP library for older projects). The block has the following pinout, and each pin must be reviewed whenever a read silently fails:
| Pin | Direction | Data Type | Function | Common Field Value |
|---|---|---|---|---|
| REQ | Input | BOOL | Rising edge triggers a new transaction | Toggle from a cyclic clock or function call |
| DISCONNECT | Input | BOOL | Forces the TCP connection closed | FALSE in normal operation |
| CONNECT | Input | TCON_IP_V4 | Connection description (IP, port, local TSAP, remote TSAP) | Remote IP = heat-pump module; Remote port = 502 |
| MB_MODE | Input | USINT | 0 = read, 1 = write, 2 = write multiple | 0 (read) |
| MB_DATA_ADDR | Input | UINT | Starting Modbus address (0-based for Siemens; mapping covered in §5) | 30001 (0-based: 0 or 1 depending on register family) |
| MB_DATA_LEN | Input | UINT | Number of registers / bits / coils | 1 (single 16-bit input register) |
| MB_DATA_PTR | InOut | VARIANT | Pointer to the data area (must be a standard tag, not a DB element of an optimized block) | Tag "RegisterValue" of type INT |
| MB_UNIT_ID | Input | USINT | Modbus Unit Identifier (slave address). 0xFF for pure Modbus TCP, but most gateways require 1 | 1 |
| DONE | Output | BOOL | TRUE for one cycle on successful completion | Monitor in HMI / logic |
| BUSY | Output | BOOL | TRUE while transaction is in progress | FALSE = idle |
| ERROR | Output | BOOL | TRUE if the last transaction failed | Latch with a marker |
| STATUS | Output | WORD | Detailed error / state code | 7005, 7006 = active; 0 = idle success |
| CONNECT_ID | Input | UDINT | Unique connection identifier across all MB_CLIENTs | 1, 2, 3… |
MB_DATA_PTR must point to a tag declared in a standard (non-optimized) data block or a global PLC tag. If the source DB has been switched to "optimized" access, the MB_CLIENT returns STATUS = 818B or similar access errors.4. Modbus TCP/IP Fundamentals
Modbus TCP carries the classic Modbus PDU inside a TCP segment. A full request frame is structured as:
[MBAP Header — 7 bytes][Function Code — 1 byte][Data — N bytes]
| Transaction ID (2) |
| Protocol ID = 0x0000 (2) |
| Length (2) |
| Unit ID (1) |
The relevant constants are:
- Port: 502 (IANA-assigned, reserved for Modbus TCP). The heat-pump gateway listens on this port.
- Protocol ID: 0x0000 for Modbus; anything else is rejected.
- Unit Identifier (Unit ID): For Modbus TCP, the MBAP header is the equivalent of the RTU slave address. 0xFF (255) is reserved for "no slave address used" (pure Modbus TCP), but many Modbus TCP gateways are actually Modbus RTU devices behind an Ethernet-to-serial converter, and they will reject anything other than 1–247.
The function code for reading input registers is 0x04. The data address field is zero-based in the wire protocol but most Modbus documentation uses the human-friendly 1-based numbering shown below:
| Register Class | Function Code | Modicon 5-Digit Address | Wire Address (0-based) | Access |
|---|---|---|---|---|
| Coil | 01, 05, 15 | 00001–09999 | 0x0000–0xFFFE | Read/Write |
| Discrete Input | 02 | 10001–19999 | 0x0000–0xFFFE | Read-only |
| Holding Register | 03, 06, 16 | 40001–49999 | 0x0000–0xFFFE | Read/Write |
| Input Register | 04 | 30001–39999 | 0x0000–0xFFFE | Read-only |
5. Siemens Modbus Address Mapping – The "Mystifying" Part
The MB_DATA_ADDR pin on MB_CLIENT does not accept the 5-digit Modicon address. It expects the zero-based offset within the chosen register class. The mapping is:
| Register Class | Mode Parameter | Modicon Address | MB_DATA_ADDR Value | Resulting Function Code |
|---|---|---|---|---|
| Coil (read) | MB_MODE = 0 | 00001 | 0 | 0x01 |
| Coil (read) | MB_MODE = 0 | 00100 | 99 | 0x01 |
| Discrete Input (read) | MB_MODE = 0 | 10001 | 0 | 0x02 |
| Holding Register (read) | MB_MODE = 0 | 40001 | 0 | 0x03 |
| Holding Register (read) | MB_MODE = 0 | 40100 | 99 | 0x03 |
| Input Register (read) | MB_MODE = 0 | 30001 | 0 | 0x04 |
| Input Register (read) | MB_MODE = 0 | 30050 | 49 | 0x04 |
To read input register 30001, MB_DATA_ADDR = 0 with MB_MODE = 0 issues function code 0x04. To read 30050, use MB_DATA_ADDR = 49. The Siemens documentation rarely explains this offset rule, which is the source of the confusion described in the problem statement: a third-party master shows the read is on function code 0x04 (input registers), but the Siemens manual only shows how to populate MB_DATA_ADDR for coils and holding registers. The mapping is implicit in the example offsets rather than spelled out in a table.
To verify, capture the outgoing TCP stream with Wireshark (filter tcp.port == 502). The request from the S7-1200 should contain function code 0x04 with starting address 0x0000 for the first input register. If the captured frame shows 0x03, the wrong register class is being targeted.
6. Root Cause: MB_UNIT_ID Default Behaviour
The MB_CLIENT instruction's MB_UNIT_ID input is initialized to 0xFF (255) in most TIA Portal versions. This is the correct value for a pure Modbus TCP server (e.g., a modern PLC, an instrument with native Modbus TCP, or Universal Robots' controller). However, when the heat-pump gateway module is an Ethernet-to-serial converter that bridges Modbus TCP to an internal Modbus RTU bus, the Unit ID is treated as the RTU slave address. Many of these gateways ignore or refuse requests where the Unit ID is 255.
Symptoms of an incorrect Unit ID are very specific:
- No
ERRORoutput – the TCP layer succeeds. - Status word cycles 7005 → 7006 every transaction.
- Data never changes – the gateway either discards the request, or returns a Modbus exception code 0x0A (Gateway Path Unavailable) or 0x0B (Gateway Target No Response) that the Siemens stack treats as a "silent" read (no exception is propagated because the request never reached a real Modbus device).
This matches the observed behaviour exactly: the engineer reported that 7005 (sending) and 7006 (receiving) cycle indefinitely with no error, but the 16-bit INT variable is never updated.
7. Status Code Reference
The STATUS output of MB_CLIENT is only valid for one scan after BUSY goes FALSE, so it must be latched in a flag word as soon as the transaction completes. A partial reference table:
| Status (Hex) | Status (Dec) | Meaning | Action |
|---|---|---|---|
| 0x0000 | 0 | Idle, no error | None |
| 0x7000 | 28672 | Wait, no active job | Trigger REQ |
| 0x7001 | 28673 | Wait for connection establishment | Check network / IP / port |
| 0x7002 | 28674 | Wait for connection confirmation | Check CONNECT data block |
| 0x7003 | 28675 | Wait for partner to connect | Server side, not client |
| 0x7004 | 28676 | Initializing | Transient |
| 0x7005 | 28677 | Sending request | Normal in-flight state |
| 0x7006 | 28678 | Receiving response | Normal in-flight state |
| 0x7007 | 28679 | Disconnection in progress | Transient |
| 0x80C8 | 32968 | Server returned Modbus exception 0x08 (Memory Parity) | Check register address validity |
| 0x80C9 | 32969 | Server returned Modbus exception 0x09 (Gateway Path) | Confirm MB_UNIT_ID and gateway routing |
| 0x80CA | 32970 | Server returned Modbus exception 0x0A (Gateway No Response) | Check downstream RTU device on gateway |
| 0x818B | 33163 | Data pointer references optimized DB | Switch DB to standard access |
| 0x8380 | 33664 | Length error in data area | Verify MB_DATA_LEN ≤ buffer size |
To capture these transients, latch the STATUS value into a retentive word on every falling edge of BUSY:
// Latch last MB_CLIENT status on transaction end
IF #MB_CLIENT_Instance.BUSY AND NOT #BusyPrev THEN
// BUSY just went true - nothing to capture yet
END_IF;
IF NOT #MB_CLIENT_Instance.BUSY AND #BusyPrev THEN
// BUSY just went false - status is final for this transaction
#LastStatus := #MB_CLIENT_Instance.STATUS;
#LastError := #MB_CLIENT_Instance.ERROR;
END_IF;
#BusyPrev := #MB_CLIENT_Instance.BUSY;
8. Step-by-Step Fix
-
Open the DB or FB that owns the
MB_CLIENTcall. Locate the instance DB generated for the block (e.g.,MB_CLIENT_DB) or the static instance inside an FB. -
Identify the
MB_UNIT_IDinput pin on the block call. In TIA Portal V13 SP1 through V18, this is an IN pin of typeUSINTwith a default of255. -
Wire a constant or tag of value
1toMB_UNIT_ID. If the heat-pump gateway expects a different RTU slave address, substitute the value documented in the gateway's register map (typically 1–247). - Recompile and download the program to the S7-1200. TIA Portal will warn if a constant is unbound, but the block will accept the literal.
-
Watch the destination tag. The 16-bit INT tied to
MB_DATA_PTRwill populate within two scan cycles (one to send, one to receive). -
Verify STATUS latches to
0x0000on the next successful read and thatBUSYtoggles cleanly.
Minimum-variable ladder representation of the call (for documentation; use STL/SCL in real projects):
// --- Modbus TCP client call (SCL) ---
"MB_CLIENT_DB".REQ := #TriggerPulse;
"MB_CLIENT_DB".DISCONNECT := FALSE;
"MB_CLIENT_DB".CONNECT := "HeatPump_Connect"; // TCON_IP_V4
"MB_CLIENT_DB".MB_MODE := 0; // Read
"MB_CLIENT_DB".MB_DATA_ADDR := 0; // Input register 30001
"MB_CLIENT_DB".MB_DATA_LEN := 1; // 1 register
"MB_CLIENT_DB".MB_DATA_PTR := "DB_Modbus".InputReg1;
"MB_CLIENT_DB".MB_UNIT_ID := 1; // *** FIX ***
"MB_CLIENT_DB".CONNECT_ID := 1;
9. Verification
After applying the fix, validate from three perspectives:
-
PLC side – In the watch table, add the
MB_DATA_PTRtag, theSTATUSword, and theERRORbit. The tag should update each cycle thatREQis pulsed; STATUS should latch to 0; ERROR should remain FALSE. -
Network side – Run Wireshark on a span port or use the PLC's online diagnostics to confirm the request frame carries:
- Unit ID = 0x01
- Function code = 0x04
- Starting address = 0x0000
- Quantity = 0x0001
- Heat-pump side – If the gateway has a status LED or web interface, confirm the slave port activity is shown. A flashing "RX/TX" per cycle confirms the request reached the downstream RTU device.
10. Common Pitfalls and Best Practices
Beyond the Unit ID fix, several recurring issues appear in the field when commissioning Modbus TCP on S7-1200:
-
Optimized DB access:
MB_DATA_PTRcannot point to an optimized data block. Either change the DB to "standard" access (right-click the DB > Properties > Attributes) or use a global PLC tag. -
Byte-swap on 32-bit values: Modbus registers are big-endian 16-bit words. When the heat pump returns 32-bit floating-point values, the two 16-bit words arrive in big-endian order; the S7-1200 will load them into successive bytes in the same order, so a manual swap is required (use
SWAPor theWordToDoubleWordtrick). -
Byte-swap on 32-bit INT: If the destination is a
DINT, the two 16-bit words end up in the wrong order; a singleSWAPon the assembled 32-bit value corrects it. -
Connection limits: An S7-1200 supports up to 8 simultaneous Modbus TCP connections (CPU-dependent; check the CPU manual for the exact number of open user connections). Each unique
CONNECT_IDconsumes a slot. - Keep-alive on firewalls: Industrial firewalls often drop idle TCP sessions after 5–30 minutes. A poll interval of 1 second or less keeps the session warm; otherwise, plan a reconnect routine.
- Port number not 502: Some heat-pump vendors use a non-standard port (e.g., 5020, 503, or 8899) and may still call it "Modbus TCP." Wireshark on the gateway IP will reveal the listening port.
- Polling rate vs. server capacity: A heat pump's small embedded Modbus TCP stack often tolerates no more than 1–2 requests per second across multiple clients. Do not loop MB_CLIENT at OB1 priority; use a clock-bit or a periodic timer to slow the rate to a respectful 500 ms – 2 s.
11. Diagnostic Decision Matrix
| Symptom | STATUS / ERROR | Most Likely Cause | Fix |
|---|---|---|---|
| Toggles 7005 / 7006 forever, data never updates | No error | MB_UNIT_ID wrong (0xFF instead of 1) | Set MB_UNIT_ID = 1 |
| ERROR = TRUE, STATUS = 80C8 | Modbus exception 08 | Invalid register address (vendor uses 1-based, not 0-based, or vice-versa) | Subtract/add 1 from MB_DATA_ADDR |
| ERROR = TRUE, STATUS = 80C9 | Modbus exception 09 | Gateway cannot route the request (Unit ID does not match downstream device) | Set MB_UNIT_ID to the actual RTU slave address |
| ERROR = TRUE, STATUS = 80CA | Modbus exception 0A | Downstream RTU device is offline | Check wiring, baud rate, parity, slave address on the RTU side |
| ERROR = TRUE, STATUS = 818B | Data pointer invalid | MB_DATA_PTR points to optimized DB | Switch DB to standard access |
| ERROR = TRUE, STATUS = 8380 | Length error | MB_DATA_LEN greater than available data area | Resize data area or reduce length |
| Connection never establishes | STATUS = 7001 / 7002 / W#16#80C1 | IP / port wrong, firewall blocking, or partner not listening on 502 | Ping partner, telnet partner 502, check router ACLs |
| Values read are swapped (e.g., 32-bit DINT has high/low words reversed) | No error | Modbus big-endian vs. S7 little-endian | Apply SWAP after assembling the 32-bit value |
12. Cross-Platform Notes
Other Modbus TCP clients (Moxa MGate series gateways, Universal Robots controller, ProSoft Technology radio gateways) interpret the Unit ID according to the same MBAP standard. When using a Moxa MGate as a Modbus TCP master in a Siemens-to-Modbus-RTU bridge, set the Moxa port's "Slave ID" to match the value placed in MB_UNIT_ID. Universal Robots' Modbus TCP server, in contrast, expects Unit ID 255 in pure Modbus TCP mode and may also use port 502 — see the official Universal Robots documentation for the register map.
ProSoft Technology's Modbus TCP gateway products (e.g., the PTQ family and the RadioLinx RLX-FH radios) follow the same Unit ID rule: the value is preserved across the TCP/RTU boundary unless the gateway is configured to "ignore Unit ID," in which case it forwards requests to a fixed downstream address. Always check the gateway's configuration page.
References (Manufacturer Manuals & Standards)
- ProSoft Technology – Introduction to Modbus TCP/IP (PDF)
- Modbus – Wikipedia (Modbus TCP, MBAP header, function codes)
- Universal Robots – Modbus TCP/IP (developer documentation)
- Moxa – Modbus TCP Gateways (product family overview)
- NXP – Modbus TCP/IP Industrial Communications Software
FAQ
Why does the S7-1200 MB_CLIENT return no error and yet never update the destination tag?
The TCP layer completes successfully (status cycles 7005 / 7006) but the Modbus request is dropped at the gateway because the Unit Identifier in the MBAP header is wrong. Most heat-pump gateways bridge Modbus TCP to Modbus RTU and require MB_UNIT_ID = 1; the Siemens default of 255 is rejected silently. Set MB_UNIT_ID to the downstream RTU slave address.
What is the correct value for MB_DATA_ADDR when the vendor's register is 30001?
Siemens MB_CLIENT uses a 0-based offset within the register class. For input register 30001, set MB_DATA_ADDR = 0; for 30050, set it to 49. The function code (0x04 for input registers) is selected automatically by the combination of MB_MODE and the address range.
How do I capture the transient STATUS value of MB_CLIENT?
Latch the STATUS word into a retentive tag on the falling edge of BUSY. Because STATUS is only valid for one scan after the transaction ends, an un-latched read will almost always see 0. A five-line snippet using a BusyPrev flag is shown in §7.
Why does Wireshark show the request with function code 0x04 but the data still does not update?
Wireshark only confirms the wire-level frame, not the gateway's response. If the gateway returns a Modbus exception (0x0A, "Gateway Target No Response"), the Siemens stack may treat it as a silent no-op. The captured Unit ID byte reveals the real cause: it should match the downstream RTU slave address, typically 1.
Is port 502 mandatory for Modbus TCP?
Port 502 is the IANA-assigned standard for Modbus TCP, but vendors may run the same protocol on alternative ports. If a heat-pump gateway listens on, for example, 5020 or 8899, TIA Portal's TCON_IP_V4 structure must be edited to point to the actual RemotePort value.