Configuring Modbus RTU on S7-1200: MB_SLAVE and MB_HOLD_REG Setup

David Krause13 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

Modbus RTU is one of the most common industrial serial protocols, and the Siemens S7-1200 family supports it natively through the MB_COMM_LOAD and MB_SLAVE function blocks when paired with a CM 1241 RS-485 or CM 1241 RS-232 communication module. This article walks through a field-proven configuration sequence and solves the three issues engineers most frequently hit when first commissioning the link: configuring the slave (Modbus) address, getting MB_HOLD_REG accepted by the TIA Portal, and recovering from a silent bus where NDR and DR stay at 0 with no error code.

Modbus itself is a master/slave request/reply protocol defined by Modbus Organization and is widely used for simple register-based polling between PLCs, VFDs, energy meters, and SCADA tools such as CAS Modbus Scanner, Modbus Poll, and Modbus Tools. The serial RTU variant uses RS-485 (or RS-232) with 8 data bits, even parity, and 1 stop bit as the default framing. Background reading is available at Wikipedia – Modbus and the Schneider Electric primer at What is Modbus and How does it work?.

Important: Modbus/RTU (serial) and Modbus/TCP (Ethernet, port 502) are completely different protocols. The S7-1200 uses different instruction sets for each. This article covers Modbus/RTU over RS-485 only.

Prerequisites

Before configuring the blocks, confirm the following hardware and software are in place.

Item Requirement
CPU S7-1200 (any firmware; MB_SLAVE instructions require firmware ≥ V2.0 on the CM, with current TIA Portal libraries supporting V4.x and later)
Communication module 6ES7241-1CH30-1XB0 (CM 1241 RS-485) or 6ES7241-1AH30-1XB0 (CM 1241 RS-232)
Engineering software SIMATIC STEP 7 (TIA Portal) V13 SP1 or later; project example provided here works on V13 SP1 as well as later Service Packs
Firmware CM 1241 firmware ≥ V1.0 (modules ship with current firmware from factory)
Master test tool CAS Modbus Scanner, Modbus Poll, or any Modbus/RTU master capable of 19200 / 8E1 (CAS Modbus Test was used in the field report)
Wiring 2-wire RS-485 bus, 120 Ω termination at both ends, common ground between devices

Hardware Wiring and Module Insertion

Insert the CM 1241 RS-485 to the left of the CPU. The module is detected automatically by TIA Portal and appears in Device view. Configure the module's serial port parameters from the Properties > RS-485 interface tab to match the master you intend to connect to:

Parameter Typical Value Notes
Baud rate 19200 bit/s Common default; 9600 is also widely used
Parity Even Modbus RTU standard is 8E1 (even parity, 1 stop bit)
Data bits 8 Fixed by Modbus RTU
Stop bits 1 Fixed by Modbus RTU
Flow control None RS-485 does not use hardware flow control
Termination Enabled on end nodes only DIP switch on back of CM 1241, or external 120 Ω resistor

Slave Address: Where to Set It

The Modbus slave address is not derived from the CPU's IP address. Setting a unit to "20" because its Ethernet IP ends in ...20 is a common mistake. The slave address is a property of the Modbus link and is configured on the MB_SLAVE block, parameter SLAVE_ADDR.

Valid range is 1 to 247. Address 0 is the Modbus broadcast address and should not be used for normal polling. The address you set must match what the master (e.g. CAS Modbus Test) is configured to poll.

Caution: A TCP/IP address (e.g. 192.168.0.20) is meaningful only for Modbus/TCP. For Modbus/RTU on RS-485, every device on the bus must have a unique number from 1 to 247 regardless of any IP address the device might also have.

MB_COMM_LOAD — Port Parameterization

The MB_COMM_LOAD block parameterizes the CM 1241's serial port. It must be executed once after the PLC starts, typically on the first scan or behind a positive edge from a system bit. Calling it every scan wastes CPU time and is not required.

Input Type Meaning
REQ BOOL Trigger pulse; rising edge starts parameterization
PORT PORT (HW identifier) HW ID of the CM 1241 from device configuration
BAUD DINT Baud rate, e.g. 9600 or 19200
PARITY DINT 0 = None, 1 = Odd, 2 = Even
FLOW_CTRL DINT 0 = None (use this for RS-485)
RTS_ON_DLY DINT RTS on delay in ms; 0 typical for RS-485
RTS_OFF_DLY DINT RTS off delay in ms; 0 typical for RS-485
RESP_TO DINT Response timeout in ms (e.g. 1000)

