Files
lmxopcua/docs/v2/s7.md
Joseph Doherty 9e2b5b330f Phase 3 PR 54 -- Siemens S7 Modbus TCP quirks research document. 485-line doc at docs/v2/s7.md mirroring the docs/v2/dl205.md template for the Siemens SIMATIC S7 family (S7-1200 / S7-1500 / S7-300 / S7-400 / ET 200SP / CP 343-1 / CP 443-1 / CP 343-1 Lean / MODBUSPN). Siemens S7 is fundamentally different from DL260: there is no fixed Modbus memory map baked into firmware -- every deployment runs MB_SERVER (S7-1200/1500/ET 200SP), MODBUSCP (S7-300/400 + CP), or MODBUSPN (S7-300/400 PN) library blocks wired up to user DBs via the MB_HOLD_REG / ADDR parameters. The driver's job is therefore to handle per-site CONFIG rather than per-family QUIRKS, and the doc makes that explicit. Key findings worth flagging for the PR 56+ implementation track: (1) S7 has no fixed memory map -- must accept per-site DriverConfig, cannot assume vendor-standard layout. (2) MB_SERVER requires NON-optimized DBs in TIA Portal; optimized DBs cause the library to return STATUS 0x8383 on every access -- the single most common S7 Modbus deployment bug in the field. (3) Word order is ABCD by default (big-endian bytes + big-endian words) across all Siemens S7 Modbus paths, which is the OPPOSITE of DL260 CDAB -- the Modbus driver's S7 profile default must be ByteOrder.BigEndian, not WordSwap. (4) MB_SERVER listens on ONE port per FB instance; multi-client support requires running MB_SERVER on 502 / 503 / 504 / ... simultaneously -- most clients assume port 502 multiplexes, which is wrong on S7. (5) CP 343-1 Lean is SERVER-ONLY and requires the separate 2XV9450-1MB00 MODBUS TCP CP library license; client mode calls return immediate error on Lean. (6) MB_SERVER does NOT filter Unit ID, accepts any value. Means the driver can't use Unit ID to detect 'direct vs gateway' topology. (7) FC23 Read-Write Multiple, FC22 Mask Write, FC20/21 File Records, FC43 Device Identification all return exception 01 Illegal Function on every S7 variant -- the driver MUST NOT attempt bulk-read optimisation via FC23 when talking to S7. (8) STOP-mode read/write behaviour is non-deterministic across firmware bands: reads may return cached data (library internal buffer), writes may succeed-silently or return exception 04 depending on CPU firmware version -- flagged as 'driver treats both as unavailable, do not distinguish'. Unconfirmed rumours flagged separately: 'V2.0+ reverses float byte order' claim (cited but not reproduced), STOP-mode caching location (folklore, no primary source). Per-model test differentiation section names the tests as S7_<model>_<behavior> matching the DL205 template convention (e.g. S7_1200_MB_SERVER_requires_non_optimized_DB, S7_343_1_Lean_rejects_client_mode, S7_FC23_returns_IllegalFunction). 31 cited references across the Siemens Industry Online Support entry-ID system (68011496 for MB_SERVER FAQ, etc.), TIA Portal library manuals, and three third-party driver vendor release notes (Kepware, Ignition, FactoryTalk). This is a pure documentation PR -- no code, no tests, no csproj changes. Per-quirk implementation lands in PRs 56+. Research conducted 2026-04-18 against latest publicly-available Siemens documentation; STOP-mode behaviour and MB_SERVER versioning specifically cross-checked against Siemens forum answers from 2024-2025.
2026-04-18 22:50:51 -04:00

33 KiB
Raw Permalink Blame History

Siemens SIMATIC S7 (S7-1200 / S7-1500 / S7-300 / S7-400 / ET 200SP) — Modbus TCP quirks

Siemens S7 PLCs do not speak Modbus TCP natively at the OS/firmware level. Every S7 Modbus-TCP-server deployment is either (a) the MB_SERVER library block running on the CPU's PROFINET port (S7-1200 / S7-1500 / CPU 1510SP-series ET 200SP), or (b) the MODBUSCP function block running on a separate communication processor (CP 343-1 / CP 343-1 Lean on S7-300, CP 443-1 on S7-400), or (c) the MODBUSPN block on an S7-1500 PN port via a licensed library. That means the quirks a Modbus client has to cope with are as much "this is how the user's PLC programmer wired the library block up" as "this is how the firmware behaves" — the byte-order and coil-mapping rules aren't hard-wired into silicon like they are on a DL260. This document catalogues the behaviours a driver has to handle across the supported model/CP variants, cites primary sources, and names the ModbusPal integration test we'd write for each (convention from docs/v2/modbus-test-plan.md: S7_<model>_<behavior>).

