Problem Overview
The MODBUSPN communication block on a Siemens SIMATIC S7-300 establishes the TCP connection successfully, transmits the request telegram, and the remote Modbus/TCP server returns a valid response. The server application confirms that the client is connected and that the response has been sent, yet the MODBUSPN block on the S7-300 never sets DONE_NDR, ERROR, or BUSY after the request. Wireshark captures on the network segment show the request leaving the CPU and the response arriving back at the CPU's MAC address, but the data never reaches the application's receive buffer.
This symptom is typical when:
- The
ENQ_ENRtrigger is driven as a level rather than a rising edge. - The DONE, ERROR, and STATUS outputs are not latched to a free memory area and are therefore missed because they remain valid for only one OB1 cycle.
- The connection ID is shared with another open communication service (TSEND/TRCV, BSEND/BRCV, AG_SEND/AG_RECV) and the underlying T-block pair collides.
- The internal
TSEND/TRCVinstance DB numbers assigned to MODBUSPN are overwritten by another block with the same FB number. - The
connect_startupparameter remains TRUE while the connection establishment sequence conflicts with the partner's startup behavior.
Reference the official Description of MODBUSCP (S7-300, S7-400) documentation for the Modbus/TCP-specific frame generation and validation behavior, which is also implemented in the legacy MODBUSPN block for older firmware.
Affected Hardware and Firmware
| Component | Order Number / Designation | Notes |
|---|---|---|
| CPU | 6ES7 318-3FL01-0AB0 | SIMATIC S7-300, CPU 319-3 PN/DP, integrated PN interface |
| Firmware | V3.2 | Final firmware release for the -0AB0 hardware version; supports MODBUSPN V2.x block library |
| Function Block | FB 105 (MODBUSPN) or FB 109 (MODBUSCP) depending on library version | Located in the "ModbusPN" or "MODBUSCP" standard library |
| STEP 7 Version | STEP 7 V5.5 + SP4 / HF7 or later | Required for MODBUSPN V2.x compatibility with the CPU 319-3 PN/DP |
| Remote Server | Third-party PC application (Modbus/TCP server, port 502) | Verified working with another S7-300 — eliminates server-side defect |
Block Interface and Internal T-Blocks
MODBUSPN encapsulates three internal communication primitives. Each primitive is bound to a specific instance DB whose FB number is allocated by the STEP 7 block container. The block properties dialog reveals the assigned numbers, and they must not collide with user-instantiated blocks.
| MODBUSPN Internal Call | Function | Typical Instance DB | Conflicts To Watch |
|---|---|---|---|
| TCON | Establish / manage the TCP connection | DB-WATCH assigned by MODBUSPN | Do not instantiate TCON elsewhere with the same ID |
| TSEND | Send the Modbus request telegram | DB assigned by MODBUSPN | Do not use a user FB with the same DB number for any other purpose |
| TRCV | Receive the Modbus response telegram | DB assigned by MODBUSPN | If another block uses the same instance DB, the receive context is corrupted |
Open the block properties of the MODBUSPN call in STEP 7 and inspect the FB numbers used for TSEND, TRCV, TCON, TDISCON. The values shown there must be exclusively used by MODBUSPN. If a TSEND instance has been copied from the example project and a separate user TSEND exists with the same number, the receive side will silently fail because TRCV is fed a corrupted instance.
Parameter Data Block Layout
The MODBUSPN block requires a parameter DB (often named PARAM_DAT or MODBUS_PARAM). The first relevant send/receive buffer pointers appear at DBW 130 for ID1_send_buffer and adjacent offsets for the receive buffer. Inspecting the online DB at this address is the most direct way to confirm whether the block is actually marshalling a request.
| Offset | Symbolic Name | Data Type | Meaning |
|---|---|---|---|
| DBW 130.0 | ID1_send_buffer | WORD / POINTER | Pointer/length to the Modbus request payload |
| DBW 132.0 | ID1_recv_buffer | WORD / POINTER | Pointer/length to the receive area in the user DB |
| DBW 134.0 | ID1_send_length | INT | Number of bytes in the request |
| DBW 136.0 | ID1_recv_length | INT | Number of bytes to be received |
| DBB 200 | connect_startup | BOOL | Set FALSE to suppress automatic connection establishment at startup |
| DBW 210 | RECV_TIMEOUT | TIME (ms) | Monitor time for the response — typical 500 ms |
| DBW 214 | CONN_TIMEOUT | TIME (ms) | Connection establishment timeout — typical 5000 ms |
Root Cause Analysis
The failure pattern — "request leaves, response arrives at the CPU, but MODBUSPN does not declare DONE/ERROR/BUSY" — is most often caused by a missed single-cycle status window combined with an incorrect ENQ_ENR trigger. The two failures reinforce each other and give the false impression that the receive path itself is broken, when in fact the call is either never executed or the result is being discarded.
ENQ_ENR must be a rising edge
The ENQ_ENR input on MODBUSPN starts exactly one Modbus transaction per positive edge. A continuous TRUE level does not start multiple telegrams — it starts one telegram and then sits in an undefined handshake state until it is reset. If the bit is never reset, the block never re-arms to accept a new request, and the second, third, and subsequent transactions never occur.
Status outputs are valid for one OB1 cycle only
DONE_NDR, ERROR, BUSY, and STATUS reflect the result of the most recent telegram. The block updates them at the end of the cycle in which the telegram finished, and they are cleared on the next call. Without latching to a separate flag word, a programmer watching the block in VAT will see no response because the status is overwritten between the time the trigger is set and the time the engineer inspects the value.
Connection ID collision
The connection ID parameter on MODBUSPN is the index used by the underlying TCON block to identify the TCP connection in the CPU's connection database. If any other communication block (BSEND, USEND, PUT/GET, AG_SEND) on the same PN interface uses the same ID, the TCP stack may route the incoming response to the wrong user instance, and MODBUSPN's TRCV will never see the data.
TSEND / TRCV instance DB collision
Because the receive and send instances are managed by MODBUSPN, copying the example project into an existing program sometimes overwrites a user block. The block compiles cleanly because the FB numbers are reused, but the runtime behavior is corrupted: the receive instance is initialized for a different connection and drops the response silently.
Step-by-Step Resolution
-
Confirm the trigger logic. Replace any direct assignment to
ENQ_ENRwith a strictly edge-driven sequence. Use the pattern shown in the ladder example below. -
Latch the status outputs. Add a S/R flip-flop or four separate Set coils that copy
DONE_NDR,ERROR,BUSY, andSTATUSinto free flags (e.g.MW 200,MW 202) and into four discreteMbits. Reset them only after the application has consumed the result. -
Unify the parameter DB. Verify the parameter DB offsets DBW 130 / DBW 132 contain valid pointers to the user data areas. Set
connect_startup = FALSEto suppress startup-time auto-establishment and prevent race conditions with the partner. -
Set correct timeouts. Use
RECV_TIMEOUT = T#500msandCONN_TIMEOUT = T#5sas the baseline. IncreaseRECV_TIMEOUTif the remote server is slow or networked over a WAN. -
Check the connection ID. Use
ID = 1for the first Modbus connection. Search the project for any other block that also usesID = 1and reassign the second block to a unique ID (2, 3, ...). - Verify the T-block instance numbers. Open MODBUSPN's block properties and note the FB numbers used internally. Search Program > Blocks for collisions.
-
Reset the partner connection cleanly. After all the above changes, power-cycle the PN interface or call
TDISCON+TCONto re-establish the connection. Wireshark should now show the request followed by a response consumed by the CPU. -
Run a known-good test telegram. Trigger
ENQ_ENRwith a one-shot rising edge. Within the next 2–3 OB1 cycles,DONE_NDRmust transition TRUE and the receive buffer must contain the expected Modbus/TCP PDU.
Ladder Logic Template
The snippet below assumes the parameter DB is DB100 ("CONTROL_DAT") and the user data area is DB200 ("MODBUS_DATA"). Adjust the symbolic names to match the project.
// --- Start one Modbus transaction on a rising edge of a start condition ---
A M 10.0 // Application start trigger (rising edge)
S "CONTROL_DAT".ENQ_ENR // Set ENQ_ENR for one cycle
// --- Reset ENQ_ENR immediately so it is a one-shot pulse ---
A "CONTROL_DAT".ENQ_ENR
R "CONTROL_DAT".ENQ_ENR
// --- Call MODBUSPN ---
CALL FB 105, "MODBUS_PN" // (or FB 109 for MODBUSCP)
ENQ_ENR := "CONTROL_DAT".ENQ_ENR
ID := 1
PARAM_DAT := "CONTROL_DAT"
DATA_DAT := "MODBUS_DATA"
DONE_NDR := "CONTROL_DAT".DONE_NDR
ERROR := "CONTROL_DAT".ERROR
STATUS := "CONTROL_DAT".STATUS
BUSY := "CONTROL_DAT".BUSY
// --- Latch the result for the application layer (visible > 1 cycle) ---
A "CONTROL_DAT".DONE_NDR
S M 20.0 // "Transaction OK" flag
A "CONTROL_DAT".ERROR
S M 20.1 // "Transaction failed" flag
A "CONTROL_DAT".DONE_NDR
O "CONTROL_DAT".ERROR
R M 20.0
R M 20.1
// --- Re-arm the next telegram AFTER the previous one has terminated ---
A M 20.0
O M 20.1
ON "CONTROL_DAT".BUSY // also re-arm if BUSY dropped without a result
S "CONTROL_DAT".ENQ_ENR
A "CONTROL_DAT".ENQ_ENR
R "CONTROL_DAT".ENQ_ENR
S / R pair on ENQ_ENR is not a typo — it generates a single-cycle positive edge for the next call. The ON "CONTROL_DAT".BUSY branch is a safety re-arm in case the status was missed by the application layer.Verification Procedure
- Open Monitor/Modify on the parameter DB and observe the send/receive buffer pointers increment as the request is sent.
- Watch
DONE_NDR,ERROR,BUSY, andSTATUSin VAT. With the latching logic above, the flag bits inMW 20must toggle on every successful transaction. - Capture the same network with Wireshark and confirm a complete Modbus/TCP exchange on TCP port 502. The transaction ID in the request and response must match, and the response's unit ID must equal the request's unit ID. Any deviation indicates a routing or proxy issue in the network path.
- Power-cycle the CPU and confirm that the connection re-establishes automatically (with
connect_startup = FALSE, the nextENQ_ENRtriggers the connection establishment). - Repeat the test under sustained load (one transaction every 200 ms for at least 10 minutes) to surface timeouts or instance-DB corruption that only appear under stress.
Status Code Reference
The STATUS word is a 16-bit value with a 16#8xxx family for Siemens-internal codes and 16#0001–16#000F for Modbus-standard exception codes. A few common values encountered in the "no data returned" scenario:
| STATUS (hex) | Meaning | Remediation |
|---|---|---|
| 16#0000 | No error, no transaction complete | ENQ_ENR not pulsed — check trigger logic |
| 16#7001 | Function triggered, BUSY | Wait for completion |
| 16#7002 | Function triggered, internal call active | Wait |
| 16#80C8 | Connection aborted by partner | Verify server application, check firewalls |
| 16#80C9 | Connection not yet established | Check IP routing and partner reachability |
| 16#80D0 / 80D1 | Send/receive timeout | Increase RECV_TIMEOUT, verify partner is responding |
| 16#80D2 | Receive buffer too small | Increase ID1_recv_length in the parameter DB |
| 16#80D4 | Modbus exception 0x0B (Gateway Target Failed) | Server cannot resolve the unit ID / slave address |
Common Configuration Mistakes
| Symptom | Likely Cause | Fix |
|---|---|---|
| Wireshark shows port 502 traffic but block never sets DONE | ENQ_ENR driven as a level; no rising edge | Add explicit S/R pair to make it a one-cycle pulse |
| DONE_NDR appears for one cycle, then disappears | Status not latched in the application | Save DONE_NDR, ERROR, STATUS to M-words or DB |
| Connection establishes but no request leaves the CPU | Send buffer pointer 0 or wrong length | Set DBW 130 / DBW 134 to a valid pointer/length pair |
| First transaction works, subsequent ones fail | ENQ_ENR not re-armed after DONE | Use DONE_NDR / ERROR to re-trigger ENQ_ENR |
| Connection drops randomly every few minutes | Partner is sending RST or FIN too early; RECV_TIMEOUT too aggressive | Raise RECV_TIMEOUT to 1–2 s, disable keep-alive on the partner |
| CPU reports "Resource shortage" SF LED | Instance DB collision on T-blocks | Re-import MODBUSPN blocks from the example project |
Advanced Diagnostics
When the basic checks above do not resolve the issue, escalate to the following diagnostics:
-
Online IDB of MODBUSPN — Open the instance DB in Monitor mode. The
REQ,DONE,NDR,ERROR, andSTATUSfields will show the live state of the underlying T-blocks. A constantly zeroREQconfirms that the user code never properly starts a telegram. - TCON diagnostics buffer — In STEP 7, open PLC > Diagnostics > Communication. Each active connection is listed with its local/remote port and state. The MODBUS connection must show Established. A Listening state means the partner has not connected back.
- CPU diagnostic buffer — PLC > Diagnostics > Buffer contains event codes for every connection abort, including the partner IP, the local port, and the reason. Look for entries referencing TCON / TSEND / TRCV.
- Port verification — Wireshark's Statistics > Conversations shows the actual port pair. Modbus/TCP must use port 502 on the server side. If the server is using an ephemeral port, the request will not be a valid Modbus/TCP PDU and MODBUSPN will reject it at the frame-check stage.
-
NTP / time synchronization — Not a Modbus issue, but a mismatched system clock can mislead a server that authenticates sessions. Verify with
SET_CLKor via the CPU's HMI time display.
Migration Path to MODBUSCP (FB 109)
For new installations, the MODBUSCP (FB 109) block supersedes MODBUSPN and provides additional diagnostics, explicit connect/disconnect modes, and a unified parameter structure. The migration is in-place: copy FB 109, DB 100 (instance), and DB 101 (parameter) from the example project, remap the user DB pointers, and retire the old MODBUSPN instance. The same edge-driven ENQ_ENR discipline applies.
Safety and Operational Notes
- Always validate the received Modbus data (function code, byte count, transaction ID) before using it in the process. A tampered or misrouted response can carry valid-looking but stale or wrong data.
- Disable the S7-300's PUT/GET server if it is not required. Leaving it open increases the attack surface on the same PN interface used by Modbus/TCP.
- Use a dedicated VLAN or firewall rule to isolate the Modbus/TCP traffic from the rest of the plant network. Modbus/TCP has no built-in authentication or encryption.
- After any firmware upgrade, re-test the full transaction sequence. Newer firmware sometimes tightens the validation of reserved bytes in the Modbus/TCP MBAP header and may reject servers that send non-standard padding.
Frequently Asked Questions
Why does MODBUSPN not show DONE or ERROR even though the server replies?
DONE_NDR, ERROR, and STATUS are valid for only one OB1 cycle. The output is updated at the end of the cycle in which the telegram completes and cleared on the next call. Latch them to separate flags or M-words to capture and analyze the result outside the single-cycle window.
How do I trigger a Modbus/TCP transaction on the MODBUSPN block?
Set the ENQ_ENR input as a positive edge, not a level. The canonical pattern is to Set ENQ_ENR on the application trigger and then immediately Reset it in the same cycle, so the block sees exactly one rising edge per transaction.
What are the recommended timeouts for RECV_TIMEOUT and CONN_TIMEOUT?
Use RECV_TIMEOUT = T#500ms and CONN_TIMEOUT = T#5s as the baseline for a LAN-attached server. Increase RECV_TIMEOUT to 1–2 s when the server is slow, virtualized, or behind a WAN. Values below 200 ms often cause false timeouts on a busy CPU.
Can the same connection ID be used by more than one communication block?
No. The connection ID is the index the PN interface uses to route incoming TCP segments to the correct user instance. Sharing an ID between MODBUSPN and another block (TSEND, BSEND, PUT/GET) will cause the receive data to be delivered to the wrong block and will appear as silent data loss on the Modbus side.
What is the difference between MODBUSPN and MODBUSCP?
Both blocks implement the same Modbus/TCP client behavior on S7-300/S7-400. MODBUSCP (FB 109) is the newer variant with extended diagnostics, explicit connect/disconnect control, and unified parameter structure. MODBUSPN is the legacy block retained for existing projects. New installations should use MODBUSCP.