S7-1200 MB_CLIENT: I/O to DB Array and Auto-Trigger Configuration

David Krause12 min read
S7-1200SiemensTutorial / How-to
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

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.

Scope: The article covers Modbus TCP via the PROFINET interface of a CPU 12xx with firmware V4.x or higher. Modbus RTU over a CM 1241 RS-485 module follows the same instruction but the connection parameters differ (see the S7-1200 System Manual, chapter on CM 1241).

Prerequisites

  1. CPU 12xx with at least firmware V4.2 (V13 SP1 in TIA Portal). Firmware V4.4 or higher is recommended for the improved MB_CLIENT error reporting.
  2. 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).
  3. 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.
  4. A configured Modbus server IP, port (default 502), unit ID, and register map (function codes 03/06/16).
  5. An empty data block with at least 1024 WORD elements (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 %MB0 through %MB7 with predefined bits such as %M0.0 First cycle, %M0.1 Always TRUE (always 1), %M0.2 Always FALSE (always 0), and diagnostic flags (e.g. %M0.3 diagnostic status changed).
  • Clock memory byte – assigns the byte to any unused MB address (default MB100) 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.

Field-caveat: If you can read the system byte with the watch table but the clock memory bits do not toggle, you almost certainly downloaded software-only. Repeat the download with Hardware and software selected.

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;
Bit order: Modbus is big-endian and counts bits starting at 1 for the LSB. Siemens 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 REQ to %M0.1 with 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;
Watch for retries: If the log shows retry, retry, retry, delay, done, done, done, retry the physical link is unstable. Inspect cabling, termination, switch port counters, and EMC before tuning the sequencer further.

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

  1. Compile the project and download Hardware and software to the CPU. Verify the SF/BF LEDs are off.
  2. Open the watch table and confirm %M0.1 = 1 (Always TRUE) and %MB100.4 toggles at 1 Hz.
  3. Set a breakpoint or force MB_CLIENT_READ.REQ to TRUE for one cycle; confirm the read populates ReadRegs[1..N].
  4. Force WriteRegs[1] to 16#00A5, trigger MB_CLIENT_WRITE.REQ, and verify the server reports coil / register 0xA5.
  5. Disable the manual REQ forces and enable the sequencer. Watch OkCnt increment in the watch table.
  6. 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_CLIENT but lives in the Communication palette under Modbus TCP. Connection handling uses TCON_IP_V4_SEC when TLS is required.
  • CPU 12xx firmware < V4.2: MB_CLIENT STATUS codes are coarser; upgrade firmware to at least V4.4 for the diagnostics above.
  • Modbus RTU via CM 1241: Replace the TCON_IP_V4 block with the RS-485 hardware parameters; REQ behavior 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).

Back to blog