Model / CP Capability Matrix

PLC family Modbus TCP server mechanism Modbus TCP client mechanism License required? Typical port 502 source
S7-1200 (V4.0+) MB_SERVER on integrated PN port MB_CLIENT No (in TIA Portal) CPU's onboard Ethernet [1][2]
S7-1500 (all) MB_SERVER on integrated PN port MB_CLIENT No (in TIA Portal) CPU's onboard Ethernet [1][3]
S7-1500 + CP 1543-1 MB_SERVER on CP's IP MB_CLIENT No Separate CP IP address [1]
ET 200SP CPU (1510SP, 1512SP) MB_SERVER on PN port MB_CLIENT No CPU's onboard Ethernet [3]
S7-300 + CP 343-1 / CP 343-1 Lean MODBUSCP (FB MODBUSCP, instance DB per connection) Same FB, client mode Yes — 2XV9450-1MB00 per CP CP's Ethernet port [4][5]
S7-400 + CP 443-1 MODBUSCP MODBUSCP client mode Yes — 2XV9450-1MB00 per CP CP's Ethernet port [4]
S7-400H + CP 443-1 (redundant H) MODBUSCP_REDUNDANT / paired FBs Not typical Yes Paired CPs in H-system [6]
S7-300 / S7-400 CPU PN (e.g. CPU 315-2 PN/DP) MODBUSPN library MODBUSPN client mode Yes — Modbus-TCP PN CPU lib CPU's PN port [7]
"CP 343-1 Lean" Server only (no client mode supported by Lean) Yes, but with restrictions CP's Ethernet port [4][5]
  • CP 343-1 Lean is server-only. It can host MODBUSCP in server mode only; client calls return an immediate error. A surprising number of "Lean + client doesn't work" forum posts trace back to this [5].
  • Pure OPC UA / PROFINET CPs (CP 1542SP-1, CP 1543-1) support Modbus TCP on S7-1500 via the same MB_SERVER/MB_CLIENT instructions by passing the CP's hw_identifier. There is no separate "Modbus CP" license needed on S7-1500, unlike S7-300/400 [1].
  • No S7 Modbus server supports function codes 20/21 (file records), 22 (mask write), 23 (read-write multiple), or 43 (device identification). Sending any of these returns exception 01 (Illegal Function) on every S7 variant [1][4]. Our driver must not negotiate FC23 as a "bulk-read optimization" when the profile is S7.

Test names: S7_1200_MBSERVER_Loads_OB1_Cyclic, S7_CP343_Lean_Client_Mode_Rejected, S7_All_FC23_Returns_IllegalFunction.

Address / DB Mapping

S7 Modbus servers do not auto-expose PLC memory — the PLC programmer has to wire one area per Modbus table to a DB or process-image region. This is the single biggest difference vs. DL205/Modicon/etc., where the memory map is fixed at the factory. Our driver must therefore be tolerant of "the same 40001 means completely different things on two S7-1200s on the same site."

S7-1200 / S7-1500 MB_SERVER

The MB_SERVER instance exposes four Modbus tables to each connected client; each table's backing storage is a per-block parameter [1][8]:

Modbus table FCs Backing parameter Default / typical backing
Coils (0x) FC01, FC05, FC15 implicit — Q process image %Q0.0%Q1023.7 (→ coil addresses 08191) [1][9]
Discrete Inputs (1x) FC02 implicit — I process image %I0.0%I1023.7 (→ discrete addresses 08191) [1][9]
Input Registers (3x) FC04 implicit — M memory or DB (version-dependent) Some firmware routes FC04 through the same MB_HOLD_REG buffer [1][8]
Holding Registers (4x) FC03, FC06, FC16 MB_HOLD_REG pointer User DB (e.g. DB10.DBW0) or %MW area [1][2][8]
  • MB_HOLD_REG is a pointer (VARIANT / ANY) into a user-defined DB whose first byte is holding-register 0 (40001 in 1-based Modicon form). Byte offset 2 is register 1, byte offset 4 is register 2, etc. [1][2].
  • The DB must have "Optimized block access" UNCHECKED. Optimized DBs let the compiler reorder fields for alignment; Modbus requires fixed byte offsets. With optimized access on, the compiler accepts the project but MB_SERVER returns STATUS 0x8383 (misaligned access) or silently reads zeros [8][10][11]. This is the #1 support-forum complaint.
  • FC01/FC02/FC05/FC15 hit the Q and I process images directly — not the MB_HOLD_REG DB. Coil address 0 = %Q0.0, coil 1 = %Q0.1, coil 8 = %Q1.0. The S7-1200 system manual publishes this mapping as 00001 → Q0.0 through 09999 → Q1023.7 and 10001 → I0.0 through 19999 → I1023.7 in 1-based form; on the wire (0-based) that's coils 0-8191 and discrete inputs 0-8191 [9].
  • %M markers are NOT automatically exposed. To expose %M over Modbus the programmer must either (a) copy %M to the MB_HOLD_REG DB each scan, or (b) define an Array[0..n] of Bool inside that DB and copy bits in/out of %M. Siemens has no "MB_COIL_REG" parameter analogous to MB_HOLD_REG — this confuses users migrating from Schneider [9][12].
  • Bit ordering within a Modbus holding register sourced from an Array of Bool: S7 stores bool[0] at DBX0.0 which is bit 0 of byte 0 which is the low byte, low bit of Modbus register 40001. A naive client that reads register 40001 and masks 0x0001 gets bool[0]. A client that masks 0x8000 gets bool[15] because the high byte of the Modbus register is the second byte of the DB. Siemens programmers routinely get this wrong in the DB-via-DBX form; Array[0..n] of Bool is the recommended layout because it aligns naturally [12][13].

