S7-1200 Modbus Troubleshooting: Bit, Coil, and Register Mapping

David Krause12 min read
ModbusSiemensTroubleshooting
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 remains the most widely deployed industrial fieldbus protocol, but its data-model semantics trip up integrators moving to the Siemens S7-1200 platform for the first time. The recurring field failures are not protocol-stack bugs - they are addressing-model mismatches between Modbus and the Siemens MB_CLIENT/MB_SERVER instruction set, combined with separator confusion between bit, byte, and word access. This reference consolidates the address-map rules, function-code mapping, TIA Portal configuration path, and verification steps needed to bring up reliable Modbus RTU and Modbus TCP on an S7-1212C/S7-1214C/S7-1215C CPU, including the offsets encountered when a slave splits discrete inputs (0-511) and discrete outputs (512-1023) into separate numeric ranges.

Modbus Data Model and Function Codes

Modbus is a request/response protocol defined by the Modbus Organization with four primary data tables, each accessed by a public function code. The address space is 0-based on the wire; many Siemens and third-party slaves display it 1-based (offset +1) in tooling.

Table Access Function Code (Read) Function Code (Write) Typical Slave Range Wire Size
Coil (DO) Read/Write 01 05 (single), 15 (multi) 00001-09999 1 bit
Discrete Input (DI) Read-only 02 - 10001-19999
Holding Register (HR) Read/Write 03 06 (single), 16 (multi), 23 (R/W) 40001-49999 16-bit word
Input Register (IR) Read-only 04 - 30001-39999 16-bit word
Bit vs byte vs word: Function codes 01/02/05/15 manipulate single bits packed eight-per-byte. Function codes 03/04/06/16/23 manipulate 16-bit words. A frequent field failure is calling FC 05 on an HR address (the slave will return illegal-function 0x01) or calling FC 06 on a coil address (slave returns illegal-data-address 0x02). Match FC to table before matching offset.

Why Siemens S7-1200 Modbus Fails on First Commissioning

Three categories account for the majority of S7-1200 Modbus startup failures observed in the field:

  1. Modbus-offset confusion. Slave documentation lists "40001" for the first holding register; the on-wire address is 0. Some slave firmware treats "register 1" as offset 0, others as offset 1. S7-1200 MB_CLIENT requires the 0-based value in MB_DATA_ADDR.
  2. Bit/word mismatch. Engineers request a single coil with FC 05 but configure the S7 buffer as WORD; or they call FC 06 expecting to write a coil and instead write the first bit of a holding register.
  3. Discrete-input/discrete-output range split. Compact slaves (low-cost sensor blocks, certain I/O link gateways) map DI to 0-511 and DO to 512-1023. Writing to address 100 expecting a coil turns on a discrete input by accident. Always derive the FC from the slave's published register map, not from the variable name on the HMI.

S7-1200 Modbus Implementation: Hardware and Firmware Prerequisites

Modbus TCP is supported natively on every S7-1200 CPU with a PROFINET port (firmware V4.0 and later). Modbus RTU requires either:

  • A CM 1241 communication module (RS-485 or RS-232), or
  • A CB 1241 RS-485 board on the left side of the CPU, or
  • A CM PtP module configured for RS-485 freeport with the Modbus RTU instruction set.
Module Order Number Interface Max Slaves / Channels
CM 1241 RS-232 6ES7241-1AH32-0XB0 RS-232 1 channel
CM 1241 RS-485 6ES7241-1CH32-0XB0 RS-485 1 channel, 32 nodes
CB 1241 RS-485 6ES7241-1CH30-1XB0 RS-485 1 channel, 32 nodes
CM 1241 RS-422/485 6ES7241-1CH32-0XB0 (firmware V2.1+) RS-422/485 switchable 1 channel
A CPU S7-1215C (6ES7215-1AG40-0XB0) carries two PROFINET ports (X1, X2) and supports up to three CM/CB modules plus one AS-i master. This is the typical CPU selected for robot-cell integrations where Modbus TCP runs alongside PROFINET to a robot such as a Universal Robots UR10.

Modbus TCP Configuration on S7-1200 in TIA Portal

