S7-300 Modbus TCP: Resolving MODBUSPN Receive Data Failure

David Krause13 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

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_ENR trigger 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 / TRCV instance DB numbers assigned to MODBUSPN are overwritten by another block with the same FB number.
  • The connect_startup parameter 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
Compatibility note: For new projects, Siemens recommends MODBUSCP (FB 109) on S7-300/S7-400. MODBUSPN remains supported on legacy systems and provides equivalent Modbus/TCP client behavior.

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
If the receive buffer pointer contains 0 or an invalid address, the block reports no error but the data never lands in the user data area. Open the parameter DB online and verify the pointers with Monitor/Modify.

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

  1. Confirm the trigger logic. Replace any direct assignment to ENQ_ENR with a strictly edge-driven sequence. Use the pattern shown in the ladder example below.
  2. Latch the status outputs. Add a S/R flip-flop or four separate Set coils that copy DONE_NDR, ERROR, BUSY, and STATUS into free flags (e.g. MW 200, MW 202) and into four discrete M bits. Reset them only after the application has consumed the result.
  3. Unify the parameter DB. Verify the parameter DB offsets DBW 130 / DBW 132 contain valid pointers to the user data areas. Set connect_startup = FALSE to suppress startup-time auto-establishment and prevent race conditions with the partner.
  4. Set correct timeouts. Use RECV_TIMEOUT = T#500ms and CONN_TIMEOUT = T#5s as the baseline. Increase RECV_TIMEOUT if the remote server is slow or networked over a WAN.
  5. Check the connection ID. Use ID = 1 for the first Modbus connection. Search the project for any other block that also uses ID = 1 and reassign the second block to a unique ID (2, 3, ...).
  6. Verify the T-block instance numbers. Open MODBUSPN's block properties and note the FB numbers used internally. Search Program > Blocks for collisions.
  7. Reset the partner connection cleanly. After all the above changes, power-cycle the PN interface or call TDISCON + TCON to re-establish the connection. Wireshark should now show the request followed by a response consumed by the CPU.
  8. Run a known-good test telegram. Trigger ENQ_ENR with a one-shot rising edge. Within the next 2–3 OB1 cycles, DONE_NDR must 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
The trailing 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

  1. Open Monitor/Modify on the parameter DB and observe the send/receive buffer pointers increment as the request is sent.
  2. Watch DONE_NDR, ERROR, BUSY, and STATUS in VAT. With the latching logic above, the flag bits in MW 20 must toggle on every successful transaction.
  3. 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.
  4. Power-cycle the CPU and confirm that the connection re-establishes automatically (with connect_startup = FALSE, the next ENQ_ENR triggers the connection establishment).
  5. 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:

  1. Online IDB of MODBUSPN — Open the instance DB in Monitor mode. The REQ, DONE, NDR, ERROR, and STATUS fields will show the live state of the underlying T-blocks. A constantly zero REQ confirms that the user code never properly starts a telegram.
  2. 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.
  3. 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.
  4. 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.
  5. NTP / time synchronization — Not a Modbus issue, but a mismatched system clock can mislead a server that authenticates sessions. Verify with SET_CLK or 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.

Back to blog