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 |
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:
-
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_CLIENTrequires the 0-based value inMB_DATA_ADDR. -
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. - 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 |
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
-
Insert the Modbus TCP instruction. Open the program block and drag
MB_CLIENTfrom Instructions > Communication > Communication Processor > Modbus TCP. Create a global instance DB when prompted. -
Wire
REQto a Bool trigger (rising edge) or a cyclic OB1 contact to issue repeated reads/writes. -
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). -
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 -
Set
MB_DATA_ADDR. This is the 0-based Modbus offset (not the 1-based Modicon label). To address holding register "40001", enter0. To address coil "00017", enter16. -
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. -
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. -
Set
DONE,BUSY,ERROR,STATUShandles and wireSTATUSto 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).
- Read discrete input 0 (expect slave's "input 1" status).
- Read coil 512 (expect slave's "output 1" status).
- Write coil 512 = 1, observe physical relay, then write 0.
- 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:
- Install the UR PROFINET GSDML file from the manufacturer support portal into TIA Portal (Options > Manage General Station Description Files).
- Insert the UR10 device into the PROFINET topology on the S7-1200 CPU's X1 port.
- 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).
- Assign a unique device name (e.g., "UR10") and IP within the same subnet as the CPU (e.g., 192.168.1.x/24).
- 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.
Verification Procedure
- 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
netcatand send a manual0x00 0x03 0x00 0x00 0x00 0x01 0x84 0x0Arequest (read HR0) and verify a 5-byte exception-free response. - Force
REQin the S7-1200 program and observeDONE = TRUEandERROR = FALSEwithin one OB1 scan plusRESP_TIMEOUT. - Read
STATUSvia the HMI; value must be16#0000after completion. - Toggle a known output (write coil = 1, observe physical contact, write = 0) before enabling any interlocks.
- 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.
- 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_PTRwith an optimized (s7-1200/1500 default) DB, theMB_CLIENTrequires the AT view or a non-optimized slice; otherwise the STATUS returns16#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/CADin the application. -
Unit ID = 0 for TCP. Many slaves ignore the unit ID on TCP, but some bridges require
0xFFor 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#80C8timeouts. -
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#80C8untilDONEorERRORclears and a fresh REQ is issued. Build a small SCL state machine to issue REQ only after verifyingBUSY = 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.