Prerequisites

  • TIA Portal V16 or later (V17/V18 recommended for current firmware V4.5+ CPUs)
  • S7-1200 CPU firmware V4.2 or later (V4.4 for secure Modbus TLS)
  • CPU in Run or Stop with accessible PROFINET interface
  • Static IP on CPU PROFINET interface (avoid DHCP for fixed slaves)

Step-by-Step

  1. Insert the Modbus TCP instruction. Open the program block and drag MB_CLIENT from Instructions > Communication > Communication Processor > Modbus TCP. Create a global instance DB when prompted.
  2. Wire REQ to a Bool trigger (rising edge) or a cyclic OB1 contact to issue repeated reads/writes.
  3. Configure CONNECT. Create a TCON_IP_V4 data block element. Enter the slave IP, port 502, and the local PROFINET port of the S7-1200 (any free local port > 1024).
  4. Set MB_MODE (function code).
    MB_MODE Function FC
    0 Read holding registers 03
    1 Write holding register (single) 06
    2 Write holding registers (multi) 16
    3 Read/write holding registers 23
    4 Read input registers 04
    5 Write coils 05
    6 Read coils 01
    7 Read discrete inputs 02
    8 Write coils (multi) 15
  5. Set MB_DATA_ADDR. This is the 0-based Modbus offset (not the 1-based Modicon label). To address holding register "40001", enter 0. To address coil "00017", enter 16.
  6. Set MB_DATA_LEN. Number of bits (coils/DI) or words (registers). Range 1-125 words for FC 03/04, 1-1968 coils for FC 01/02/15.
  7. Assign MB_DATA_PTR. A tag in a standard or optimized global DB sized to the request. Use Word for HR/IR, Bool array for coils/DI.
  8. Set DONE, BUSY, ERROR, STATUS handles and wire STATUS to an HMI diagnostic tag.

Modbus RTU Configuration on S7-1200 (CB 1241 / CM 1241)

Modbus RTU uses MB_COMM_LOAD for port setup and MB_MASTER / MB_SLAVE for message handling. Critical parameters on MB_COMM_LOAD:

Input Typical Value Notes
BAUD 9600 / 19200 / 38400 / 115200 Match slave DIP or vendor spec
PARITY 0 (none), 1 (odd), 2 (even) RTU standard = 8E1; many inverters use 8N1
FLOW_CTRL 0 (none), 1 (XON/XOFF), 2 (RTS always on) RS-485: 2 (no hardware handshake)
RESP_TIMEOUT 1000 ms default Increase to 3000-5000 ms for slow RF/serial converters
MB_DB Background DB Generated automatically by instruction

RS-485 termination: install 120 Ω across A-B at both bus ends only. On multi-drop, set bias resistors (typically 680 Ω to +5 V on B, 680 Ω to GND on A) if the master is not the bias source. CB 1241 ships without bias; CM 1241 V2.1+ exposes bias via DIP switch.

Master-Side Address Translation (0-based vs 1-based vs Offset)

The single largest source of "the slave won't respond" tickets is offset confusion. The Modbus protocol always uses a 0-based address on the wire. Slave vendor documents use three different conventions:

Slave Doc Label Wire Address (decimal) MB_DATA_ADDR
40001 (Modicon) 0 0
40010 (Modicon) 9 9
Register 1 (vendor zero-indexed) 0 0
Register 1 (vendor one-indexed) 1 1
Reg. 100 + offset 0x100 256 256

