Configuring Modbus Register Access Types on S7-1200 MB_SLAVE

David Krause17 min read
ModbusSiemensTutorial / 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

Configuring Modbus Register Access Types on S7-1200 MB_SLAVE

Modbus is a request/reply protocol built on four data tables: Coils, Discrete Inputs, Input Registers, and Holding Registers. The four-table data model is the foundation of Modbus standard compliance, and it dictates the function codes a master can issue, the access rights a slave must enforce, and the addressing scheme integrators must document for the master's polling table. The Siemens S7-1200 implements the Modbus RTU slave role through the MB_SLAVE instruction in the SIMATIC S7-1200 communication library, and it allows you to bind those four tables to specific memory areas inside a STEP 7 (TIA Portal) data block. The challenge many integrators hit on a first deployment is mapping internal PLC variables to read-only vs. read/write tables so that an external master cannot overwrite values the application considers authoritative.

This reference walks through the Modbus data model, the function codes that drive access semantics, and the TIA Portal configuration of MB_SLAVE so that read-only and read/write regions are enforced on the slave side rather than relying on master politeness.

Modbus Data Model Fundamentals

The Modbus Application Protocol (current revision 1.1b3) defines a process image organized into four reference tables. The tables are logical, not physical: any Modbus device maps its internal data into one of these four buckets and the master reaches them by address range and function code.

Table Object Type Access Address Range (decimal) PDU Address Range Read FC Write FC
Coils Single bit Read / Write 00001 – 09998 0x0000 – 0x270E 01 05, 15 (0x0F)
Discrete Inputs Single bit Read-only 10001 – 19998 0x0000 – 0x270E 02
Input Registers 16-bit word Read-only 30001 – 39998 0x0000 – 0x270E 04
Holding Registers 16-bit word Read / Write 40001 – 49998 0x0000 – 0x270E 03 06, 16 (0x10)
Note on addressing. The decimal addresses (00001, 10001, 30001, 40001) are a Modbus convention; the actual Protocol Data Unit (PDU) carries a zero-based address. A "40001" reference number sent by the master is transmitted as PDU address 0x0000. Several masters add an offset of 1; verify the exact mapping your master expects against its own documentation. The Modbus specification PDU uses 0-based offsets; the convention "address 1 = PDU 0" is universally applied.

Function code selection is not arbitrary. The function code is what the slave inspects to decide whether a request is permitted against a given table. The eight primary function codes a fully implemented Modbus slave responds to are:

FC (decimal / hex) Name Operates On Operation
01 (0x01) Read Coils Coils Read 1–2000 contiguous bits
02 (0x02) Read Discrete Inputs Discrete Inputs Read 1–2000 contiguous bits
03 (0x03) Read Holding Registers Holding Registers Read 1–125 contiguous 16-bit words
04 (0x04) Read Input Registers Input Registers Read 1–125 contiguous 16-bit words
05 (0x05) Write Single Coil Coils Write one bit (0x0000=OFF, 0xFF00=ON)
06 (0x06) Write Single Register Holding Registers Write one 16-bit word
15 (0x0F) Write Multiple Coils Coils Write 1–1968 contiguous bits
16 (0x10) Write Multiple Registers Holding Registers Write 1–123 contiguous 16-bit words

There is no write function code defined for either Discrete Inputs (table 2) or Input Registers (table 3). A Modbus slave receiving a write request against an address that maps into a read-only table returns exception 02 – Illegal Data Address. Exception 02 is the slave's hard guarantee that read-only data is non-writable, regardless of master behavior.

Why the Four-Table Model Matters for S7-1200 Sizing

On the S7-1200 with the Modbus library instructions, each table is realized by a block of addresses inside a STEP 7 data block. The MB_SLAVE instruction exposes four MB_ADDR_xxx inputs that point to the start of each table. A typical parameter set is shown in the call below.

// MB_SLAVE call (typified)
"MB_SLAVE_DB"(
  MODE        := 1,                  // 1 = half-duplex (RS485) slave
  MB_ADDR     := 1,                  // Modbus station address 1..247
  BAUD        := 9600,
  PARITY      := 0,                  // 0=none, 1=even, 2=odd
  RESP_TO     := 1000,               // response timeout in ms
  MB_DB       := "ModbusData",       // user data DB
  HOLD_REG    := "ModbusData".HR,    // start of holding register area
  COIL_REG    := "ModbusData".CR,    // start of coil area
  INPUT_REG   := "ModbusData".IR,    // start of input register area
  INPUT_COIL  := "ModbusData".IC,    // start of discrete input area
  DONE        := M10.0,
  ERROR       := M10.1,
  STATUS      := MW12,
  NDR         := M10.2,
  DR          := M10.3);

