Overview
This reference walks through commissioning a Siemens SIMATIC S7-1200 CPU as a Modbus TCP client using the MB_CLIENT instruction, mapping physical digital inputs and outputs into a Modbus register DB array, and configuring the REQ input so that read/write transactions fire automatically without operator intervention. The procedures below extend the official Siemens application example 83130159 with field-tested patterns for clock memory, edge detection, multi-instance sequencing, and retry/fault handling.
The pattern is platform-agnostic enough to be migrated to a S7-1500 with MB_CLIENT V4/V5 or a third-party Modbus master; the DB array layout and the edge-triggered sequencer described below can be reused without modification.
Prerequisites
- CPU 12xx with at least firmware V4.2 (V13 SP1 in TIA Portal). Firmware V4.4 or higher is recommended for the improved
MB_CLIENTerror reporting. - TIA Portal V13 SP1, V14 SP1, V15, V15.1, V16, V17, or V18 installed with the S7-1200 HSP matching the CPU order number (e.g. 6ES7214-1AG40-0XB0).
- CPU PROFINET interface connected to the Modbus server on the same subnet. A managed switch is preferred when the segment is shared with PROFINET traffic.
- A configured Modbus server IP, port (default 502), unit ID, and register map (function codes 03/06/16).
- An empty data block with at least 1024
WORDelements (or two DBs split for read/write) for the register window.
Hardware and CPU Configuration
Two system flags must be enabled in the device configuration before they can be used in user logic:
-
System memory byte – exposes
%MB0through%MB7with predefined bits such as%M0.0First cycle,%M0.1Always TRUE (always 1),%M0.2Always FALSE (always 0), and diagnostic flags (e.g.%M0.3diagnostic status changed). -
Clock memory byte – assigns the byte to any unused
MBaddress (defaultMB100) and exposes ten frequencies from 10 Hz down to 0.5 Hz, with period doubling per bit. See the table below.
Open Devices & Networks > PLC > Properties > System and clock memory, tick Enable system memory byte and Enable clock memory byte, then set the address of the clock memory byte. After saving, the configuration must be compiled and downloaded to the hardware – a software-only download will not activate the bits.
Clock Memory Bit Frequencies
| Bit | Period | Frequency | Typical Use |
|---|---|---|---|
| %MB100.0 | 0.1 s | 10 Hz | Reserved for diagnostic / fast I/O |
| %MB100.1 | 0.2 s | 5 Hz | Reserved |
| %MB100.2 | 0.4 s | 2.5 Hz | Reserved |
| %MB100.3 | 0.5 s | 2 Hz | Fast poll, debug only |
| %MB100.4 | 1.0 s | 1 Hz | Recommended MB_CLIENT REQ |
| %MB100.5 | 2.0 s | 0.5 Hz | Slow poll, low-priority registers |
| %MB100.6 | 4.0 s | 0.25 Hz | Diagnostic heartbeat |
| %MB100.7 | 8.0 s | 0.125 Hz | Watchdog |
Data Block Layout for Modbus Registers
For a register window of 1 to 1024 words, build a global DB with the following structure. The array start index is 1 so that Register[i] directly corresponds to Modbus register i.
DATA_BLOCK "ModbusRegs"
{ S7_Optimized_Access := 'TRUE' }
AUTHOR : eng
FAMILY : comm
VERSION : 0.1
STRUCT
ReadRegs : ARRAY[1..1024] OF WORD; // Holding/input registers from server
WriteRegs : ARRAY[1..1024] OF WORD; // Holding registers written to server
FaultCnt : INT; // Failed transaction counter
OkCnt : INT; // Successful transaction counter
RetryTmr : TON_TIME; // Back-off timer
END_STRUCT;
END_DATA_BLOCK
The ReadRegs block is the destination buffer of the MB_CLIENT read; WriteRegs is the source buffer of the write. With optimized access, pass the array symbolically as P#DB.ModbusRegs.ReadRegs to MB_CLIENT.MB_DATA_PTR. If you must use absolute addressing on a non-optimized DB, use P#DB20.DBX0.0 WORD 1024 for an array starting at byte offset 0.
Mapping Physical I/O to the Word Array
CPU 12xx process image starts at %I0.0 for inputs and %Q0.0 for outputs. The first eight digital inputs can be packed into WriteRegs[1] using a single MOVE from a byte tag, or explicitly with bit assignments.
Method A – MOVE byte to word (recommended)
Declare a temporary BYTE tag (e.g. tmpByte) and copy the input byte IB0 into it, then copy to "ModbusRegs".WriteRegs[1]. This keeps the upper byte of the word at zero and makes the bit pattern predictable for the Modbus server.
"ModbusRegs".WriteRegs[1] := IB0; // Pack %I0.0..%I0.7
"QB0" := "ModbusRegs".ReadRegs[1] & 16#00FF; // Unpack %Q0.0..%Q0.7
Method B – Bit-by-bit assignment
Use this only when the DB array must hold individual bit fields that are not contiguous in the process image. For eight DI, eight explicit coil writes are required on the server side.
"ModbusRegs".WriteRegs[1].%X0 := %I0.0;
"ModbusRegs".WriteRegs[1].%X1 := %I0.1;
...
"ModbusRegs".WriteRegs[1].%X7 := %I0.7;
WORD is little-endian in display but the network order is preserved by the MB_CLIENT instruction, so the symbol-side mapping above is correct.MB_CLIENT REQ Signal Behavior
The REQ input of MB_CLIENT is edge-triggered: a rising transition (0 → 1) starts a single transaction. A static TRUE produces exactly one transaction because the instruction latches the request on the rising edge and ignores the level afterwards. This is the key reason that wiring REQ directly to %M0.1 (Always TRUE) is not a continuous poll – it is a single shot at CPU startup.
| REQ Source | Behavior | Risk |
|---|---|---|
| Static TRUE | One shot at startup | Silent – registers freeze |
| Clock memory 10 Hz | Floods the network | Drops responses, watchdog trips |
| Clock memory 1 Hz | Continuous poll, 1 s period | Safe for most servers |
| P_TRIG on clock | One transaction per pulse | Recommended pattern |
| DONE/ERROR self-retrigger | As fast as the server replies | Use only with stable server |
Recommended auto-trigger wiring
// Cycle 1 Hz pulse into a rising-edge detector
// Then AND with a sequencer enable
%MB100.4 ── P_TRIG ──┬── %DB.Cfg.MB_REQ_Read
│
%DB.Cfg.ReadActive ───────┘
Combine the edge detector with a sequencer so that the REQ of each MB_CLIENT instance only fires when it is its turn. Two naive patterns to avoid:
- Tying
REQto%M0.1with the expectation of continuous polling. It only sends once. - Using the 10 Hz clock without edge detection – every PLC cycle the rising edge is present, so the instruction re-triggers before the previous transaction completes, doubling or quadrupling the load.
Sequencing Multiple MB_CLIENT Instances
The Modbus standard permits only one outstanding transaction per TCP connection. Two MB_CLIENT blocks pointing at the same CONNECT will collide unless they are mutexed by user logic. The simplest robust pattern is a state machine that walks through IDLE → READ → WRITE → IDLE and only releases the next REQ after the previous block reports DONE=1 or ERROR=1.
Sequencer state codes
| State | Code | Action |
|---|---|---|
| IDLE | 0 | Wait one cycle |
| READ_REQ | 10 | Pulse MB_CLIENT_READ.REQ |
| READ_WAIT | 11 | Poll DONE / ERROR |
| WRITE_REQ | 20 | Pulse MB_CLIENT_WRITE.REQ |
| WRITE_WAIT | 21 | Poll DONE / ERROR |
| FAULT | 99 | Log STATUS, start back-off |
// Step through READ then WRITE, never both at once
CASE #State OF
0 : // IDLE
#State := 10;
10 : // READ_REQ – one pulse
"MB_CLIENT_READ".REQ := TRUE;
#State := 11;
11 : // READ_WAIT
"MB_CLIENT_READ".REQ := FALSE;
IF "MB_CLIENT_READ".DONE THEN
"ModbusRegs".OkCnt := "ModbusRegs".OkCnt + 1;
#State := 20;
ELSIF "MB_CLIENT_READ".ERROR THEN
"ModbusRegs".FaultCnt := "ModbusRegs".FaultCnt + 1;
#State := 99;
END_IF;
20 : // WRITE_REQ
"MB_CLIENT_WRITE".REQ := TRUE;
#State := 21;
21 : // WRITE_WAIT
"MB_CLIENT_WRITE".REQ := FALSE;
IF "MB_CLIENT_WRITE".DONE THEN
"ModbusRegs".OkCnt := "ModbusRegs".OkCnt + 1;
#State := 0;
ELSIF "MB_CLIENT_WRITE".ERROR THEN
"ModbusRegs".FaultCnt := "ModbusRegs".FaultCnt + 1;
#State := 99;
END_IF;
99 : // FAULT – 10 s back-off
IF "ModbusRegs".RetryTmr.Q THEN
"ModbusRegs".RetryTmr(IN := FALSE);
#State := 0;
END_IF;
END_CASE;
Connection Parameter Block (TCON_IP_V4)
Both MB_CLIENT instances must reference the same TCON_IP_V4 data block unless you open two TCP connections. Define one DB of type TCON_IP_V4 and pass it to both instances via the CONNECT parameter.
| Parameter | Value (example) | Notes |
|---|---|---|
| InterfaceId | 64 (PROFINET interface 1) | From device configuration |
| ConnId | 1 | Local handle, 1..4095 |
| ActiveEstablished | TRUE | CPU is the active partner |
| RemoteAddress | 192.168.0.20 | Modbus server IP |
| RemotePort | 502 | Standard Modbus TCP port |
| LocalPort | 0 | Any free local port |
MB_CLIENT Error Codes and Status Meanings
When ERROR=1, inspect STATUS. The values below are extracted from the TIA Portal F1 help and the Siemens online help for MB_CLIENT.
| STATUS (hex) | Meaning | Field Action |
|---|---|---|
| 0x0000 | No error | — |
| 0x80C8 | Connection ID invalid | Verify ConnId and ActiveEstablished |
| 0x8380 | Connection in use | Sequencer not mutexing the blocks |
| 0x8381 | Connection lost | Check cabling, server power, ping |
| 0x8382 | Connection refused by server | Wrong port / IP / firewall |
| 0x8383 | Connect timeout | Raise connect timeout, check route |
| 0x8384 | Local port already in use | Set LocalPort := 0 |
| 0x80A1 | Modbus exception 01 (illegal function) | Server does not support that FC |
| 0x80A2 | Modbus exception 02 (illegal data addr) | Register offset out of range |
| 0x80A3 | Modbus exception 03 (illegal data value) | Quantity or value rejected |
| 0x80A4 | Modbus exception 04 (slave failure) | Server-side fault – inspect server log |
| 0x80A8 | Modbus exception 08 (memory parity) | Storage error on server |
| 0x80D1 | No response from server | Server offline, frame timeout too short |
| 0x80D2 | CRC / framing error (RTU only) | Baud / parity mismatch, EMI |
| 0x80EB | Timeout during transmission | Increase TIME_OUT on the instance |
| 0x80F7 | Pointer invalid | MB_DATA_PTR length < quantity |
Commissioning Procedure
- Compile the project and download Hardware and software to the CPU. Verify the SF/BF LEDs are off.
- Open the watch table and confirm
%M0.1= 1 (Always TRUE) and%MB100.4toggles at 1 Hz. - Set a breakpoint or force
MB_CLIENT_READ.REQto TRUE for one cycle; confirm the read populatesReadRegs[1..N]. - Force
WriteRegs[1]to16#00A5, triggerMB_CLIENT_WRITE.REQ, and verify the server reports coil / register0xA5. - Disable the manual REQ forces and enable the sequencer. Watch
OkCntincrement in the watch table. - Introduce a fault (disconnect the server, power-cycle it) and verify the sequencer enters FAULT, increments
FaultCnt, and recovers within 10 s of restoration.
Verification Checklist
| Item | Expected | How to verify |
|---|---|---|
| Clock memory | %MB100.4 toggles at 1 Hz | Watch table with trigger on change |
| System memory | %M0.1 = 1, %M0.0 = 1 on first scan only | Watch table, power-cycle test |
| DI → word | WriteRegs[1] tracks IB0 | Toggle input, observe array |
| Word → DO | QB0 tracks ReadRegs[1] low byte | Force register, measure output |
| Read latency | < 50 ms typical LAN | Trace REQ → DONE in trace |
| Fault recovery | Auto-resume within back-off | Pull server cable, re-insert |
Troubleshooting Matrix
| Symptom | Likely Cause | Diagnostic | Fix |
|---|---|---|---|
| No transactions, REQ wired to %M0.1 | Static TRUE = single shot | Watch STATUS = 0 after first cycle | Use P_TRIG on a clock bit or sequencer |
| Transactions every scan, high STATUS churn | REQ wired to fast clock without edge | STATUS = 0x80D1 / 0x8381 | Insert P_TRIG, drop to 1 Hz or slower |
| One MB_CLIENT works, the other errors 0x8380 | Both pointing at same ConnId without mutex | Watch both DONE bits | Sequencer or two distinct ConnIds |
| Clock memory bits all zero | Software-only download | Check online > system memory | Download hardware and software |
| ReadRegs freezes after first cycle | REQ latched on – no rising edge | Force REQ toggles data | Edge detector on sequencer trigger |
| WriteRegs[1] shows inverted bits | Endian confusion on display | Inspect on server side | Confirm symbol-side swap is acceptable |
| STATUS 0x80F7 on large quantity | MB_DATA_PTR shorter than quantity | Cross-check pointer LEN | Resize DB or reduce quantity |
| STATUS 0x80A2 sporadically | Register offset above server map | Server diagnostics | Reduce starting offset or quantity |
Performance and Network Discipline
Modbus TCP is a request/response protocol with no inherent pipelining. On a 100 Mbit/s LAN, a single FC03 read of 125 registers round-trips in roughly 10–30 ms including TCP overhead, so a 1 Hz poll gives a transaction-to-idle ratio of 1:30 to 1:70 – sustainable indefinitely on any server. Pushing to 10 Hz without DONE gating will overrun almost any embedded server within seconds because retries are dropped on the floor and the response queue backs up.
When the application requires faster updates than 1 Hz, gate the next REQ on the previous DONE rather than on a clock. This pattern is also called back-to-back:
"MB_CLIENT_READ".REQ := "MB_CLIENT_READ".DONE OR #FirstScan;
#FirstScan := FALSE;
Use a TON minimum-period timer if back-to-back ever floods the server. Typical floor is 100 ms for a small register window, 250 ms for a full 125-register scan.
Migration Notes
-
S7-1500: The instruction is still
MB_CLIENTbut lives in the Communication palette under Modbus TCP. Connection handling usesTCON_IP_V4_SECwhen TLS is required. -
CPU 12xx firmware < V4.2:
MB_CLIENTSTATUS codes are coarser; upgrade firmware to at least V4.4 for the diagnostics above. -
Modbus RTU via CM 1241: Replace the
TCON_IP_V4block with the RS-485 hardware parameters;REQbehavior is identical. - Third-party gateway: Some gateways strip MBAP length bytes; symptoms are STATUS 0x80D1 with successful TCP open. Set the gateway to Modbus TCP transparent mode.
FAQ
Why does MB_CLIENT only send one transaction when REQ is always TRUE?
REQ is rising-edge triggered. A static TRUE produces exactly one transaction on the first scan in which it is high; subsequent cycles see no transition and no request is generated. Use a P_TRIG on a clock memory bit or a sequencer that re-arms REQ after each DONE.
What clock frequency should I use to auto-trigger MB_CLIENT?
1 Hz (%MB100.4 by default) is the recommended starting point for most Modbus TCP servers. Faster clock bits (10 Hz, 5 Hz) flood the network unless you also insert an edge detector and a minimum-period timer of at least 100 ms.
How do I run two MB_CLIENT blocks against the same server?
Either open two TCP connections by giving each block its own TCON_IP_V4 instance with a distinct ConnId, or – more common – share one connection and sequence the blocks with a state machine that releases REQ only after the previous DONE or ERROR.
My clock memory byte does not toggle in the watch table, what is wrong?
The byte is configured in the device view but you downloaded software-only. Repeat the download with "Hardware and software" selected so the CPU stores the system and clock memory settings.
How can I pack digital inputs into one Modbus register?
Use a single MOVE from the input byte (IB0) to the low byte of the target WORD in the DB array. The upper byte stays zero, which most Modbus servers treat as eight unused inputs. Bit-by-bit assignment is only needed when the source bits are non-contiguous.
What STATUS code means the server rejected the register address?
0x80A2 corresponds to Modbus exception code 02 (illegal data address). Verify that the start offset plus quantity does not exceed the server's register map, and that the function code (03, 06, 16) is permitted at that range.
Can I share one REQ source between a read and a write MB_CLIENT?
No. Each block needs its own REQ so the sequencer can independently release it after the prior DONE. Sharing REQ causes both blocks to fire simultaneously and triggers STATUS 0x8380 (connection in use).