Always confirm with a known response. Read register "1" and verify the slave returns the documented default (often 0x0001 or the device's firmware build code). If you get STATUS = 16#8381 (illegal data address), increment or decrement by one and retry.

Discrete I/O Range Split: Inputs 0-511, Outputs 512-1023

A common pattern in low-cost serial I/O multiplexers and certain industrial gateways is to expose:

  • Discrete Inputs as Modbus addresses 0-511 (FC 02)
  • Discrete Outputs (Coils) as Modbus addresses 512-1023 (FC 01/05/15)

This is a vendor-specific mapping that lets the master treat the entire device as one contiguous address space while still respecting the read-only vs read/write nature of each table. On the S7-1200, the resulting configuration must use FC 02 with offsets 0-511 and FC 05/15 with offsets 512-1023. Attempting to read coils at offset 100 or to write discrete inputs at offset 600 will return illegal-data-address (0x02).

A correct commissioning sequence is:
  1. Read discrete input 0 (expect slave's "input 1" status).
  2. Read coil 512 (expect slave's "output 1" status).
  3. Write coil 512 = 1, observe physical relay, then write 0.
  4. Only then enable the cyclic OB block.

Status Code Matrix and Root Cause

STATUS (hex) Meaning Root Cause Corrective Action
16#0000 No error - -
16#7000 No active request REQ edge missed Use rising edge orcyclic OB pulse
16#7001 First execution - Continue
16#7002 Subsequent execution - Continue
16#8085 FC not allowed in MB_MODE for this slave MB_MODE selection wrong Match mode to table
16#80C8 Slave did not respond within RESP_TIMEOUT Wiring, baud, parity, IP/port Verify cable, ping TCP slave, increase RESP_TIMEOUT
16#80D1 Send error (TCP) Connection not established Check TCON connection state
16#80D2 Receive error (TCP) TCP closed by peer Check slave cycle, firewall
16#80D4 Connection terminated Slave went offline Verify slave power/network
16#8380 Modbus exception 01 - illegal function FC not supported by slave Use a supported FC
16#8381 Modbus exception 02 - illegal data address MB_DATA_ADDR out of range Adjust offset (0-based vs 1-based)
16#8382 Modbus exception 03 - illegal data value MB_DATA_LEN invalid for slave Reduce length to slave max
16#8383 Modbus exception 04 - slave device failure Slave internal fault Check slave diagnostics
16#8384 Modbus exception 05 - acknowledge Long command in progress Wait for next cycle
16#8385 Modbus exception 06 - slave busy Previous command active Insert idle gap, retry
16#8386 Modbus exception 08 - memory parity error Slave NVRAM fault Replace slave or reflash

Migration Path: When Modbus Is Not the Right Choice

Some downstream devices do not implement Modbus at all, or implement it incompletely. Universal Robots' UR10 collaborative arm is a textbook case: its native fieldbus is PROFINET (URsim e-Series), with a Real-Time (RT) and optional IRT stack. Modbus TCP can be exposed as a profile option in the URCap SDK, but the supported, deterministic data path is PROFINET.

If a Siemens S7-1215C must integrate a UR10:

  1. Install the UR PROFINET GSDML file from the manufacturer support portal into TIA Portal (Options > Manage General Station Description Files).
  2. Insert the UR10 device into the PROFINET topology on the S7-1200 CPU's X1 port.
  3. Configure the slot assignment: usually slot 1 = input module, slot 2 = output module; size matches the UR's published process data width (commonly 16 bytes in / 16 bytes out).
  4. Assign a unique device name (e.g., "UR10") and IP within the same subnet as the CPU (e.g., 192.168.1.x/24).
  5. Download the HWCN and verify the UR10's status LED turns green; the S7-1200 will report the device as reachable in Online > Accessible Nodes.
If the "robot unreachable" error persists after PROFINET configuration, the three highest-probability causes are (1) duplicate IP or device name, (2) missing GSDML installation in TIA Portal, or (3) PROFINET name assignment not pushed to the UR (right-click device > "Assign PROFINET device name").

Verification Procedure

  1. From a workstation on the same subnet, ping the slave IP and confirm a reply under 5 ms (Modbus TCP) or open a TCP socket to port 502 with netcat and send a manual 0x00 0x03 0x00 0x00 0x00 0x01 0x84 0x0A request (read HR0) and verify a 5-byte exception-free response.
  2. Force REQ in the S7-1200 program and observe DONE = TRUE and ERROR = FALSE within one OB1 scan plus RESP_TIMEOUT.
  3. Read STATUS via the HMI; value must be 16#0000 after completion.
  4. Toggle a known output (write coil = 1, observe physical contact, write = 0) before enabling any interlocks.
  5. Capture a Wireshark trace on the TCP path and confirm valid Modbus/TCP MBAP header (transaction ID, protocol 0, length, unit ID) and matching function code.
  6. For RTU: capture the bus with a USB-RS-485 dongle at 9600 8E1 (or matching slave settings) and confirm inter-character silence < 1.5 char times and inter-frame silence > 3.5 char times.

Common Edge Cases and Field-Proven Caveats

  • Optimized vs standard DB blocks. When using MB_DATA_PTR with an optimized (s7-1200/1500 default) DB, the MB_CLIENT requires the AT view or a non-optimized slice; otherwise the STATUS returns 16#80B1. Either disable optimization on the DB or create a non-optimized POKE area in a separate DB.
  • Byte-swap on 32-bit values. Modbus is big-endian; S7-1200 is little-endian. A 32-bit float read from a sensor will arrive byte-swapped unless the slave implements word-swap or you handle it with TAW / CAD in the application.
  • Unit ID = 0 for TCP. Many slaves ignore the unit ID on TCP, but some bridges require 0xFF or the original RTU unit ID. Always confirm with the slave manual.
  • Multiple masters on RTU. Modbus RTU supports a single master; a second polling node will cause CRC collisions and silent 16#80C8 timeouts.
  • Watchdog on TCP slave restart. After a slave reboot, the S7-1200 keeps the TCP socket open but receives no response. MB_CLIENT reports 16#80C8 until DONE or ERROR clears and a fresh REQ is issued. Build a small SCL state machine to issue REQ only after verifying BUSY = FALSE.
  • Security. Plain Modbus TCP has no authentication. For production cells with safety or process-critical data, isolate the Modbus network on a VLAN and use the S7-1200 firewall (CPU FW V4.4+) to restrict source IPs.

Inter-Platform Notes

For integrators porting Modbus code between controllers, the data-layer semantics are identical, but the calling convention differs. Schneider Electric's M340/M580 use READ_VAR / WRITE_VAR with %MW addressing; Allen-Bladley CompactLogix uses MSG instructions with CIP-to-Modbus routing; Omron NJ/NX uses the Sysmac Modbus_RTU/TCP function blocks with explicit node, address, and quantity parameters. The 0-based address rule applies uniformly; the only platform-specific trap is the Siemens optimized-DB byte layout. For legacy Modicon M340, the legacy 984 addressing (40001 = 0) matches Siemens; for CompactLogix, the MSG tag uses 1-based by default and subtracts 1 internally.

FAQ

Why does my S7-1200 Modbus read return STATUS 16#8381 (illegal data address)?

The MB_DATA_ADDR is out of the slave's valid range or uses the wrong convention. Modbus is 0-based on the wire, so slave label 40001 corresponds to address 0 in MB_DATA_ADDR. Subtract 1 from any 1-based vendor label.

How do I read a coil (bit) versus a holding register (word) on the same slave?

Use MB_MODE = 6 (read coils, FC 01) with a Bool buffer for bits and MB_MODE = 0 (read holding registers, FC 03) with a Word buffer for 16-bit data. Mixing modes returns illegal-function 0x01 from the slave.

What is the difference between MB_CLIENT and MB_SLAVE on the S7-1200?

MB_CLIENT initiates requests to remote Modbus servers (master role); MB_SLAVE responds to incoming requests and exposes CPU data as Modbus registers to other masters. An S7-1200 can run both simultaneously on different ports or serial modules.

Why does STATUS 16#80C8 keep appearing on RS-485?

The slave did not respond within RESP_TIMEOUT. Verify baud rate, parity (8E1 vs 8N1), A/B polarity, termination (120 Ω at both ends only), and that no other master is on the bus. Increase RESP_TIMEOUT to 3000-5000 ms for slow slaves.

Can an S7-1215C communicate with a Universal Robots UR10 over Modbus?

UR10's native deterministic fieldbus is PROFINET. Modbus TCP works via URCap but is not the recommended path for real-time motion. For new integrations, install the UR PROFINET GSDML into TIA Portal, add the UR10 to the PROFINET topology, and use the process data slots directly.

Back to blog