Drive REQ from a one-shot that triggers only on the first scan cycle:

// OB1 - First-scan trigger
// In a global DB or as a tag:
#FirstScanDone : BOOL;

// Ladder snippet:
// [FirstScan] --[NOT #FirstScanDone]--( #MBCommLoad.REQ )
// [MBCommLoad.DONE]--( #FirstScanDone )

If MB_COMM_LOAD reports STATUS = 16#8180 the port is in use or the HW ID is wrong; 16#8181 indicates a parity/baud combination not supported.

MB_SLAVE — Modbus Server Configuration

Place an MB_SLAVE instance in OB1 and call it on every scan once MB_COMM_LOAD.DONE is true. The block processes Modbus requests and exposes the S7-1200's holding registers to a Modbus master.

Input Type Meaning
MB_ADDR UINT Modbus station address (1–247). This is your slave ID.
MB_HOLD_REG VARIANT (POINTER) Pointer to the start of the holding register area; see next section
NDR BOOL New data received from master (one-scan pulse)
DR BOOL Data read by master (one-scan pulse)
ERROR BOOL Error flag (one-scan pulse — capture it!)
STATUS WORD Detailed error/status code
Critical: The ERROR and STATUS outputs are valid for only one scan cycle. If you do not latch them into a static tag (e.g. with an M bit or DB word), you will see no error information when you go online.

A standard MB_SLAVE supports the following Modbus function codes; non-supported requests return exception code 01 (illegal function).

FC Function Address range
01 Read Coils 0xxxx (process image output, Q area)
02 Read Discrete Inputs 1xxxx (process image input, I area)
03 Read Holding Registers 4xxxx (MB_HOLD_REG area)
04 Read Input Registers 3xxxx (input word area / IW)
05 Write Single Coil 0xxxx
06 Write Single Register 4xxxx
15 Write Multiple Coils 0xxxx
16 Write Multiple Registers 4xxxx
23 Read/Write Multiple Registers 4xxxx

Configuring MB_HOLD_REG — The Most Common Pitfall

The MB_HOLD_REG input is a VARIANT pointer to the start of a contiguous block of words that the master will read/write with FC 03, FC 06, FC 16, and FC 23. Two pointer formats are accepted:

Format A — Optimized Data Block (Recommended)

Create a global DB whose data block number is non-optimized (in DB properties, uncheck "Optimized block access"). Within it, declare either a structured type or a simple word array.

// Data block HR_DB, non-optimized
DATA_BLOCK "HR_DB"
{ S7_Optimized_Access := 'FALSE' }
VERSION : 0.1
NON_RETAIN
  STRUCT
    Data : STRUCT
      Temp_1    : REAL;    // 4 words  -> Modbus 40001..40004
      Temp_2    : REAL;    // 4 words  -> Modbus 40005..40008
      Avg_Time  : UDINT;   // 4 words  -> Modbus 40009..40012
      Good_Count: UDINT;   // 4 words  -> Modbus 40013..40016
      Bad_Count : UDINT;   // 4 words  -> Modbus 40017..40020
    END_STRUCT;
  END_STRUCT;
END_DATA_BLOCK

In the MB_HOLD_REG input on the MB_SLAVE block, type the symbolic name of the variable. Because the DB is non-optimized, TIA Portal will accept a simple symbol such as "HR_DB".Data or even "HR_DB" at the DB boundary.

