1. Overview
Modbus TCP simulation in the S7-1200 ecosystem is the standard pre-commissioning step for validating a Modbus client application before connecting to a real field device. A typical engineering workflow uses a Windows-based Modbus server simulator such as ModSim from Win-Tech Software or Modbus Slave from ModbusTools to emulate a remote device, while the S7-1214C DC/DC/DC runs the MB_CLIENT instruction under TIA Portal to read holding registers, read discrete inputs, and write coils.
This reference covers the exact configuration required to:
- Stand up a ModSim TCP server with 6 coils, 1 discrete input, and 7 floating-point holding registers.
- Configure the S7-1200 MB_CLIENT instruction in TIA Portal V17 or later.
- Resolve the two most common field issues:
- Optimized vs. non-optimized data block access for coils and discrete inputs.
- Byte-order (endianness) differences between Modbus register payloads and bit-packed S7 tags.
- Run more than one MB_CLIENT connection to the same server IP using different local/remote ports.
MB_C / MB_M / MB_S instructions instead.
2. Prerequisites
| Item | Specification / Version | Notes |
|---|---|---|
| S7-1200 CPU | S7-1214C DC/DC/DC, FW V4.2 or higher | Native Modbus TCP library available from FW V4.0 onward |
| TIA Portal | V17 / V18 / V19 | MB_CLIENT in the "Instructions > Communication > MODBUS TCP" folder |
| ModSim | ModSim32 (current build) or ModSim from Win-Tech Software | Windows MDI application; free download from win-tech.com |
| Ethernet cabling | Direct or via managed switch | CPU PROFINET port on a single subnet |
| PC IP address | Static, e.g. 192.168.0.10 | Same subnet as the CPU 192.168.0.1 |
| CPU IP address | 192.168.0.1 (default) | Configured in TIA Portal project or via device display |
3. ModSim TCP Server Configuration
ModSim exposes up to 100 simulated slave devices and supports Modbus RTU, ASCII, and TCP. For the S7-1200 use case we run ModSim in Modbus/TCP server mode, listening on port 502.
- Install and start ModSim.
- Select Connection → Connect → Modbus/TCP/IP Server.
- Set the local TCP port to
502(default Modbus TCP port). - Set the Device ID (Unit Identifier / Slave Address) to
1unless the application requires a different slave number. - Configure the data blocks to match the application:
| ModSim Block | Function Code | Quantity | Starting Address | Engineering Meaning |
|---|---|---|---|---|
| Coils (0xxxxx) | FC 01 / 05 / 15 | 6 | 00001 | 6 digital outputs writable from the S7-1200 |
| Discrete Inputs (1xxxxx) | FC 02 | 1 | 10001 | 1 digital input read by the S7-1200 |
| Holding Registers (4xxxxx) | FC 03 / 06 / 16 | 14 (7 × 2 words) | 40001 | 7 IEEE-754 single-precision floats |
Display = 1 + Protocol, so ModSim address 00001 = protocol address 0, address 10001 = protocol address 0 for discrete inputs, and 40001 = protocol address 0 for holding registers.
4. S7-1200 MB_CLIENT Instruction
The MB_CLIENT instruction handles a single TCP connection to a Modbus server. Place one instance per Modbus server (or per port on a shared server). The instruction sits in Program blocks > System blocks > Program resources.
4.1 Instance DB and Static Parameters
Drop MB_CLIENT into a cyclic OB (typically OB1). TIA Portal creates an instance data block automatically. The relevant input parameters are:
| Parameter | Data Type | Value in this Application | Description |
|---|---|---|---|
| REQ | Bool | Edge-triggered flag | Start of a new job on rising edge |
| DISCONNECT | Bool | 0 | 0 = hold connection, 1 = close connection |
| CONNECT_MODE | UInt | 0 (TCP/IP) | Always TCP/IP for Modbus TCP |
| IP1..IP4 | USInt (octet) | 192, 168, 0, 10 | ModSim PC IPv4 address |
| IP_PORT | UInt | 502 | ModSim TCP listening port |
| MB_MODE | USInt | 0 / 1 / 2 | 0 = read, 1 = write, 2 = read/write diagnostics |
| MB_DATA_ADDR | UInt | 0 / 0 / 0 | 0-based start address (coil, DI, or HR) |
| MB_DATA_LEN | UInt | 6 / 1 / 14 | Number of bits or words |
| MB_DATA_PTR | VARIANT | Global DB tag | Pointer to a global DB (see Section 5) |
4.2 Status / Error Codes
The output DONE, BUSY, and ERROR bits indicate the job state. The STATUS word is filled with a hex value on completion. Common values to watch for in a ModSim test environment:
| STATUS (hex) | Meaning | Typical Cause |
|---|---|---|
| 0x0000 | Job completed, no error | Successful read/write |
| 0x7000 | No job active, connection idle | Waiting for REQ |
| 0x7001 | First call after REQ, job in progress | Normal during execution |
| 0x7002 | Additional call, job still in progress | Normal during execution |
| 0x80C8 | Connection aborted by remote | ModSim closed socket, wrong port, or firewall |
| 0x8380 | MB_DATA_ADDR / MB_DATA_LEN invalid | Address/length out of range, count = 0 |
| 0x8382 | MB_DATA_PTR is not a global DB or wrong type | Pointer to non-DB memory, see Section 5 |
| 0x8383 | MB_DATA_LEN exceeds 100 words / 800 bits | Job too large for one request |
5. Optimized vs. Non-Optimized Data Block
This is the single most common source of confusion in the S7-1200 Modbus TCP workflow. The behavior observed in the field — coils and discrete inputs only accessible with an optimized DB — is the documented contract of the MB_CLIENT instruction.
5.1 Why Coils and Discrete Inputs Require an Optimized DB
The TIA Portal documentation for MB_CLIENT states explicitly:
"For the MB_DATA_PTR parameter, the data block used must be a global data block with optimized access. The number of bits addressed must be divisible by 8."
Two reasons drive this:
- Bit-packing into bytes. A coil request for <8 bits would need to be combined with neighbouring bits to align on a byte boundary. With a non-optimized DB the offset of any tag is the absolute byte address the engineer specified, which can place a 6-bit coil field in the middle of a byte. The instruction packs the request into a 16-bit Modbus word internally, so it writes back only the relevant 8 bits; the residual bits in the destination byte are read as zero and then the instruction is forced to perform a read-modify-write on the DB — which is not supported against non-optimized symbolic tags in older firmware.
-
Symbolic consistency check. With optimized access, the compiler knows the symbolic width of every tag and the run-time system can perform the bit-to-byte mapping deterministically. With standard (non-optimized) access, the bit field is interpreted as an absolute address, and the library returns
0x8382for bit-pointer jobs.
5.2 Recommended DB Layout
Create a single global DB named Modbus_Data with Optimized block access enabled (Properties > Attributes > Optimized block access = checked). The internal layout that consistently works for the application in question:
DATA_BLOCK "Modbus_Data"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
NON_RETAIN
STRUCT
// Coils (FC 01 / 05 / 15), 6 bits packed in the first byte
Coils : BYTE; // bits 0..5 = coil 0..5
Reserved1 : BYTE; // pad to 16-bit alignment
// Discrete inputs (FC 02), 1 bit
DiscreteInputs : BYTE; // bit 0 = DI 0
Reserved2 : BYTE; // pad to 16-bit alignment
// Holding registers (FC 03 / 06 / 16), 7 floats = 14 words
HR_Word_0 : WORD; // Float 0 low word
HR_Word_1 : WORD; // Float 0 high word
HR_Word_2 : WORD; // Float 1 low word
HR_Word_3 : WORD; // Float 1 high word
...
HR_Word_13 : WORD; // Float 6 high word
END_STRUCT;
END_DATA_BLOCK
Three separate MB_CLIENT calls reference three different offsets in the same DB:
-
MB_DATA_PTR := "Modbus_Data".Coils,MB_DATA_LEN := 8(padded to byte boundary) -
MB_DATA_PTR := "Modbus_Data".DiscreteInputs,MB_DATA_LEN := 8 -
MB_DATA_PTR := "Modbus_Data".HR_Word_0,MB_DATA_LEN := 14
MB_DATA_LEN = 1 and a target that points at an absolute memory bit in a separate, optimized byte container. This is not recommended for production code; switch the DB to optimized access and align the rest of the project accordingly.
6. Modbus Data Types, Endianness, and Byte Swapping
The second field issue is the apparent byte-order difference between holding-register floats and the bit-packed coil/DI area. The explanation is that the two requests use different Modbus function codes and have different contract semantics.
6.1 Function-Code Map
| Modsim Object | Function Code | PDU Unit | Byte Order in Modbus PDU | S7 Tag Type |
|---|---|---|---|---|
| Coil | FC 01 / 05 / 15 | 1 bit per coil, packed 8 per byte | LSB-first within byte; bytes in network order | Bool / Byte |
| Discrete Input | FC 02 | 1 bit per DI, packed 8 per byte | LSB-first within byte; bytes in network order | Bool / Byte |
| Holding Register | FC 03 / 06 / 16 | 16-bit word | Big-endian (high byte first) per word | Word / Int / Real |
| Input Register | FC 04 | 16-bit word | Big-endian per word | Word / Int / Real |
6.2 Why the Float "Looks Big-Endian" and Coils "Look Little-Endian"
Both observations are correct and the two are not contradictory — they describe different data granularities:
-
Holding registers (FC 03). The Modbus specification requires a 16-bit register, and the wire format places the high byte first (big-endian at the byte level). For a 32-bit IEEE-754 float occupying two consecutive registers, the standard network byte order is: high-word high-byte, high-word low-byte, low-word high-byte, low-word low-byte. When this stream is loaded into a Siemens
REALtag by MB_CLIENT, the library concatenates the two 16-bit words and presents the value to the S7-1200 in the format the CPU expects. -
Coils / discrete inputs (FC 01, FC 02). The unit on the wire is a bit. Modbus packs up to 8 bits per byte in the response payload, and within each byte the LSB of the byte carries coil 0 (or DI 0), the next bit carries coil 1, and so on. There is no "endianness" in the float sense; this is a bit-position convention. When the S7-1200 unpacks the response into the optimized DB, the bit at
Coils.%X0is the first bit of the first byte — which is what the application sees as "little-endian at the bit level".
6.3 Practical Example
For the value 23.5 (IEEE-754 single = 0x41BC0000) at HR address 0/1:
| Element | Bytes on Wire | Notes |
|---|---|---|
| HR 0 high byte | 0x41 | Big-endian word 0 high |
| HR 0 low byte | 0xBC | Big-endian word 0 low |
| HR 1 high byte | 0x00 | Big-endian word 1 high |
| HR 1 low byte | 0x00 | Big-endian word 1 low |
For coil 0 = TRUE, coil 1 = FALSE, coil 2 = TRUE, coil 3..5 = FALSE at coil address 0:
| Bit Position | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|---|---|---|---|---|---|---|---|---|
| Coil index | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| Value | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 |
| Byte (hex) | 0x05 | |||||||
The CPU tag "Modbus_Data".Coils.%X0 reads TRUE, %X1 reads FALSE, %X2 reads TRUE, matching the wire format.
6.4 If You Need Different Bit Order
ModSim exposes a Sim swap dialog (under the Sim menu) that lets the test engineer mirror coil bytes or register words for regression testing when a real field device has a non-standard layout. This does not change Modbus semantics; it changes only how ModSim displays and stores the values locally. On the S7-1200 side, if a target bit must be inverted, do it in user logic rather than in the wire mapping:
// Mirror coils 0..5 from LSB to MSB for a legacy field device
FOR i := 0 TO 5 DO
"ProcessData".CoilOut[i] := "Modbus_Data".Coils.%X(5 - i);
END_FOR;
7. Multiple MB_CLIENT Connections to One Server
The MB_CLIENT help text states: "For each 'MB_CLIENT' connection, a unique server IP address must be specified." This wording is misleading. The actual contract is that each instance must open a unique TCP connection, identified by the 5-tuple of (server IP, server port, client IP, client port, protocol). Modbus TCP servers typically accept many TCP connections per IP, distinguished by the remote port of the client (which is assigned dynamically by the S7-1200 Ethernet stack).
7.1 Three Connections to One ModSim Server
To run three MB_CLIENT instances against the same ModSim IP (192.168.0.10) on different Modbus function sets, simply place three MB_CLIENT blocks with three separate instance DBs. Each gets a different IP_PORT if ModSim is configured for multiple listening ports (ModSim allows up to four TCP server instances per window through the Connection → Connect menu):
| Instance DB | Server IP | Server Port | MB_MODE | Length | Function |
|---|---|---|---|---|---|
| MB_Client_Coils | 192.168.0.10 | 502 | 1 (write) | 6 bits | FC 15 force coils |
| MB_Client_DI | 192.168.0.10 | 502 | 0 (read) | 8 bits | FC 02 read DI |
| MB_Client_HR | 192.168.0.10 | 502 | 0 (read) | 14 words | FC 03 read HR |
All three can be issued from the same ModSim window on the same port 502 because the S7-1200 opens three distinct TCP sockets (different local ports). ModSim sees them as three independent masters and dispatches PDUs to the corresponding data block based on function code.
7.2 Alternative: Multiple Ports on One Server
Some PC applications (ModSim, Modbus Slave, ModRSsim2) can bind to several ports. The configuration file for ModRSsim2, for example, lets you expose the same data on port 502 and 1502. Splitting work across ports makes Wireshark traces easier to read when multiple masters talk to one server.
8. Step-by-Step Commissioning Procedure
- Verify CPU firmware. In TIA Portal, Online → Accessible devices, read the order number and firmware. Confirm V4.0 or higher.
- Bring ModSim online. Start ModSim, configure three blocks (6 coils, 1 DI, 7 floats) and Connection → Connect → Modbus/TCP/IP Server. Confirm "Listening on 0.0.0.0:502" in the status bar.
- Compile and download the TIA project. Include the Modbus TCP library if not part of the standard distribution.
-
Watch the status word. First job returns
0x7001(in progress), then0x0000on success. If the status is0x80C8, the TCP connection failed — check the PC firewall and IP reachability withping 192.168.0.10from the CPU's Web server or an HMI diagnostic page. -
Force a coil in ModSim (coil 0 = TRUE) and verify the S7-1200 tag
Modbus_Data.Coils.%X0updates on the next read cycle. -
Write a float to HR 0 in the S7-1200 (e.g.
Modbus_Data.HR_Word_0 := 16#41BCandHR_Word_1 := 16#0000for 23.5) and confirm the value appears in ModSim's holding register display. -
Watch the byte order in ModSim: with the value 23.5, ModSim should display
0x41BC, 0x0000as two consecutive 16-bit registers. -
Cycle the connection by toggling
DISCONNECT= 1, then = 0. The status should return to0x0000within a few scans.
9. Verification Checklist
| Check | Expected Result | Pass/Fail Criteria |
|---|---|---|
| TCP connect | STATUS = 0x0000 within 1 s | Any 0x80Cx is a failure |
| Read HR | Float value visible in ModSim and on HMI tag | Round-trip accuracy ± 1 ULP |
| Write coils | ModSim coil display reflects S7 tag | Bits 0..5 only; bits 6..7 always 0 |
| Read DI | S7 tag updates on read cycle | No glitch during poll |
| Multi-client | Three instance DBs all in 0x0000 | No 0x80C8 between them |
| Disconnect/Reconnect | Status cycles 0x0000 → 0x7000 → 0x0000 | No CPU STOP |
10. Troubleshooting Matrix
| Symptom | Likely Root Cause | Remedy |
|---|---|---|
| Coils/DI read returns all zero; HR read works | DB not optimized; bit count not divisible by 8 | Set S7_Optimized_Access := 'TRUE'; pad length to 8 |
| STATUS = 0x8382 on every call | Pointer does not reference a global DB tag | Use a global DB; remove POINTER TO indirection |
| STATUS = 0x80C8 immediately | ModSim not started, wrong port, firewall | Start ModSim; allow port 502 in Windows Firewall |
| Float read back is corrupted (NaN, sign-flipped) | Word-swap mismatch on legacy device | Use Sim → Swap in ModSim; verify against manual |
| Coil bit reversed in S7 tag | Bit position within byte | Reference coils via %X index, not by absolute address |
| Only one of three MB_CLIENTs works | Connection resource limit reached | Reduce active connections; verify CPU max in Device configuration |
| STATUS = 0x8383 | Request length > 100 words / 800 bits | Split into multiple jobs |
| ModSim rejects connection after first run | ModSim single-client mode | Use File → New Window to add another ModSim instance |
11. Field-Proven Caveats
- ModSim32 vs ModSim. Win-Tech's ModSim is the free MDI application. ModSim32 is a more recent build with extended support for multi-port TCP/IP servers and binary register views. For complex test benches prefer ModSim32.
-
Watchdog on long polls. If the S7-1200 is busy or the project is large, the MB_CLIENT may time out (default ~5 s). Increase the
TIME_OUTparameter and shorten the cycle to keep below the timeout. -
Port 502 requires admin on Windows. Only the first application to bind port 502 succeeds; subsequent applications must use a different port. If the S7-1200 shows
0x80C4, the local port for that instance is already in use. - Endianness of "REAL" tags. The S7-1200 stores REAL in standard IEEE-754 single-precision, big-endian at the byte level. The Modbus wire format is also big-endian per register. The combined layout is big-endian at the byte level but big-endian at the word level too, which is why many engineers observe "big-endian on the wire". The library handles the mapping automatically; do not insert manual SWAP operations on the S7 side.
- ModSim documentation link. For additional point types and connection modes refer to the Win-Tech ModSim product page and the Advantech IAG FAQ on ModSim usage for the canonical Configuration → Connect → Modbus/TCP sequence.
12. FAQ
Why do coils and discrete inputs only work with an optimized data block in MB_CLIENT?
The MB_CLIENT instruction explicitly requires a global data block with optimized access for bit fields. Non-optimized DBs return STATUS 0x8382 because the library cannot guarantee byte alignment of symbolic bit tags. Enable S7_Optimized_Access := 'TRUE' on the global DB and pad the request length to a multiple of 8.
Are Modbus holding-register floats big-endian or little-endian?
Modbus is big-endian at the byte and word level. Each 16-bit register transmits its high byte first, and a 32-bit float uses two consecutive registers in the same big-endian order. The S7-1200 REAL tag matches this layout directly through MB_CLIENT, so no byte swap is required.
Can I run more than one MB_CLIENT to the same ModSim IP?
Yes. Each MB_CLIENT opens its own TCP socket and ModSim accepts multiple connections on the same port (502). The help text "unique server IP" is misleading; the real constraint is unique TCP connections, not unique IPs. The S7-1200 supports up to 8 concurrent Modbus TCP connections by default.
What STATUS value indicates the TCP connection has failed?
STATUS = 0x80C8 means the connection was aborted by the remote side or never established. Verify ModSim is started, port 502 is allowed in the Windows Firewall, and the PC IP is reachable from the S7-1200 with ping. The full Siemens status code reference is in the S7-1200 System Manual, Chapter on Modbus TCP.
Which simulator should I use for a multi-slave test bench?
For simple single-server tests, ModSim from Win-Tech Software is sufficient. For larger benches with multiple slaves, Modbus Slave from ModbusTools can simulate up to 100 slaves in 100 windows, which is the standard choice for production-line regression testing.