Troubleshooting MODB4 V1.0 FC4 Input Register Read on S7-300
The Siemens S7-300 Modbus TCP library ships with multiple function blocks (FBs) for Modbus client/server communication. On older library releases, the legacy block MODB4 (V1.0, dated 2003) is often found in field installations where engineers need to read Modbus input registers (function code 04, 3xxxx area) from a redundant S7-300 station. A common commissioning defect is that the block appears to communicate correctly—requests and responses are visible in the instance DB—but the read data is never written to the configured destination data block, and no error is reported. This article documents the root cause, the exact parameter mapping, the affected library versions, and the validated fixes, including a memory-saving strategy if the newer MODBUSCP block is too large for the available work memory.
1. Problem Summary
An S7-300 station configured for software-based warm-standby redundancy is communicating with Modbus TCP slaves over a CP343-1 Ethernet CP. The application FB MODB4 (Modbus TCP library, V1.0) is used to read input registers (FC04, 3xxxx area). The block is supposed to read N input registers from a remote device and copy the payload into one of the parameterized data blocks (DB_1 through DB_8).
Observed behaviour:
- With
INPUT_HOLDING = FALSE(FC03 holding registers, 4xxxx area),MODB4reads correctly and writes the payload to the configured destination DB. - With
INPUT_HOLDING = TRUE(FC04 input registers, 3xxxx area), the request PDU is correctly formed and sent, the response PDU is correctly parsed and is visible in the receive buffer of the instance DB, but the payload is not written to the DB configured at the initialization call. - No status/error code is returned. The instance DB's status word is
16#0000, indicating success. - In some cases, payload bytes are written into a DB that is unrelated to the parameters passed to the
MODB4call, silently overwriting application data.
16#0000 from MODB4 V1.0 does not guarantee that data was written to the correct destination DB. The block performs no parameter validation at runtime; it silently uses whatever destination DB matches the internal area index derived from INPUT_HOLDING.
2. Root Cause Analysis
The defect is a parameter-area mismatch, not a protocol or wiring fault. The MODB4 V1.0 block exposes five destination data block pointers (DB_1 through DB_5) at the FB interface. Internally, however, the block maintains a second set of three destination areas (DB_6 through DB_8) used exclusively for the input-register read path (FC04).
Internally the block implements roughly the following dispatch logic at run time:
IF INPUT_HOLDING = FALSE THEN // FC03 holding registers (4xxxx)
DestArea := 1..5; // Use DB_1 .. DB_5 parameter inputs
ELSE // FC04 input registers (3xxxx)
DestArea := 6..8; // Use DB_6 .. DB_8 parameter inputs
END_IF;
The user documentation (English/German) explicitly states that for holding registers the user must parameterize DB_1 through DB_5, and for input registers the user must parameterize DB_6 through DB_8. The INPUT_HOLDING boolean is not a "swap the same five areas" flag—it is a selector between two disjoint sets of five vs. three destination areas.
When the engineer sets INPUT_HOLDING = TRUE while leaving DB_1 through DB_5 configured (as is reasonable when one expects the same five areas to be used), the block takes the initialized values of DB_6 through DB_8 instead. In an OB100 warm-restart those background areas are typically 0, so the block computes a NULL destination pointer and the write step becomes a no-op. In some situations the pointer happens to alias an unrelated DB number that was loaded earlier in the same scan, and the payload is written there—producing the mysterious "data appearing in a DB that has nothing to do with the call" symptom.
The fact that the request/response are observed in the instance DB is consistent: the protocol portion of MODB4 runs to completion. The failure is in the post-processing copy step that follows the FC04 read.
3. Affected Versions, Blocks and Licences
| Block | Version | Year | FC04 Support | Memory Footprint | Licence Model |
|---|---|---|---|---|---|
| MODB4 (internal helper) | V1.0 / V1.2 | 2003 / 2008 | Yes (via DB_6..DB_8) | Small | 1 licence per S7-CPU |
| MODB4H (wrapper, Modbus Red) | V1.0 | 2008 | Yes | Small + helper | 1 licence per redundant pair |
| MODBUSCP (current) | V1.x+ | 2010+ | Yes | Larger | Licence check inside block |
Library delivery media for the legacy product is the CD S7Open Modbus Redundant V1.0 (part of the order number 6GK1 754-1PX00-0AA0 and the related "Modbus/TCP – Redundante Anbindung" package). The library contains both the user-callable FB MODB4H and the internal helper MODB4. Engineers who skip the wrapper and call the helper directly (as in the case described here) are using a block that is not intended as a stand-alone API.
MODB4 V1.0 enforces a 1-CPU = 1-licence model and is not legal to use in a redundant S7-300 pair without two licences. The wrapper MODB4H from the Modbus Red product is licensed once per redundant pair. Always verify the licence situation before commissioning a redundant system.
For full reference see the Siemens support pages for "S7-300 Modbus/TCP – Redundante Anbindung" and the manual PDF shipped with the product, and the Modbus organization specification for the function-code semantics:
- Modbus Protocol Reference (modbus-tools.com)
- Modbus FC04 – Read Input Registers message structure
- Modbus Interface – FC04 reference (Beijer Electronics documentation)
4. Step-by-Step Fix
- Open the OB100 (warm-restart) and the cyclic OB1 call of
MODB4in STEP 7 V5.5 (or compatible). - At the initialization call (typically in OB100), do not use
DB_1whenINPUT_HOLDINGwill ever be set toTRUE. Configure a real destination DB number for theDB_6input of the FB (and forDB_7,DB_8if those input areas are also read). - Confirm the destination DB exists, is large enough for the maximum read length, and is not used elsewhere as a "shared work" DB.
MODB4will write raw 16-bit register words in big-endian word order starting at byte 0 of the destination DB. - Save, recompile, and download the project to the S7-300 station. Perform a warm restart to force OB100 to run.
- Force
INPUT_HOLDING = TRUEon the FB call in OB1 and observe:- The first word of the receive buffer in the instance DB should contain the slave's response function code (e.g.
16#04). - The destination DB should now contain the same payload visible in the receive buffer.
- Status word of the instance DB should still return
16#0000on success, or a Modbus exception code (16#84for illegal function,16#83for illegal data value, etc.) on failure.
- The first word of the receive buffer in the instance DB should contain the slave's response function code (e.g.
5. Verification Procedure
Once the destination DB has been re-pointed, run the following cross-checks before signing off the FAT/SAT:
-
Wire-level capture. Mirror the CP343-1 port with Wireshark and confirm that the request PDU carries function code
0x04and a starting address that matches the slave's 3xxxx map. Confirm the response PDU matches the byte count expected for the requested register count. -
Instance DB cross-check. Open the instance DB online and read the receive buffer (typically the
RECEIVEorBUFFERfield) and theSTATUSword. Both should be non-zero and consistent with the slave reply. -
Destination DB cross-check. Open the configured destination DB (the one now assigned to
DB_6) and verify the data words match the response payload. Pay attention to byte order: Modbus is big-endian, S7-300 words are big-endian on the wire but tool display may be little-endian depending on view. -
Failure-mode sweep. Disconnect the slave and verify that
MODB4reports a connection/timeout status (typically a non-zeroSTATUSfrom the underlying AG_SEND/AG_RECV path, e.g.16#8181,16#8183,16#8185,16#8192). The block must not report16#0000in the absence of a real read.
6. Memory Optimisation When Migrating to MODBUSCP
If the engineering team is forced to migrate to the newer MODBUSCP library because of additional function codes or redundant-pair licensing, and the S7-300 CPU is short on work memory, the following optimisation is supported:
The MODBUSCP block internally contains two large sub-FBs:
-
MB_CP_SRV– implements the Modbus server (slave) side. -
MB_CP_CLI– implements the Modbus client (master) side.
Each sub-FB is several KB of MC7 code. If the S7-300 is acting purely as a Modbus client (only reads/writes slaves), the MB_CP_SRV sub-FB can be physically deleted from the S7 program to reclaim work memory. Conversely, if the S7-300 is acting purely as a Modbus server, the MB_CP_CLI sub-FB can be deleted. Always keep the corresponding type/instance DB consistent and re-run the link/check in STEP 7 to confirm no unresolved references remain.
.s7p/download is on site, the deletion cannot be performed. Plan the optimisation during the design phase, not during on-site commissioning.
A typical memory-recovery profile on a CPU 315-2 PN/DP:
| Configuration | Work-memory used by MODBUSCP | Notes |
|---|---|---|
| Full library (CLI + SRV) | ~14 KB MC7 | Default |
| Client-only (MB_CP_SRV deleted) | ~9 KB MC7 | Saves ~5 KB |
| Server-only (MB_CP_CLI deleted) | ~9 KB MC7 | Saves ~5 KB |
If the legacy MODB4/MODB4H blocks are retained and they fit in work memory, no migration is strictly required—the fix in section 4 is sufficient. MODB4 V1.0 is small (roughly 2–3 KB MC7) and remains a viable choice for installations where memory is at a premium and the FC03/FC04/FC06/FC16 surface area is sufficient.
7. Redundancy Considerations
The reported installation uses a software warm-standby redundancy scheme. In such a configuration the communication FBs are usually placed outside the redundancy wrapper, i.e. they execute on the active CPU only and are not synchronised. This is the correct placement for MODB4/MODB4H and for MODBUSCP when the S7-300 is the client.
On a role swap (active ↔ standby) the new active CPU re-runs OB100 and re-initialises the FB, which reopens the TCP connection to the slaves. To prevent stale connections on the old active CPU, terminate the CP connection cleanly in the redundancy-fail OB (typically OB101 / OB102) before handing over. Failure to do so may cause the slaves to see ghost connections and the new active CPU to receive 16#8183 (connection already established) errors until the old socket times out.
8. Modbus Function-Code Mapping Reference
The following table summarises the function codes most commonly needed in S7-300 Modbus TCP integrations and the corresponding destination area mapping for MODB4 V1.0:
| Modbus Function | Mnemonic | Modbus Address | Direction | MODB4 Parameter | Destination Areas |
|---|---|---|---|---|---|
| FC01 | Read Coils | 0xxxx | Read | — (not in MODB4 V1.0 base set) | — |
| FC02 | Read Discrete Inputs | 1xxxx | Read | — (not in MODB4 V1.0 base set) | — |
| FC03 | Read Holding Registers | 4xxxx | Read | INPUT_HOLDING = FALSE | DB_1 .. DB_5 |
| FC04 | Read Input Registers | 3xxxx | Read | INPUT_HOLDING = TRUE | DB_6 .. DB_8 |
| FC05 | Write Single Coil | 0xxxx | Write | — (not in MODB4 V1.0 base set) | — |
| FC06 | Write Single Register | 4xxxx | Write | INPUT_HOLDING = FALSE, WRITE = TRUE | DB_1 .. DB_5 (source data) |
| FC15 | Write Multiple Coils | 0xxxx | Write | — (not in MODB4 V1.0 base set) | — |
| FC16 | Write Multiple Registers | 4xxxx | Write | INPUT_HOLDING = FALSE, WRITE = TRUE | DB_1 .. DB_5 (source data) |
For the FC01, FC02, FC05 and FC15 bit-oriented function codes, the legacy MODB4 V1.0 block does not provide a direct user interface. Migrate to MODBUSCP if those are required.
9. Common Error Codes and Their Meanings
| Status (hex) | Source | Meaning | Recommended Action |
|---|---|---|---|
| 16#0000 | MODB4 | Idle / no active job | None |
| 16#0001 | MODB4 | Job dispatched, awaiting reply | Wait one cycle |
| 16#0002 | MODB4 | Job completed, data available in instance DB | Consume from dest DB |
| 16#7000 | MODB4 | FB not enabled (EN = 0) | Check enable logic |
| 16#8181 | AG_SEND/AG_RECV | Connection not established | Check CP config, IP, TCP port 502 |
| 16#8183 | AG_SEND/AG_RECV | Connection already exists / resource busy | Check for duplicate CP connection |
| 16#8185 | AG_SEND/AG_RECV | Send buffer length error | Check BUFFER / LEN parameters |
| 16#8192 | AG_SEND/AG_RECV | Connection terminated by remote | Check slave, network, firewall |
| 16#83 | Modbus exception | Illegal data value (slave rejects) | Verify address / count, slave register map |
| 16#84 | Modbus exception | Illegal function (slave does not support FC04) | Check slave capability |
| 16#85 | Modbus exception | Acknowledge of long job (write FC16) | Informational, wait for completion |
| 16#86 | Modbus exception | Slave device failure | Check slave diagnostics |
10. Pre-Commissioning Checklist
- Verify the CP343-1 has a configured Modbus TCP connection with the correct remote IP, remote port (default 502), and connection ID matching the AG_SEND/AG_RECV call inside
MODB4. - Verify the slave's 3xxxx register map is published by the slave vendor and that the requested starting address and register count are within bounds.
- Create destination DBs for
DB_1throughDB_5and forDB_6throughDB_8at the correct sizes. The destination DB length must be at least 2 × register-count bytes (one 16-bit register = 2 bytes). - Set
INPUT_HOLDINGconsistently across all calls. If the application reads both holding and input registers, instantiateMODB4twice (or use a wrapper that dispatches) and parameterize one for each path. - Capture a Wireshark trace of the first 30 s of operation to confirm the FC04 request is actually leaving the CP and the FC04 response is arriving.
- Confirm the licence situation: one
MODB4licence per S7-CPU, or oneMODB4Hlicence per redundant pair if using the Modbus Red product.
11. Best-Practice Notes
-
Always call the wrapper, not the internal helper. When using the Modbus Red product, call
MODB4Hin OB1, not the internalMODB4FB. The wrapper handles the redundancy-specific initialisation and the licence check. -
Validate destination DBs in OB100. The destination DBs for
DB_1..DB_5andDB_6..DB_8should be opened and sized before the first call ofMODB4. A NULL pointer at FB input defaults to a "do nothing" path, which produces the silent-success symptom. -
Decouple the read rate from the cycle time. Run
MODB4on a 100–500 ms clock generator, not on every OB1 scan, to avoid saturating the CP and starving the rest of the program. -
Reserve a dedicated instance DB. The
MODB4instance DB is the canonical place to find the receive buffer, the last request, the last response, and the status word. Treat it as a diagnostic DB and include it in the HMI diagnostic screen for the line. -
Document the area map. In the project documentation, list which destination DB corresponds to which Modbus address range. Future engineers will assume
DB_1is "the first DB" and waste hours rediscovering the FC04 path.
12. When to Migrate to MODBUSCP
Migrate from MODB4/MODB4H to MODBUSCP if any of the following is true:
- Bit-oriented Modbus function codes (FC01, FC02, FC05, FC15) are required.
- Multiple concurrent slave connections are required (the legacy block is effectively single-connection per call instance).
- The application must support FC23 (read/write multiple registers) or vendor-specific function codes.
- Long-term vendor support and firmware maintenance is required;
MODB4V1.0 is a 2003-vintage block with no further updates. - The redundant pair is being replaced by a CPU 31x PN/DP generation that requires the current library API.
If none of the above apply and work memory is tight, the documented fix in section 4 is sufficient and MODB4/MODB4H remains a valid, licence-compliant solution for the remainder of the installation's life.
Why does MODB4 V1.0 read input registers (FC04) without writing to my destination DB?
The block writes the FC04 payload to DB_6 through DB_8, not to DB_1 through DB_5. If those high-numbered DB inputs are left at zero, the write is a no-op even though the request and response are visible in the instance DB. Configure real DB numbers at DB_6 (and DB_7, DB_8 as needed) in the OB100 initialization call.
Is INPUT_HOLDING a single-flag selector or two flags (one per area)?
It is a single boolean. INPUT_HOLDING = FALSE selects FC03 (holding registers, 4xxxx) and uses DB_1..DB_5. INPUT_HOLDING = TRUE selects FC04 (input registers, 3xxxx) and uses DB_6..DB_8. The five DB inputs at the FB are not re-used between the two modes.
Why does the status word stay at 16#0000 even when the data is wrong?
MODB4 V1.0 performs no parameter validation at runtime. The status word reflects only the protocol-layer state (idle, dispatched, completed, error from AG_SEND/AG_RECV, Modbus exception). A null destination DB pointer is treated as a successful no-op, so 16#0000 does not confirm correct data placement.
Can I keep using MODB4 V1.0 in a redundant S7-300 system?
Yes, but you need one licence per S7-CPU when using the internal MODB4 directly. If you use the Modbus Redundant product, call the wrapper MODB4H V1.0 instead and you need one licence per redundant pair. Always verify your licence certificate before commissioning.
How do I reduce the work memory footprint of MODBUSCP?
Delete the sub-FB you do not need: MB_CP_SRV when the S7-300 is Modbus client only, or MB_CP_CLI when it is server only. Each deletion reclaims roughly 5 KB of MC7 code. Re-link and download to verify no unresolved symbols remain.
What Modbus exception codes will MODB4 surface if the slave rejects the read?
MODB4 returns the standard Modbus exception codes in the high byte of the status word: 16#81 (illegal function), 16#82 (illegal data address), 16#83 (illegal data value), 16#84 (server device failure). A successful read returns 16#0002; an in-flight read returns 16#0001; idle returns 16#0000.