Each pointer argument resolves to a ANY-style byte address whose first word is the start offset within MB_DB and whose length is implicit in the size of the variable. The order and location of these structures inside MB_DB is not constrained other than by non-overlap: a coil starting at byte 0 cannot share bytes with an input register starting at byte 0, because the four tables must be addressable independently.

Prerequisites

Before configuring access semantics, validate the following.

  1. Firmware. S7-1200 CPU firmware 4.0 or later for the Modbus RTU library; firmware 4.2 or later for the extended Modbus TCP variant (MB_CLIENT / MB_SERVER). Confirm in the TIA Portal "Online & Diagnostics" → "Module Information" view.
  2. CM/CB or onboard RS485. The Modbus RTU slave role requires a serial interface. The S7-1211/1212/1214 CPUs include an onboard RS485 port (port 0). For RS232, RS422, or a second channel, a CM 1241 communication module (e.g., 6ES7241-1AH32-0XB0) is required. The Modbus library version must match the module; consult the Siemens Industry Online Support portal for the latest library compatibility list.
  3. Library installation. Install the "Modbus (RTU/ASCII) Master and Slave" library package in TIA Portal. The library ships as a global library ("Modbus_Vxx") and must be opened once and copied into the project so the function blocks appear under "Communication" → "Modbus".
  4. Wiring and termination. For 2-wire RS485 networks, verify that the termination resistor is enabled only at the two physical ends of the bus. On the CPU's onboard port, this is the three-position switch on the bottom of the CPU (left = terminated, center = no termination with pull-up/pull-down, right = no termination, no bias).
  5. Station address planning. Assign a unique Modbus station address (1–247) to the slave. Address 0 is the broadcast address and is write-only; it is not used for normal polling.

Designing the Data Block Layout

The cleanest implementation uses a single, well-named data block with four ARRAY structures, one per Modbus table. This keeps the four reference tables separate, makes the slave's access semantics explicit, and provides a single location to attach documentation and the "Writable from HMI/OPC UA" attribute described later.

DATA_BLOCK "ModbusData"
{ S7_Optimized_Access := 'FALSE' }
  STRUCT
    // --- Coils (read/write) ---
    Coils        : ARRAY[0..127] OF BOOL;   // 16 bytes, FC01/05/15

    // --- Discrete Inputs (read-only) ---
    DiscInputs   : ARRAY[0..127] OF BOOL;   // 16 bytes, FC02 only

    // --- Input Registers (read-only, 16-bit) ---
    InRegs       : ARRAY[0..63]  OF WORD;   // 128 bytes, FC04 only

    // --- Holding Registers (read/write, 16-bit) ---
    HoldRegs     : ARRAY[0..127] OF WORD;   // 256 bytes, FC03/06/16

    // --- Pad to word boundary to make offsets predictable ---
    _pad         : ARRAY[0..1]   OF BYTE;
  END_STRUCT;
END_DATA_BLOCK
Optimization note. The instance MB_DB of MB_SLAVE and the user data block must be non-optimized (S7_Optimized_Access = FALSE). The Modbus library uses absolute byte offsets in its ANY pointers and cannot resolve symbolic addresses inside an optimized block. This is one of the most common commissioning errors.

With the layout above, the byte offsets passed to MB_SLAVE resolve as follows. Byte offsets are relative to the start of the data block; they are the values that must be assigned to the MB_ADDR_xxx inputs if those are used, or be the start of the array passed via ANY.