Watch the trailing period: When the symbol is typed, the TIA Portal input helper often appends an extra period after the symbol you select. If you see "HR_DB".Data. with a trailing dot, manually delete the extra period. Leaving it in place produces "Incorrect pointer on MB_HOLD_REG" (status 16#8382).

Format B — Absolute M-Area Pointer

If you do not want to use a DB, you can point the holding register area directly into the M (merker) area using an Any-pointer literal in the form P#Mn.0 WORD x.

// 100 words of holding registers starting at M50.0
P#M50.0 Word 100

This reserves Modbus addresses 40001 to 40100 mapped onto MW50 through MW250 (2 bytes per word). Watch the size — a 100-word block consumes 200 bytes of M memory.

Common Status Codes for MB_HOLD_REG Errors

STATUS (hex) Meaning Fix
16#8183 Pointer to non-existent DB / wrong symbolic name Verify the DB number exists and the name is correct
16#8382 Pointer length/format invalid ("Incorrect pointer on MB_HOLD_REG") Remove trailing period in the symbol; ensure DB is non-optimized; re-check Any-pointer syntax
16#8383 Data type mismatch (e.g. trying to map BOOLs into a word region) Use INT, WORD, DWORD, REAL, or UDINT elements only
16#8384 MB_HOLD_REG overlaps with system memory or is outside the data area Re-locate the DB to user range; check the address does not exceed 60 KB

Step-by-Step Commissioning Procedure

  1. Insert CM 1241 RS-485 in the device configuration. Note its Hardware identifier (HW ID) — typically 269 or similar.
  2. Create global DB HR_DB with optimized access disabled. Add a structure of REAL and UDINT variables as shown above.
  3. Open OB1 and add an MB_COMM_LOAD instance. Connect a one-shot pulse to REQ, the CM 1241 HW ID to PORT, and constants for baud/parity/flow control.
  4. Add an MB_SLAVE instance in OB1. Set MB_ADDR to the desired station address (1–247).
  5. Wire MB_HOLD_REG to the Data structure of HR_DB. Confirm there is no trailing period in the symbol.
  6. Capture ERROR and STATUS into static tags. Without this, you cannot see what went wrong after the block returns.
  7. Download the project, go online, and force MB_COMM_LOAD.REQ for one scan if it has not already executed.
  8. Connect the master tool (CAS Modbus Scanner) to the RS-485 port, set the same baud/parity, and poll FC 03 at register 0. The response should show the current values of HR_DB.Data.Temp_1, Temp_2, etc.

Verifying the Link

Watch MB_SLAVE.NDR (new data — write request received) and MB_SLAVE.DR (data read) on each scan. The master should generate a steady stream of pulses on these bits at the master's poll rate. If both remain 0 and no error is reported, the bus is silent — see the troubleshooting matrix below.

Symptom Likely Cause Action
ERROR pulses once with STATUS = 16#8382 Bad MB_HOLD_REG pointer Remove trailing period, verify DB is non-optimized
ERROR with STATUS = 16#8180 MB_COMM_LOAD did not run, or HW ID wrong Check PORT input, force one-shot
No error, NDR = DR = 0, master times out Wiring swap (A/B inverted), missing termination, or mismatched baud Swap A+/B− lines; check 120 Ω at both ends; verify baud/parity match master
Master receives exception code 02 (illegal data address) Polling address outside the MB_HOLD_REG range Ensure request stays inside the 100-word region; remap the DB
Master receives exception code 03 (illegal data value) Quantity too high for the FC Limit per-request word count to the FC specification (e.g. 125 words for FC 03)
Communication works briefly then drops RS-485 echo / biasing missing on long cables Add fail-safe bias resistors (typically 680 Ω to +5 V on A, 680 Ω to GND on B)

Capturing the One-Scan Error

Because the ERROR bit is high for a single scan, capture it into a retentive tag for offline inspection. A minimal pattern looks like this:

// M-memory capture registers
// %MW100  : MB_SLAVE.ERROR  (latched)
// %MW102  : MB_SLAVE.STATUS (latched)

// Ladder:
// [MB_SLAVE.ERROR]   --( #CaptureErrorSet )     // SET bit
// [MB_SLAVE.ERROR]   --( MOVE MB_SLAVE.STATUS > %MW102 )
// [MB_SLAVE.NDR]     --( MOVE MB_SLAVE.STATUS > %MW104 )

With this in place, if the master polls an unsupported function, you will see the latched status at %MW102 on the next online session and can decode it against the table above or the Siemens SIMATIC Technical Support knowledge base.

Differentiating Modbus/RTU from Modbus/TCP

A frequent misconfiguration is to leave the master's TCP/IP parameters in place while wiring a serial port. If the tool expects 192.168.0.20:502, you will not see any response on RS-485. Confirm the master is in RTU mode and pointed at the correct COM port.

Property Modbus/RTU Modbus/TCP
Physical layer RS-232 or RS-485 Ethernet
Frame format Binary with CRC-16 MBAP header + PDU
S7-1200 instruction set MB_COMM_LOAD + MB_SLAVE / MB_MASTER TCON, TSEND, TRCV with FB "MB_CLIENT" / "MB_SERVER" from the Modbus/TCP library
Address concept Slave ID 1–247 on a shared bus IP address + port 502 per device
Typical errors if swapped Master times out Master returns connection refused

Best Practices and Field Notes

  • Always use a non-optimized DB for the holding register area. Optimized DBs are not accepted by the legacy MB_SLAVE block in classic STEP 7 libraries.
  • Use a single positive-edge trigger to call MB_COMM_LOAD. Continuous calls do not harm the CPU but waste cycle time.
  • Latch ERROR and STATUS outputs into retentive memory. Without this, you will not be able to diagnose intermittent issues after the fact.
  • Place a 120 Ω termination at each physical end of the RS-485 bus, never in the middle. The CM 1241 has an internal switch you can use if it is the last device.
  • Match A+ to A+ and B− to B−. On Siemens terminals these are labeled T/R+ and T/R−. If the master times out with no errors, swap them.
  • For long cables (> 10 m) or noisy environments, add failsafe bias (typically 680 Ω pull-up to +5 V on T/R+ and 680 Ω pull-down to GND on T/R−).
  • For more than 31 devices or cables longer than 1200 m, install an RS-485 repeater (e.g. Phoenix Contact PSI-REP-RS485W2 or similar).
  • Verify Modbus standard framing: 8 data bits, even parity, 1 stop bit (8E1) unless the device explicitly requires something else.

Project Archive Compatibility

The example project discussed in the source material was originally provided in TIA Portal V13 SP2 format. To open it on V13 SP1, either upgrade the installation to SP2 or later, or export the program blocks manually and re-import them into a fresh SP1 project. The MB_SLAVE and MB_COMM_LOAD blocks themselves are forward-compatible — the differences are at the project container level, not in the block logic.

Quick-Reference: Required Block Wiring

// OB1 main scan — call both blocks
// MB_COMM_LOAD (called once on first scan)
//   REQ        := #FirstScanEdge
//   PORT       := 269                     // HW ID of CM 1241
//   BAUD       := 19200
//   PARITY     := 2                       // 2 = Even
//   FLOW_CTRL  := 0
//   RTS_ON_DLY := 0
//   RTS_OFF_DLY:= 0
//   RESP_TO    := 1000
//   DONE       -> %M10.0
//   ERROR      -> %M10.1
//   STATUS     -> %MW12

// MB_SLAVE (called every scan after MB_COMM_LOAD.DONE = 1)
//   MB_ADDR    := 1                       // Modbus slave ID
//   MB_HOLD_REG:= "HR_DB".Data            // No trailing period!
//   NDR        -> %M20.0                 // Latch to inspect
//   DR         -> %M20.1
//   ERROR      -> %M20.2                 // Latch with S/R flip-flop
//   STATUS     -> %MW22

FAQ

Why does the S7-1200 MB_SLAVE report "Incorrect pointer on MB_HOLD_REG" even though the DB exists?

The most common cause is a trailing period in the symbolic name (for example "HR_DB".Data. instead of "HR_DB".Data). The TIA Portal input helper often appends one when you select the symbol. Delete the extra period. The second most common cause is an optimized-access DB — MB_SLAVE requires the DB to have optimized access disabled.

Can I use the CPU's IP address (e.g. 192.168.0.20) as the Modbus slave address?

No. Modbus/RTU uses a station ID between 1 and 247 on a shared RS-485 bus, and the address is set on the MB_ADDR input of the MB_SLAVE block. The CPU's IP address is meaningful only for Modbus/TCP, which uses different instructions (MB_CLIENT/MB_SERVER over TCON).

How do I make MB_SLAVE work with a non-DB memory area such as the M (merker) area?

Enter an Any-pointer literal in the form P#Mn.0 WORD x on the MB_HOLD_REG input, where n is the starting byte and x is the word count. For example, P#M50.0 WORD 100 reserves 100 words (200 bytes) starting at MB50 for Modbus FC 03/06/16/23 access.

Why are NDR and DR both 0 with no error reported?

The MB_SLAVE block has nothing to do unless a Modbus master actually transmits on the bus. Verify (1) the master is in RTU mode on the correct COM port, (2) baud and parity match (8E1 is standard), (3) A+/B− wiring is not swapped, and (4) the bus is terminated with 120 Ω at both ends. Also confirm MB_COMM_LOAD has run successfully (DONE = 1) before MB_SLAVE starts processing.

How do I see the MB_SLAVE error code if it only pulses for one scan?

Latch the ERROR bit and the STATUS word into retentive M-memory or DB tags every scan. Without this latching logic, the error is lost the moment the next scan starts and online diagnostics show nothing. The same technique applies to NDR and DR if you need a record of which Modbus request was last serviced.

Back to blog