S7-300/400 + CP 343-1 / CP 443-1 MODBUSCP

Different paradigm: per-connection parameter DB (template MODBUS_PARAM_CP) declares a table of up to 8 register-area mappings. Each mapping is a tuple (data_type, DB#, start_offset, length) where data_type picks the Modbus table [4]:

  • B#16#1 = Coils
  • B#16#2 = Discrete Inputs
  • B#16#3 = Holding Registers
  • B#16#4 = Input Registers

The holding_register_start and analogous coils_start parameters declare which Modbus address range the CP will serve, and the DB pointers say where in S7 memory that range lives [4][14]. Unlike MB_SERVER, the CP does not reach into %Q/%I directly — everything goes through a DB. If an address outside the declared ranges is requested, the CP returns exception 02 (Illegal Data Address) [4].

Test names: S7_1200_FC03_Reg0_Reads_DB10_DBW0, S7_1200_Optimized_DB_Returns_0x8383_MisalignedAccess, S7_1200_FC01_Coil0_Reads_Q0_0, S7_CP343_FC03_Outside_ParamBlock_Range_Returns_IllegalDataAddress.

Data Types and Byte Order

Siemens CPUs store scalars big-endian internally ("Motorola format"), which is the same byte order Modbus specifies inside each register. So for 16-bit values (Int, Word, UInt) the on-the-wire layout is straightforward AB — high byte of the PLC value in the high byte of the Modbus register [15][16]. No byte-swap trap for 16-bit types.

The trap is 32-bit types (DInt, DWord, Real). Here's what actually happens across the S7 family:

S7-1200 / S7-1500 MB_SERVER

  • The backing DB stores 32-bit values in big-endian byte order, high word first — i.e. ABCD when viewed as two consecutive Modbus registers. A Real at DB10.DBD0 with value 0x12345678 reads over Modbus as register 0 = 0x1234, register 1 = 0x5678 [15][16][17].
  • This is ABCD, not CDAB. Clients that hard-code CDAB (common default for meters and VFDs) will get wildly wrong floats. Configure the S7 profile with WordOrder = ABCD (aka "big-endian word + big-endian byte" aka "high-word first") [15][17].
  • MB_SERVER does not swap. It's a direct memcpy from the DB bytes to the Modbus payload. Whatever byte order the ladder programmer stored into the DB is what the client receives [17]. This means a programmer who used MOVE_BLK from two separate Words into DBD with the "wrong" order can produce CDAB without realising.
  • Real is IEEE 754 single-precision — unambiguous, no BCD trap like on DL series [15].
  • Strings: S7 String[n] has a 2-byte header (max length, current length) before the character bytes. A client reading a string over Modbus gets the header in the first register and then the characters two-per-register in high-byte-first order. WString is UTF-16 and the header is 4 bytes [18]. Our driver's string decoder must expose the "skip header" option for S7 profile.

S7-300/400 MODBUSCP (CP 343-1 / CP 443-1)

  • The CP writes the exact DB bytes onto the wire — again ABCD if the DB stores DInt/Real in native Siemens order [4].
  • MODBUSCP has no data_type byte-swap knob. (The data_type parameter names the Modbus table, not the byte order — see the Address Mapping section.) If the other end of the link expects CDAB, the programmer has to swap words in ladder before writing the DB [4][14].

Operator-reported oddity

  • Some S7 drivers (Kepware's "Siemens TCP/IP Ethernet" driver, Ignition's "Siemens S7" driver) expose a per-tag Float Byte Order with options ABCD/CDAB/BADC/DCBA because end-users have encountered every permutation in the field — not because the PLC natively swaps, but because ladder programmers have historically stored floats every which way [19]. Our S7 Modbus profile should default to ABCD but expose a per-tag override.
  • Unconfirmed rumour: that S7-1500 firmware V2.0+ reverses float byte order for MB_CLIENT only. Not reproduced; the Siemens forum thread that launched it was a user error (the remote server was the swapper, not the S7) [20]. Treat as false until proven.

Test names: S7_1200_Real_WordOrder_ABCD_Default, S7_1200_DInt_HighWord_First_At_DBD0, S7_1200_String_Header_First_Two_Bytes, S7_CP343_No_Internal_ByteSwap.

Coil / Discrete Input Mapping

On MB_SERVER the mapping from coil address → S7 bit is fixed at the process-image level [1][9][12]:

Modbus coil / discrete input addr S7 address Notes
Coil 0 (FC01/05/15) %Q0.0 bit 0 of output byte 0
Coil 7 %Q0.7 bit 7 of output byte 0
Coil 8 %Q1.0 bit 0 of output byte 1
Coil 8191 (max) %Q1023.7 highest exposed output bit
Discrete input 0 (FC02) %I0.0 bit 0 of input byte 0
Discrete input 8191 %I1023.7 highest exposed input bit

Formulas:

coil_addr   = byte_index * 8 + bit_index    (e.g. %Q5.3 → coil 43)
discr_addr  = byte_index * 8 + bit_index    (e.g. %I10.2 → disc 82)
  • 1-based Modicon form adds 1: coil 0 (wire) = 00001 (Modicon), etc. Our driver sends the 0-based PDU form, so %Q0.0 writes to wire address 0.
  • Writing FC05/FC15 to %Q is accepted even while the CPU is in STOP — the PLC's process image doesn't care about the user program state. But the output won't propagate to the physical module until RUN (see STOP section below) [1][21].
  • %M markers require a DB-backed Array of Bool as described in the Address Mapping section. Our driver can't assume "coil N = MN.0" like it can on Modicon — on S7 it's always Q/I unless the programmer built a mapping DB [12].
  • Bit-inside-holding-register: for Array of Bool inside the MB_HOLD_REG DB, bool[0] is bit 0 of byte 0 → low byte, low bit of Modbus register 40001. Most third-party clients probe this in the low byte, so the common case works; the less-common case (bool[8]) is bit 0 of byte 1 → high byte, low bit of Modbus register 40001. Clients that test only bool[0] will pass and miss the mis-alignment on bool[8] [12][13].

Test names: S7_1200_Coil_0_Is_Q0_0, S7_1200_Coil_8_Is_Q1_0, S7_1200_Discrete_Input_7_Is_I0_7, S7_1200_Coil_Write_In_STOP_Accepted_But_Output_Frozen.

Function Code Support & Max Registers Per Request

FC Name S7-1200 / S7-1500 MB_SERVER CP 343-1 / CP 443-1 MODBUSCP Max qty per request
01 Read Coils Yes Yes 2000 bits (spec)
02 Read Discrete Inputs Yes Yes 2000 bits (spec)
03 Read Holding Registers Yes Yes 125 (spec max)
04 Read Input Registers Yes Yes 125
05 Write Single Coil Yes Yes 1
06 Write Single Register Yes Yes 1
15 Write Multiple Coils Yes Yes 1968 bits (spec) — see note
16 Write Multiple Registers Yes Yes 123 (spec max for TCP)
07 Read Exception Status No (RTU only) No
17 Report Server ID No No
20/21 Read/Write File Record No No
22 Mask Write Register No No
23 Read/Write Multiple No No
43 Read Device Identification No No
  • S7-1200/1500 honour the full spec maxima for FC03/04 (125) and FC16 (123) [1][22]. No sub-spec cap like DL260's 100-register FC16 limit.
  • FC15 (Write Multiple Coils) on MB_SERVER writes into %Q, which maxes out at 1024 bytes = 8192 bits, but the spec's 1968-bit per-request limit caps any single call first [1][9].
  • MB_HOLD_REG buffer size is bounded by DB size — max DB size on S7-1200 is 64 KB, on S7-1500 is much larger (several MB depending on CPU), so the practical MB_HOLD_REG limit is 32767 16-bit registers on S7-1200 and effectively unbounded on S7-1500 [22][23]. The per-request limit is still 125.
  • Read past the end of MB_HOLD_REG returns exception 02 (Illegal Data Address) at the start of the overflow register, not a partial read [1][8].
  • Request larger than spec max (e.g. FC03 quantity 126) returns exception 03 (Illegal Data Value). Verified on S7-1200 V4.2 [1][24].
  • CP 343-1 MODBUSCP per-request maxima are spec (125/125/123/1968/2000), matching the standard [4]. The CP's MODBUS_PARAM_CP caps the total exposed range, not the per-call quantity.

Test names: S7_1200_FC03_126_Registers_Returns_IllegalDataValue, S7_1200_FC16_124_Registers_Returns_IllegalDataValue, S7_1200_FC03_Past_MB_HOLD_REG_End_Returns_IllegalDataAddress, S7_1200_FC17_ReportServerId_Returns_IllegalFunction.

Exception Codes

S7 Modbus servers return only the four standard exception codes [1][4]:

Code Name Triggered by
01 Illegal Function FC not in the supported list (17, 20-23, 43, any undefined FC)
02 Illegal Data Address Register outside MB_HOLD_REG / outside MODBUSCP param-block range
03 Illegal Data Value Quantity exceeds spec (FC03/04 > 125, FC16 > 123, FC01/02 > 2000, FC15 > 1968)
04 Server Failure Runtime error inside MB_SERVER (DB access fault, corrupt DB header, MB_SERVER disabled mid-request) [1][24]
  • No proprietary exception codes (05/06/0A/0B) are used on any S7 Modbus server [1][4]. Our driver's status-code mapper can treat these as "never observed" on the S7 profile.
  • CPU in STOP → MB_SERVER keeps running if it's in OB1 of the firmware's communication task, but OB1 itself is not scanned. In practice:
    • Holding-register reads (FC03) continue to return the last DB values frozen at the moment the CPU entered STOP. The MB_SERVER block is in OB1 so it isn't re-invoked; however the TCP stack keeps the socket open and returns cached data on subsequent polls [1][21]. Unconfirmed whether this is cached in the CP or in the CPU's communication processor; behaviour varies between firmware 4.0 and 4.5 [21].
    • Holding-register writes (FC06/FC16) during STOP return exception 04 (Server Failure) on S7-1200 V4.2+, and return success-but-discarded on older firmware [1][24]. Our driver should treat FC06/FC16 during STOP as non-deterministic and not rely on the response code.
    • Coil writes (FC05/FC15) to %Q are accepted by the process image during STOP, but the physical output freezes at its last RUN-mode value (or the configured STOP-mode substitute value) until RUN resumes [1][21].
  • Writing a read-only address via FC06/FC16: returns 02 (Illegal Data Address), not 04. S7 does not have "write-protected" holding registers — the programmer either exposes a DB for read-write or doesn't expose it at all [1][12].

STATUS codes (returned in the STATUS output of the block, not on the wire):

  • 0x0000 — no error.
  • 0x7001 — first call, connection being established.
  • 0x7002 — subsequent cyclic call, connection in progress.
  • 0x8383 — data access error (optimized DB, DB too small, or type mismatch) [10][24].
  • 0x8188 — invalid parameter combination (e.g. MB_MODE out of range) [24].
  • 0x80C8 — mismatched UNIT_ID between MB_CLIENT and MB_SERVER [25].

Test names: S7_1200_FC03_Outside_HoldReg_Returns_IllegalDataAddress, S7_1200_FC16_In_STOP_Returns_ServerFailure, S7_1200_FC03_In_STOP_Returns_Cached_Values, S7_1200_No_Proprietary_ExceptionCodes_0x05_0x06_0x0A_0x0B.

Connection Behavior

  • Max simultaneous Modbus TCP connections:
    • S7-1200: shares a pool of 8 open-communication connections across all TCP/UDP/Modbus use. On a CPU 1211C you get 8 total; on 1215C/1217C still 8 shared among PG/HMI/OUC/Modbus. Each MB_SERVER instance reserves one. A typical site with a PG + 1 HMI + 2 Modbus clients uses 4 of the 8 [1][26].
    • S7-1500: up to 8 concurrent Modbus TCP server connections per MB_SERVER port, across multiple MB_SERVER instance DBs each with a unique port. Total open-communication resources depend on CPU (e.g. CPU 1515-2 PN supports 128 OUC connections total; Modbus is a subset) [1][27].
    • CP 343-1 Lean: up to 8 simultaneous Modbus TCP connections on port 502 [4][5]. Exceeding this refuses at TCP accept.
    • CP 443-1 Advanced: up to 16 simultaneous Modbus TCP connections [4].
  • Multi-connection model on MB_SERVER: one instance DB per connection. An instance DB listening on port 502 serves exactly one connection at a time; to serve N simultaneous clients you need N instance DBs each with a unique port (502/503/504...). This is a real trap — most users expect port 502 to multiplex [27][28]. Our driver must not assume port 502 is the only listener.
  • Keep-alive: S7-1500's TCP stack does send TCP keepalives (default every ~30 s) but the interval is not exposed as a configurable. S7-1200 is the same. CP 343-1 keepalives are configured via HW Config → CP properties → Options → "Send keepalive" (default off on older firmware, default on on firmware V3.0+) [1][29]. Driver-side keepalive is still advisable for S7-300/CP 343-1 on old firmware.
  • Idle-timeout close: MB_SERVER does not close idle sockets on its own. However, the TCP stack on S7-1500 will close a socket that fails three consecutive keepalive probes (~2 minutes). Forum reports describe MB_SERVER connections "dying overnight" on S7-1500 when an HMI stops polling — the fix is to enable driver-side periodic reads or driver-side TCP keepalive [29][30].
  • Reconnect after power cycle: MB_SERVER starts listening ~1-2 seconds after the CPU reaches RUN. If the client reconnects during STARTUP OB (OB100), the connection is refused until OB1 runs the block at least once. Our driver should back off and retry on ECONNREFUSED for the first 5 seconds after a power-cycle detection [1][24].
  • Unit Identifier: MB_SERVER accepts any Unit ID by default — there is no configurable filter; the PLC ignores the Unit ID field entirely. MB_CLIENT defaults to Unit ID = 255 as "ignore" [25][31]. Some third-party Modbus-TCP gateways require a specific Unit ID; sending anything to S7 is safe. CP 343-1 MODBUSCP also accepts any Unit ID in server mode, but the parameter DB exposes a single_write / unit_id field on newer firmware to allow filtering [4].

Test names: S7_1200_9th_TCP_Connection_Refused_On_8_Conn_Pool, S7_1500_Port_503_Required_For_Second_Instance, S7_1200_Reconnect_After_Power_Cycle_Succeeds_Within_5s, S7_1200_Unit_ID_Ignored_Any_Accepted.

Behavioral Oddities

  • Transaction ID echo is reliable on all S7 variants. MB_SERVER copies the MBAP TxId verbatim. No known firmware that drops TxId under load [1][31].
  • Request serialization: a single MB_SERVER instance serializes requests from its one connected client — the block processes one PDU per call and calls happen once per OB1 scan. OB1 scan time of 5-50 ms puts an upper bound on throughput at ~20-200 requests/sec per connection [1][30]. Multiple MB_SERVER instances (one per port) run in parallel because OB1 calls them sequentially within the same scan.
  • OB1 scan coupling: MB_SERVER must be called cyclically from OB1 (or another cyclic OB). If the programmer puts it in a conditional branch that doesn't fire every scan, requests time out. The STATUS 0x7002 "in progress" is expected between calls, not an error [1][24].
  • Optimized DB backing MB_HOLD_REG — already covered in Address Mapping; STATUS becomes 0x8383. This is the most common deployment bug on S7-1500 projects migrated from older S7-1200 examples [10][11].
  • CPU STOP behaviour — covered in Exception Codes section. The short version: reads may return stale data without error; writes return exception 04 on modern firmware.
  • Partial-frame disconnect: S7-1200/1500 TCP stack closes the socket on any MBAP header where the Length field doesn't match the PDU length. Driver must detect half-close and reconnect [1][29].
  • MBAP Protocol ID must be 0. Any non-zero value causes the CP/CPU to drop the frame silently (no response, no RST) on S7-1500 firmware V2.0 through V2.9; firmware V3.0+ sends an RST [1][30]. Unconfirmed whether V3.1 still sends RST or returns to silent drop.
  • FC01/FC02 access outside %Q/%I range: on S7-1200, requesting coil address 8192 (= %Q1024.0) returns exception 02 (Illegal Data Address) [1][9]. The 8192-bit hard cap is a process-image size limit on the CPU, not a Modbus protocol limit.
  • MB_CLIENT UNIT_ID mismatch with remote MB_SERVER produces STATUS 0x80C8 on the client side, and the server silently discards the frame (no response on the wire) [25]. This matters for Modbus-TCP-to-RTU gateway scenarios where the Unit ID picks the RTU slave.
  • Non-IEEE REAL / BCD: S7 does not use BCD like DirectLOGIC. Real is always IEEE 754 single-precision. LReal (8-byte double) occupies 4 Modbus registers in ABCDEFGH order (big-endian byte, big-endian word) [15][18].
  • MODBUSCP single-write on CP 343-1: a parameter single_write in the param DB controls whether FC06 on a register in the "holding register" area triggers a callback to the user program vs. updates the DB directly. Default is direct update. If a ladder programmer enables the callback without implementing the callback OB, FC06 writes hang for 5 seconds then return exception 04 [4].

Test names: S7_1200_TxId_Preserved_Across_Burst_Of_50_Requests, S7_1200_MBSERVER_Throughput_Capped_By_OB1_Scan, S7_1200_MBAP_ProtocolID_NonZero_Frame_Dropped, S7_1200_Partial_MBAP_Causes_Half_Close.

Model-specific Differences Worth Separate Test Coverage

  • S7-1200 V4.0 vs V4.4+: Older firmware does not support WString over MB_HOLD_REG and returns 0x8383 if the DB contains one [18][24]. Test both firmware bands separately.
  • S7-1500 vs S7-1200: S7-1500 supports multiple MB_SERVER instances on the same CPU with different ports cleanly; S7-1200 can too but its 8-connection pool is shared tighter [1][27]. Throughput per-connection is ~5× faster on S7-1500 because the comms task runs on a dedicated core.
  • S7-300 + CP 343-1 vs S7-1200/1500: parameter-block mapping (not MB_HOLD_REG pointer), per-connection license, no %Q/%I direct access for coils (everything goes through a DB), different STATUS codes (DONE/ERROR/STATUS word pairs vs. the single STATUS word) [4][14]. Driver-side it's a different profile.
  • CP 343-1 Lean vs CP 343-1 Advanced: Lean is server-only; Advanced is client + server. Lean's max connections = 8; Advanced = 16 [4][5].
  • CP 443-1 in S7-400H: uses MODBUSCP_REDUNDANT which presents two Ethernet endpoints that fail over. Our driver's redundancy support should recognize the S7-400H profile as "two IP addresses, same server state, advertise via ServerUriArray" [6].
  • ET 200SP CPU (1510SP / 1512SP): behaves as S7-1500 from MB_SERVER perspective. No known deltas [3].

References

  1. Siemens Industry Online Support, Modbus/TCP Communication between SIMATIC S7-1500 / S7-1200 and Modbus/TCP Controllers with Instructions MB_CLIENT and MB_SERVER, Entry ID 102020340, V6 (Feb 2021). https://cache.industry.siemens.com/dl/files/340/102020340/att_118119/v6/net_modbus_tcp_s7-1500_s7-1200_en.pdf
  2. Siemens TIA Portal Online Docs, MB_SERVER instruction. https://docs.tia.siemens.cloud/r/simatic_s7_1200_manual_collection_eses_20/communication-processor-and-modbus-tcp/modbus-communication/modbus-tcp/modbus-tcp-instructions/mb_server-communicate-using-profinet-as-modbus-tcp-server-instruction
  3. Siemens, SIMATIC S7-1500 Communication Function Manual (covers ET 200SP CPU). http://public.eandm.com/Public_Docs/s71500_communication_function_manual_en-US_en-US.pdf
  4. Siemens Industry Online Support, SIMATIC Modbus/TCP communication using CP 343-1 and CP 443-1 — Programming Manual, Entry ID 103447617. https://cache.industry.siemens.com/dl/files/617/103447617/att_106971/v1/simatic_modbus_tcp_cp_en-US_en-US.pdf
  5. Siemens Industry Online Support FAQ "Which technical data applies for the SIMATIC Modbus/TCP software for CP 343-1 / CP 443-1?", Entry ID 104946406. https://www.industry-mobile-support.siemens-info.com/en/article/detail/104946406
  6. Siemens Industry Online Support, Redundant Modbus/TCP communication via CP 443-1 in S7-400H systems, Entry ID 109739212. https://cache.industry.siemens.com/dl/files/212/109739212/att_887886/v1/SIMATIC_modbus_tcp_cp_red_e_en-US.pdf
  7. Siemens Industry Online Support, SIMATIC MODBUS (TCP) PN CPU Library — Programming and Operating Manual 06/2014, Entry ID 75330636. https://support.industry.siemens.com/cs/attachments/75330636/ModbusTCPPNCPUen.pdf
  8. DMC Inc., Using an S7-1200 PLC as a Modbus TCP Slave. https://www.dmcinfo.com/blog/27313/using-an-s7-1200-plc-as-a-modbus-tcp-slave/
  9. Siemens, SIMATIC S7-1200 System Manual (V4.x), "MB_SERVER" pages 736-742. https://www.manualslib.com/manual/1453610/Siemens-S7-1200.html?page=736
  10. lamaPLC, Simatic Modbus S7 error- and statuscodes. https://www.lamaplc.com/doku.php?id=simatic:errorcodes
  11. ScadaProtocols, How to Configure Modbus TCP on Siemens S7-1200 (TIA Portal Step-by-Step). https://scadaprotocols.com/modbus-tcp-siemens-s7-1200-tia-portal/
  12. Industrial Monitor Direct, Reading and Writing Memory Bits via Modbus TCP on S7-1200. https://industrialmonitordirect.com/blogs/knowledgebase/reading-and-writing-memory-bits-via-modbus-tcp-on-s7-1200
  13. PLCtalk forum "Siemens S7-1200 modbus understanding". https://www.plctalk.net/forums/threads/siemens-s7-1200-modbus-understanding.104119/
  14. Siemens SIMATIC S7 Manual, "Function block MODBUSCP — Functionality" (ManualsLib p29). https://www.manualslib.com/manual/1580661/Siemens-Simatic-S7.html?page=29
  15. Chipkin, How Real (Floating Point) and 32-bit Data is Encoded in Modbus. https://store.chipkin.com/articles/how-real-floating-point-and-32-bit-data-is-encoded-in-modbus-rtu-messages
  16. Siemens Industry Online Support forum, MODBUS DATA conversion in S7-1200 CPU, Entry ID 97287. https://support.industry.siemens.com/forum/WW/en/posts/modbus-data-converson-in-s7-1200-cpu/97287
  17. Industrial Monitor Direct, Siemens S7-1500 MB_SERVER Modbus TCP Configuration Guide. https://industrialmonitordirect.com/de/blogs/knowledgebase/siemens-s7-1500-mb-server-modbus-tcp-configuration-guide
  18. Siemens TIA Portal, Data types in SIMATIC S7-1200/1500 — String/WString header layout (system manual, "Elementary Data Types").
  19. Kepware / PTC, Siemens TCP/IP Ethernet Driver Help, "Byte / Word Order" tag property. https://www.opcturkey.com/uploads/siemens-tcp-ip-ethernet-manual.pdf
  20. Siemens SiePortal forum, Transfer float out of words, Entry ID 187811. https://sieportal.siemens.com/en-ww/support/forum/posts/transfer-float-out-of-words/187811 (operator-reported "S7 swaps float" claim — traced to remote-device issue; unconfirmed.)
  21. Siemens SiePortal forum, S7-1200 communication with Modbus TCP, Entry ID 133086. https://support.industry.siemens.com/forum/WW/en/posts/s7-1200-communication-with-modbus-tcp/133086
  22. Siemens SiePortal forum, S7-1500 MB Server Holding Register Max Word, Entry ID 224636. https://support.industry.siemens.com/forum/WW/en/posts/s7-1500-mb-server-holding-register-max-word/224636
  23. Siemens, SIMATIC S7-1500 Technical Specifications — CPU-specific DB size limits in each CPU manual's "Memory" table.
  24. Siemens TIA Portal Online Docs, Error messages (S7-1200, S7-1500) — Modbus instructions. https://docs.tia.siemens.cloud/r/en-us/v20/modbus-rtu-s7-1200-s7-1500/error-messages-s7-1200-s7-1500
  25. Industrial Monitor Direct, Fix Siemens S7-1500 MB_Client UnitID Error 80C8. https://industrialmonitordirect.com/blogs/knowledgebase/troubleshooting-mb-client-on-s7-1500-cpu-1515sp-modbus-tcp
  26. Siemens SiePortal forum, How many TCP connections can the S7-1200 make?, Entry ID 275570. https://support.industry.siemens.com/forum/WW/en/posts/how-many-tcp-connections-can-the-s7-1200-make/275570
  27. Siemens SiePortal forum, Simultaneous connections of Modbus TCP, Entry ID 189626. https://support.industry.siemens.com/forum/ww/en/posts/simultaneous-connections-of-modbus-tcp/189626
  28. Siemens SiePortal forum, How many Modbus TCP IP clients can read simultaneously from S7-1517, Entry ID 261569. https://support.industry.siemens.com/forum/WW/en/posts/how-many-modbus-tcp-ip-client-can-read-simultaneously-in-s7-1517/261569
  29. Industrial Monitor Direct, Troubleshooting Intermittent Modbus TCP Connections on S7-1500 PLC. https://industrialmonitordirect.com/blogs/knowledgebase/troubleshooting-intermittent-modbus-tcp-connections-on-s7-1500-plc
  30. PLCtalk forum "S7-1500 modbus tcp speed?". https://www.plctalk.net/forums/threads/s7-1500-modbus-tcp-speed.114046/
  31. Siemens SiePortal forum, MB_Unit_ID parameter in Modbus TCP, Entry ID 156635. https://support.industry.siemens.com/forum/WW/en/posts/mb-unit-id-parameter-in-modbus-tcp/156635