Modbus Table Symbol Byte Offset (P#) Size Master Address Range Master FCs
Coils DB1.DBX0.0 P#0.0 128 bits (16 bytes) 00001 – 00128 01, 05, 15
Discrete Inputs DB1.DBX16.0 P#16.0 128 bits (16 bytes) 10001 – 10128 02
Input Registers DB1.DBW32 P#32.0 64 words (128 bytes) 30001 – 30064 04
Holding Registers DB1.DBW160 P#160.0 128 words (256 bytes) 40001 – 40128 03, 06, 16

The master sees four completely independent address spaces, even though all four physically live in a single DB. The MB_SLAVE instance dispatches the incoming PDU to the correct region based on function code and address range, and the PLC program reads from / writes to the appropriate substructure.

Enforcing Read-Only Behavior on the Slave

Modbus is silent on whether a slave should accept writes to a particular register; the four-table model only guarantees that Discrete Inputs and Input Registers are read-only at the protocol level. To force a subset of Holding Registers or Coils to be read-only, the application program must override any writes that arrive. Two complementary techniques are used in real deployments.

Technique 1: Application-Level Override (Cyclic Recopy)

Mirror every "read-only" holding register to a write-disabled coil or register and have the application code (in OB1 or a cyclic OB) overwrite the value in the holding register area every scan. The master sees a brief window where its value lands, but the next scan the PLC repaints it. This is the technique described in the source material as "if you cyclically overwrite holding register with your values, the master can write something to it but it will be automatically overwritten by your system." It is reliable, has no library dependencies, and works on any firmware.

Technique 2: TIA Portal DB Attribute "Writable from HMI/OPC UA"

In TIA Portal, individual tags in a data block expose a security-related attribute called Accessible from HMI/OPC UA and Writable from HMI/OPC UA. These attributes govern the visibility and write authorization through the OPC UA server of the S7-1200 (firmware 4.2 or later with an active OPC UA license). They do not gate Modbus RTU traffic; an external master using MB_SLAVE on the RS485 port has no awareness of OPC UA attributes. They are, however, useful if the same data block is also exposed via OPC UA: a tag with Writable from HMI/OPC UA = unchecked becomes read-only for OPC UA clients while remaining writable for the Modbus master.

To set the attribute:

  1. Open the data block "ModbusData".
  2. Click a tag such as HoldRegs[0].
  3. In the inspector pane under "Properties" → "Attributes", uncheck Accessible from HMI/OPC UA if the tag should not be exposed to OPC UA at all, or uncheck Writable from HMI/OPC UA if it should be visible but read-only.
  4. Repeat for each tag that requires restricted access.

This approach does not, by itself, prevent a Modbus master from writing the same tag. Combine it with the application-level override in OB1 if Modbus-side write protection is the requirement.

Configuring Each Modbus Table on MB_SLAVE

The next subsections describe the four tables, the wiring inside the data block, and the OB logic that maintains the read-only contract.

2.1 Coils (Read/Write, FC 01/05/15)

Use Coils for boolean setpoints and commands the master should be allowed to switch — remote enable signals, set/reset bits, mode selectors, alarm acknowledgements. Because Modbus coils are 1-bit and the S7-1200 ARRAY OF BOOL packs 8 coils per byte, the master's view of Coils[0] corresponds to bit 0 of the byte at the array's base address.

If a particular coil must be read-only from the master, the application logic in OB1 should ignore the corresponding bit and overwrite it with the controlled value on every scan. The PLC still exposes the slot to the master at the same address; the master can technically issue FC 05 or 15 against it, but the value is overwritten before any application code sees it.

2.2 Discrete Inputs (Read-Only, FC 02)

Discrete Inputs are the read-only bit table. Use them for status bits, feedback signals, and interlocks that the master should see but not change. The S7-1200 implementation accepts FC 02 against the configured input coil area; any FC 05/15 against the same address range returns exception 02.

Typical application logic refreshes the discrete input area from physical inputs in OB1 or a process image update OB:

// OB1 fragment — refresh read-only bit table from process image
"ModbusData".DiscInputs[0]  := "I0.0";   // Field input 0 → DI 10001
"ModbusData".DiscInputs[1]  := "I0.1";   // Field input 1 → DI 10002
"ModbusData".DiscInputs[2]  := "DB_Motor".Running;   // Internal status → DI 10003

2.3 Input Registers (Read-Only, FC 04)

Input Registers are 16-bit words and are the read-only analog table. Use them for measured values, scaled sensor data, and internal diagnostic counters. The application populates the input register area on every scan; the master can only read.

Because the Modbus data model is fixed at 16-bit words, sending a 32-bit floating point across Modbus requires engineering the byte order. The two prevailing conventions are:

  • Word-swapped (Modicon convention). The lower-addressed word holds the upper half of the float. This is the default of most Modbus masters (Modbus Poll, ModScan, Schneider M340 CPU). Each float consumes two consecutive input registers (e.g., IR[0]–IR[1] for the first float).
  • Byte-swapped (little-endian register order). The lower-addressed word holds the lower half of the float. Common on some Allen-Bradley masters and on field devices that have a "Modbus Float Order" setting.

On the S7-1200, the canonical code to write a REAL into the input register area looks like this:

// Load a REAL into IR[0..1] using word-swap (Modicon) order
VAR_TEMP
  dw : DWORD;
  wHi, wLo : WORD;
END_VAR

dw := REAL_TO_DWORD("Process".Temperature_C);   // 32-bit IEEE-754
wLo := DWORD_LOW_WORD(dw);                       // low half
wHi := DWORD_HIGH_WORD(dw);                      // high half

"ModbusData".InRegs[0] := wLo;                   // low word first (Modicon)
"ModbusData".InRegs[1] := wHi;                   // high word second
Floating-point precision. Modbus is 16-bit, so floating-point values always require two consecutive registers. The Modbus standard does not specify byte order, which is a common source of cross-vendor interoperability issues. Document the byte order in the device's Modbus map and confirm with the master integrator's Modbus map specification.

2.4 Holding Registers (Read/Write, FC 03/06/16)

Holding Registers are the read/write 16-bit word table. They are the most common destination for setpoints, recipe data, and operator-entered values. The master reads and writes with FC 03/06/16.

For values that must be read-only from the master but consumed by the application, the canonical pattern is:

  1. The application copies its authoritative value into HoldRegs[N] on every scan of OB1.
  2. Any write from the master (FC 06 or 16) lands in HoldRegs[N] at the end of the master's transaction.
  3. On the next scan, the application overwrites HoldRegs[N] with the authoritative value, masking the master's write.

This pattern is robust but introduces a one-scan window of vulnerability. For tighter guarantees, gate the master write at the application level with a comparison: if the master wrote, set a sticky fault bit and refuse to apply the setpoint until operator acknowledgement.

Verification and Commissioning

After downloading the project, perform the following checks with a Modbus master on a bench network before the unit is shipped to the field.

  1. Loopback read of Holding Registers. From the master, issue FC 03 against register 40001 (PDU 0x0000). Confirm that the value matches the initial value in HoldRegs[0].
  2. Read-only verification of Input Registers. Issue FC 04 against 30001. Confirm that the value reflects the application-set input. Then issue FC 06 against the same address. The slave must respond with exception 02 (Illegal Data Address), and the value must remain unchanged.
  3. Read-only verification of Discrete Inputs. Issue FC 02 against 10001. Issue FC 05 against the same address. Confirm exception 02.
  4. Write test on Holding Registers. Issue FC 06 against 40001 with a known value. Read back with FC 03 and confirm.
  5. Coil write test. Issue FC 05 against 00001 with value 0xFF00. Read back with FC 01. Confirm the bit transitioned.
  6. Application-level override test. For any holding register designated as "read-only from master" via application logic: from the master, write a test value. Confirm that on the next scan the value is reverted to the application-controlled value.
  7. Status word inspection. With the program running, monitor the STATUS output of MB_SLAVE for non-zero values. The library defines the status codes in the instruction's help text; 0x0000 is "no error" and nonzero values indicate a malformed PDU, parity error, or address out of range.

Troubleshooting Matrix

Symptom Likely Cause Resolution
Master gets no response on any function code Baud rate / parity mismatch, A-B wiring reversed, termination missing on long cable, station address mismatch Verify BAUD, PARITY, and MB_ADDR on both sides; check that the RS485 A/B (or D+/D- on some vendors) are not crossed; confirm 120 Ω termination is enabled only at the two ends
FC 03 returns 0xFFFF for every register Modbus station address set to 0 (broadcast) or address does not match the master poll Confirm MB_ADDR is in 1–247; check master poll station address
Exception 02 (Illegal Data Address) on FC 03 against 40050 but works against 40001 Address is outside the configured holding register area Increase the size of the HoldRegs array or update the master to poll only within the supported range
STATUS = 0x8180 / 0x8181 after a write Modbus library diagnostic code: parity or framing error on the wire Inspect the cable for noise; reduce baud rate; verify shield grounding
Master can read but write seems to "stick" for one scan then revert Application-level override active on that register — expected behavior Confirm with the application engineer whether the override is intentional; if not, remove the OB1 overwrite for that tag
OPC UA client can write a tag that the Modbus master cannot The "Writable from HMI/OPC UA" attribute is checked; Modbus access is governed separately Uncheck "Writable from HMI/OPC UA" on the tag if OPC UA read-only is also required; remember that this does not affect Modbus
MB_SLAVE reports 0x80A1 / 0x80A2 (DB type) Configured data DB is optimized (S7_Optimized_Access = TRUE) Set the user data DB and the MB_SLAVE instance DB to non-optimized access

Reference — Modbus Address Map to Publish to the Master Integrator

Hand the following table to the master integrator. It defines the contract between the S7-1200 slave and the polling master; any change to the data block layout must be reflected here.

Modbus Reference PDU Address Symbolic Tag (TIA) Data Type Access Function Codes Engineering Unit / Meaning
00001 0x0000 ModbusData.Coils[0] BOOL R/W 01, 05, 15 Remote Enable
00002 0x0001 ModbusData.Coils[1] BOOL R/W 01, 05, 15 Reset Command
10001 0x0000 ModbusData.DiscInputs[0] BOOL R 02 Pump 1 Running
10002 0x0001 ModbusData.DiscInputs[1] BOOL R 02 High Pressure
30001–30002 0x0000–0x0001 ModbusData.InRegs[0..1] FLOAT (Modicon order) R 04 Process Temperature, °C
30003 0x0002 ModbusData.InRegs[2] INT (scaled x10) R 04 Process Pressure, 0.1 bar/bit
40001 0x0000 ModbusData.HoldRegs[0] INT R/W 03, 06, 16 Setpoint: Flow Rate, 0.1 L/min/bit
40002 0x0001 ModbusData.HoldRegs[1] INT R/W 03, 06, 16 Setpoint: Temperature, °C
40050–40051 0x0032–0x0033 ModbusData.HoldRegs[49..50] FLOAT (Modicon order) R/W 03, 06, 16 Recipe: Line Speed, m/s

What is the proper way to designate a Modbus register as read-only on the S7-1200?

There is no per-tag read-only flag in the Modbus protocol itself. The standard mechanism is to map the value into the Input Register table (FC 04) for 16-bit read-only data or the Discrete Input table (FC 02) for 1-bit read-only data. Any write attempt (FC 06/16 or FC 05/15) against those addresses returns Modbus exception 02 (Illegal Data Address). For read-only values that must live in the Holding Register table for compatibility with the master, implement an OB1 application-level override that recycles the value every scan.

Does the "Writable from HMI/OPC UA" attribute block Modbus writes?

No. The attribute gates the OPC UA server of the S7-1200 only. A Modbus RTU master connected to the same CPU via RS485 has no awareness of OPC UA attributes. Use the cyclic OB override pattern in addition to (or instead of) the attribute if Modbus write protection is the requirement.

Why does my S7-1200 return exception 02 for an address I configured in the data block?

Exception 02 (Illegal Data Address) is returned when the requested address falls outside the configured MB_ADDR range. The data block may contain the variable, but if the MB_SLAVE pointer is not configured to include that byte offset, the slave treats the address as out of range. Verify the start offset and length of each table in the MB_SLAVE call and ensure the symbolic addresses resolve inside the configured region.

How many Holding Registers can the S7-1200 Modbus RTU slave expose?

The limit is bounded by the data block size and the S7-1200 work memory, not by a hard library limit. Practical deployments use 200–500 words in the Holding Register area without issue. Holding Register polling with FC 03 supports 1–125 words per request, so partition the map into chunks of 125 or fewer to minimize transaction overhead.

What is the correct byte order for sending a REAL via Modbus?

The Modbus standard does not specify byte order. Two conventions exist: Modicon word-swapped (low word first, high word second) and little-endian (high word first, low word second). Confirm with the master integrator which order is expected. On the S7-1200, use DWORD_HIGH_WORD and DWORD_LOW_WORD from the IEC standard functions to extract the two words from a REAL cast to DWORD, then place them in the appropriate ARRAY OF WORD positions.

Back to blog