Reading Multiple Modbus TCP/IP Areas with MODBUS_PN on S7-300

David Krause18 min read
ModbusSiemensTutorial / How-to
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

The CPU 315-2 PN/DP integrates a PROFINET interface but does not include a native Modbus TCP/IP client or server in firmware. To poll third-party Modbus TCP servers from the S7-300, Siemens distributes the MODBUS_PN library as a free block library under support article ID 62830463 and as the example project MODBUS_PN_CPU_EXAMPLE. This library implements a Modbus TCP/IP client on the PROFINET interface, framing MBAP requests per the Modbus Application Protocol Specification v1.1b3 (modbus.org) over the IANA-assigned TCP port 502. The shipped example polls a single data area. This reference documents how to scale the example to read many Modbus data areas on one TCP connection, using the JobClient FB and the Job List FB included in the additional block library.

1. Overview: Modbus TCP/IP on the S7-300 PN/DP Generation

The CPU 315-2 PN/DP (typical order numbers 6ES7315-2EH14-0AB0 and related variants) provides a PROFINET interface capable of standard TCP/IP socket communication. Modbus TCP/IP, however, is not part of the S7-300 system software. Applications that need Modbus TCP must install a user-level protocol stack; the canonical Siemens solution for the S7-300 PN/DP generation is the MODBUS_PN block library.

The MODBUS_PN library exposes a small set of FBs that:

  • Open a passive or active TCP connection to the remote Modbus TCP server on port 502.
  • Build the 7-byte MBAP header (Transaction ID, Protocol ID = 0x0000, Length, Unit ID) and prepend it to the Modbus function code and payload.
  • Parse the MBAP response and copy the data into a user-supplied data buffer.
  • Set status bits DONE_NDR (success) and ERROR (failure) plus an ERROR_CODE.

The user drives the FB by populating a CONTROL_DAT structure (the library UDT) with the request parameters and setting a rising edge on ENQ_ENR. The FB clears ENQ_ENR, sends the request, and updates the status. This same pattern can be extended to many data areas by re-using one MODBUS_PN instance with a job sequencer.

2. Prerequisites, Library Installation, and CPU Resources

Before commissioning multi-area Modbus TCP/IP reads on a CPU 315-2 PN/DP, confirm the following prerequisites.

  1. STEP 7 V5.5 or V5.6 installed with the S7-300 family support package.
  2. MODBUS_PN library downloaded from the Siemens support article ID 62830463. The library ships as a self-extracting archive that installs the FBs, UDTs, and an example project.
  3. CPU PROFINET interface configured with a static IP address, subnet mask, and (if needed) router address in NetPro or in the CPU properties.
  4. Open TCP connection resources. The CPU 315-2 PN/DP firmware limits the number of simultaneous TCP connections on the PROFINET interface; consult the CPU 31xC and CPU 31x Manual for the precise number supported by your firmware version.
  5. Network reachability to the Modbus TCP server. Verify routing, switch VLAN configuration, and firewall rules. Modbus TCP/IP uses IANA-assigned TCP port 502 (see IANA Service Name and Transport Protocol Port Number Registry).
  6. Modbus server documentation showing the data map: which Modbus address ranges map to coils, discrete inputs, holding registers, and input registers.
Each MODBUS_PN FB instance that opens a TCP socket consumes one connection resource from the CPU. Plan connection allocation carefully when polling multiple servers or running PROFINET I/O simultaneously.

3. MODBUS_PN Library Architecture

The MODBUS_PN library contains five FBs and one UDT, plus optional wrappers for cyclic polling.

Block Type Purpose
MODBUS_PN FB Core FB that opens the TCP socket, frames MBAP requests, parses MBAP responses, and updates the CONTROL_DAT status fields.
JobClient FB Wrapper FB that submits a single job to MODBUS_PN, sets ENQ_ENR, waits for DONE_NDR or ERROR, and clears the trigger. Use in OB1 for sequential multi-area reads.
Job List FB Sequencer FB that cycles through an array of pre-defined jobs at the user-defined rate. Recommended for production multi-area polling.
CONTROL_DAT UDT User-Defined Type holding request parameters (UNIT, DATA_TYPE, START_ADDRESS, LENGTH, WRITE_READ, ENQ_ENR) and response status (DONE_NDR, ERROR, ERROR_CODE, data buffer).
DATA_BLK DB / array Data buffer where the FB places read payloads or from which it reads write payloads.
MODBUS_PN_DB Instance DB Working storage of the MODBUS_PN FB.

One instance of MODBUS_PN handles one TCP connection to one server. Multiple jobs targeting different Modbus data areas on the same server are submitted through JobClient calls or scheduled by the Job List FB. To connect to additional servers, instantiate additional MODBUS_PN FBs, each bound to a different remote IP.

4. CONTROL_DAT UDT Field Reference

The CONTROL_DAT UDT is the master parameter structure that the user populates before triggering a job and that the FB updates on completion. The fields below are typical for the released library; always open the UDT inside STEP 7 to verify offsets in the installed version.

Symbolic name Address (rel.) Type Direction Meaning
UNIT DBW 0 INT User → FB Modbus unit identifier (1-247). In Modbus TCP, this is normally 1 or 0xFF and may be ignored by the server.
DATA_TYPE DBW 2 INT User → FB Modbus area selector: 1 = Coils, 2 = Discrete Inputs, 3 = Holding Registers, 4 = Input Registers.
START_ADDRESS DBW 4 INT User → FB Zero-based Modbus address of the first coil or register.
LENGTH DBW 6 INT User → FB Number of elements to read or write. Bounded by the FC's PDU size limit.
WRITE_READ DBX 8.0 BOOL User → FB 0 = read request, 1 = write request.
ENQ_ENR DBX 8.1 BOOL User → FB / FB → User Rising edge triggers a new Modbus transaction. Cleared by the FB when the job starts.
DONE_NDR DBX 8.2 BOOL FB → User Set when the job completes successfully. Reset when the next ENQ_ENR rising edge is detected.
ERROR DBX 8.3 BOOL FB → User Set when the job fails (TCP error, Modbus exception, timeout).
ERROR_CODE DBW 10 INT FB → User Modbus exception code (1-13) or library-specific code.
RD_WR_DONE_NDR_ERR DBX 12.0 BOOL FB → User Composite status; set on every job end (success or failure).
data buffer DBW 14+ BYTE/INT/WORD User ↔ FB Read payloads land here for reads; the FB reads write payloads from here.
The zero-based START_ADDRESS in CONTROL_DAT is offset by 40001, 30001, 10001, or 1 depending on the Modbus address convention used by the server. For example, to read the holding register displayed as 40801, set START_ADDRESS = 800. For holding register 40001, set START_ADDRESS = 0.

5. Single-Area Reading: The Base Example

The shipped MODBUS_PN_CPU_EXAMPLE project polls one data area. The example's parameter block, lifted from the library comments, is reproduced below:


// you can set the parameters for the next job here
// it is set in comments to give you the possibility to use the variable table
// for changes during test
L   1
T   "CONTROL_DAT".UNIT
L   3
T   "CONTROL_DAT".DATA_TYPE
L   800
T   "CONTROL_DAT".START_ADDRESS
L   5
T   "CONTROL_DAT".LENGTH
SET
=   "CONTROL_DAT".WRITE_READ
SET
=   "CONTROL_DAT".ENQ_ENR    //job trigger
BEU

This loads:

  • UNIT = 1 (Modbus unit identifier)
  • DATA_TYPE = 3 (Holding Registers)
  • START_ADDRESS = 800 (zero-based; corresponds to the server address 40801)
  • LENGTH = 5 (five registers)
  • WRITE_READ = 1 (interpreted as read in this example since the LENGTH is consistent with a holding-register read; the actual FC is FC3 because the LENGTH > 1)
  • ENQ_ENR = 1 (rising-edge triggers the FB to send the MBAP request)

The trigger block at the bottom of the example code maintains the trigger:


A   "CONTROL_DAT".ENQ_ENR
R   "CONTROL_DAT".ENQ_ENR    //reset trigger after job has started
O   "CONTROL_DAT".DONE_NDR
O   "CONTROL_DAT".ERROR
S   "CONTROL_DAT".ENQ_ENR

The first two lines detect the rising edge and clear it once the FB has accepted the job. The third and fourth lines re-trigger the same job once it has finished (DONE_NDR or ERROR), so the example continuously reads the same area. This pattern only works for two areas if you swap the parameters between triggers; for more than two areas, replace it with the JobClient FB or Job List FB pattern below.

6. Multi-Area Reading with the JobClient FB

If you want to read three or more data areas without using the Job List sequencer, drive the JobClient FB manually. JobClient is a thin wrapper that:

  1. Writes the parameters for the next job into CONTROL_DAT.
  2. Pulses ENQ_ENR.
  3. Waits until DONE_NDR or ERROR is true.
  4. Clears the trigger so the next call can submit a new job.

A practical implementation in OB1 (pseudo-STL, three-job pattern):


// Job 1: Holding Registers 800-804
CALL "JobClient"
     JOB_UNIT       := 1
     JOB_DATA_TYPE  := 3
     JOB_START_ADDR := 800
     JOB_LENGTH     := 5
     JOB_WRITE_READ := FALSE
     JOB_DB         := "DATA_BLK"
     CONTROL        := "CONTROL_DAT"
     DONE           := "stat_job1_done"
     ERR            := "stat_job1_err"

// 100 ms wait (IEC timer TP or S5 timer SE)

// Job 2: Holding Registers 1000-1009
CALL "JobClient"
     JOB_UNIT       := 1
     JOB_DATA_TYPE  := 3
     JOB_START_ADDR := 1000
     JOB_LENGTH     := 10
     JOB_WRITE_READ := FALSE
     JOB_DB         := "DATA_BLK"
     CONTROL        := "CONTROL_DAT"
     DONE           := "stat_job2_done"
     ERR            := "stat_job2_err"

// 100 ms wait

// Job 3: Input Registers 0-9
CALL "JobClient"
     JOB_UNIT       := 1
     JOB_DATA_TYPE  := 4
     JOB_START_ADDR := 0
     JOB_LENGTH     := 10
     JOB_WRITE_READ := FALSE
     JOB_DB         := "DATA_BLK"
     CONTROL        := "CONTROL_DAT"
     DONE           := "stat_job3_done"
     ERR            := "stat_job3_err"

Key properties of the JobClient pattern:

  • Only one job is in flight at a time, so requests serialize naturally on the single TCP connection.
  • The user no longer needs the manual ENQ_ENR rising-edge block from the base example; JobClient owns the trigger lifecycle. The four STL lines that manually pulse ENQ_ENR are redundant and can be removed when switching to JobClient.
  • The per-job DONE and ERR outputs can be wired to status words in the user program for HMI display or alarm generation.
  • The inter-job wait timer avoids hammering the server with back-to-back transactions; adjust to the desired cycle time.

7. Multi-Area Cycling with the Job List FB

For production deployments that poll many areas continuously, the Job List FB is the recommended pattern. Job List:

  • Holds a configurable array of job descriptors (UNIT, DATA_TYPE, START_ADDRESS, LENGTH, WRITE_READ, target DB, offset).
  • Cycles through the array each time the FB is called, submits the current job to the underlying MODBUS_PN instance, and increments to the next entry on completion.
  • Tracks an index pointer that can be reset to 0 to restart the cycle.

Typical setup procedure:

  1. Open the UDT for the Job List FB in STEP 7 and inspect the job table layout (a STRUCT array inside the instance DB in recent library versions).
  2. Declare an instance DB of the Job List FB.
  3. Pre-initialize the job table in the instance DB with the production list of addresses. The number of entries supported depends on the library version; typical releases support 8 to 32 entries, with larger counts available in extended blocks.
  4. Call the Job List FB in OB35 (cyclic interrupt, default 100 ms) or in OB1, and ensure MODBUS_PN is called in the same cycle.
  5. Monitor the internal index pointer and DONE/ERROR status to confirm cycling.

Because Job List submits jobs without user-side trigger management, it removes the burden of writing rising-edge logic in the user program and is the pattern Siemens publishes for "more than two areas." It also centralises the job definitions in a single data block, simplifying future maintenance when the Modbus server's address map changes.

8. Trigger State Machine and MBAP Frame Format

Internally, the MODBUS_PN FB and the wrapper FBs implement a small state machine that governs how ENQ_ENR, DONE_NDR, and ERROR interact.

State Trigger Exit condition
IDLE ENQ_ENR = 0 Rising edge on ENQ_ENR → ARMED
ARMED Parameters valid FB clears ENQ_ENR, opens TCP if necessary, sends MBAP request → WAIT_RESP
WAIT_RESP Waiting for MBAP reply Response received → COMPLETE, or timeout → ERROR
COMPLETE Done DONE_NDR set, data buffer filled, ENQ_ENR remains 0 until user re-triggers
ERROR Fault ERROR set, ERROR_CODE populated, ENQ_ENR remains 0 until user re-triggers

Rules for correct state transitions:

  • Never set ENQ_ENR while the previous job is still in WAIT_RESP. The FB will ignore the second edge.
  • Reset ENQ_ENR (or let JobClient do it) before submitting the next job.
  • DONE_NDR and ERROR are mutually exclusive within a single job. OR both as a "job complete" signal for sequencer logic.

Each MBAP frame sent by the FB follows the Modbus Application Protocol Specification v1.1b3 layout:

Field Length Value Description
Transaction ID 2 bytes Set by client, echoed by server Matches responses to requests when multiple transactions are in flight.
Protocol ID 2 bytes 0x0000 Always zero for Modbus.
Length 2 bytes Number of bytes following Includes Unit ID plus the PDU (function code and data).
Unit ID 1 byte From CONTROL_DAT.UNIT Modbus unit identifier; most Modbus TCP servers ignore this byte.
Function Code 1 byte Derived from DATA_TYPE and WRITE_READ 1, 2, 3, 4 for reads; 5, 6, 15, 16 for writes.
Data variable FC-specific payload Includes START_ADDRESS, LENGTH, and any register values for writes.

9. Modbus Function Code Mapping

The MODBUS_PN library maps DATA_TYPE and WRITE_READ to Modbus function codes per the Modbus Application Protocol Specification v1.1b3.

DATA_TYPE WRITE_READ = 0 (Read) WRITE_READ = 1 (Write) PDU unit
1 (Coils) FC1 Read Coils FC5 Write Single Coil (LENGTH=1) / FC15 Write Multiple Coils (LENGTH>1) bit
2 (Discrete Inputs) FC2 Read Discrete Inputs (read-only per Modbus spec) bit
3 (Holding Registers) FC3 Read Holding Registers FC6 Write Single Register (LENGTH=1) / FC16 Write Multiple Registers (LENGTH>1) 16-bit register
4 (Input Registers) FC4 Read Input Registers (read-only per Modbus spec) 16-bit register

The library typically caps LENGTH at 125 words for holding-register reads and 123 words for writes to stay within the 260-byte TCP PDU limit. Going above the cap sets ERROR with a library-specific ERROR_CODE. Per the Modbus specification, the absolute maximum LENGTH for FC3 is 125 and for FC16 is 123; for FC1 and FC2 the practical ceiling is 2000 bits, but the library may impose a smaller limit.

10. Endianness, Byte Order, and Data Buffer Handling

The MODBUS_PN library stores 16-bit register values in network byte order (big-endian) by default. The S7-300 CPU is little-endian. If a holding register read directly into an S7 WORD variable appears byte-swapped (for example, the server returns 0x1234 and the CPU shows 0x3412), apply a byte swap using TAW (swap bytes within an accumulator word) or the SWAP STL instruction. Some library versions handle this automatically; verify by reading a known reference word such as a vendor-specific identifier register.

For multi-word values (32-bit floats, 32-bit integers, 64-bit values), additional byte and word swapping may be needed. Common patterns:

  • 32-bit integer or float: read two consecutive holding registers, then combine with a swap if the server is big-endian and the CPU is little-endian.
  • 32-bit float (IEEE 754): use standard S7-300 libraries to convert DWORD to REAL after appropriate byte reordering.
  • Bit-packed status words: extract bits with masking operations; respect server bit ordering (Modbus specifies LSB-first within a byte).

For bit-level reads (FC1, FC2), each bit is packed into a byte of the response PDU. The library unpacks this into the data buffer as a sequence of BOOLs starting at the buffer offset. Verify the exact unpacking layout by reading a known coil pattern (e.g. the server's status word) before commissioning the production logic.

11. Connection Management, Poll Capacity, and Multi-Server Topology

A single MODBUS_PN FB instance connects to one server. To poll a second server, instantiate a second MODBUS_PN FB with its own instance DB, configured for the new remote IP address. For applications that need to poll many servers, allocate one MODBUS_PN instance per server.

Poll capacity per second is bounded by the per-area round-trip time. For a CPU 315-2 PN/DP on a 100 Mbit/s PROFINET segment with a typical Modbus TCP server, the per-area round-trip T is approximately 5-30 ms. The number of areas that can be polled per second across all MODBUS_PN instances is approximately:


N_areas_per_second = 1000 / T

With T = 25 ms, expect around 40 area polls per second across all instances. Batching multiple registers into a single LENGTH value (up to 125 registers per FC3 request) reduces per-area overhead and improves effective throughput.

For deterministic cycle times, call MODBUS_PN from a cyclic interrupt OB (OB35 default 100 ms). This decouples the Modbus cycle from OB1 scan time and avoids blocking OB1 with long TCP transactions.

Topology considerations:

  • Segment Modbus TCP traffic to a dedicated VLAN to minimise broadcast noise and isolate the CPU from unrelated traffic.
  • Place the Modbus TCP server and the CPU on the same subnet when possible to reduce router hops and round-trip variance.
  • Configure PROFINET port properties to allow at least the number of TCP connections you plan to use, plus headroom for PROFINET I/O.

12. Error Handling, Diagnostics, and Security

Common faults observed in field deployments of MODBUS_PN on S7-300 CPUs:

Symptom Likely cause Remedy
ERROR = 1, ERROR_CODE = library-specific (no MBAP reply) TCP socket not opened; remote IP unreachable; firewall blocking port 502. Ping the server; verify routing; check that port 502 is open in any intermediate firewalls. Modbus TCP/IP uses IANA port 502.
ERROR = 1, ERROR_CODE = Modbus 02 ILLEGAL_DATA_ADDRESS START_ADDRESS is outside the server's address space. Verify the server's Modbus address map; lower START_ADDRESS to a known-valid range.
ERROR = 1, ERROR_CODE = Modbus 03 ILLEGAL_DATA_VALUE LENGTH too long, or LENGTH = 0. Bound LENGTH between 1 and the FC-specific maximum (125 for FC3, 123 for FC16, smaller for the library cap).
ERROR = 1, ERROR_CODE = Modbus 04 SLAVE_DEVICE_FAILURE Server-side error in the slave device. Check the slave's diagnostic registers or vendor manual.
ERROR = 1, ERROR_CODE = library-specific (TCP timeout) Server slow to respond; CPU scan time too long; OB1 priority too low. Move the MODBUS_PN call to OB35 or another cyclic OB with adequate priority; verify the server's response timeout is greater than the CPU cycle.
DONE_NDR = 1 but data buffer = 0 Server returned data but address map offsets user expected wrong; or word-swap issue (big-endian vs little-endian). Confirm with a Modbus scanner tool that the server's payload matches. Apply TAW or SWAP if needed.
Connection drops every few minutes Keep-alive not set; intermediate router kills idle TCP. Configure TCP keep-alive on the PROFINET interface, or schedule periodic low-cost reads (e.g. FC3 length 1) to maintain the socket.

When the CPU transitions to STOP after a MODBUS_PN call, examine the diagnostic buffer (PLC → Diagnostic/Setting → Module Information in STEP 7). Look for:

  • OB85 (peripheral fault) — check that MODBUS_PN is called periodically with consistent input parameters.
  • OB122 (I/O access error) — typically indicates a pointer error in the data buffer access; verify the DATA_BLK offset and length.
  • OB121 (programming error) — check that DATA_BLK is the correct DB and that LENGTH does not exceed the buffer size.
Modbus TCP/IP carries no authentication, no encryption, and no integrity protection. On a plant network, segment Modbus TCP traffic to a dedicated VLAN, restrict access to port 502 with firewall rules, and never expose Modbus TCP servers directly to the enterprise network or the internet.

13. Commissioning and Verification Checklist

To commission a multi-area Modbus TCP/IP reader on the CPU 315-2 PN/DP, follow this verification sequence:

  1. Confirm IP reachability. Ping the server's IP address and the CPU's PROFINET IP from a workstation on the same subnet. Both must respond.
  2. Verify port 502 is open. Use a Modbus scanner or TCP port-check tool against the server. Modbus TCP/IP listens on IANA port 502.
  3. Run the unmodified example. Load the MODBUS_PN_CPU_EXAMPLE project, set the server IP, and observe DONE_NDR going true. Confirm at least one register read returns the expected value.
  4. Add a second job. Modify the example to read a second area (for example, START_ADDRESS = 1000, LENGTH = 10). Use a Variable Table to flip the trigger and confirm both areas return correctly.
  5. Insert the JobClient FB. Replace the manual ENQ_ENR logic with JobClient calls in OB1. Verify DONE and ERR signals toggle as expected. Confirm that the manual rising-edge lines can be removed safely.
  6. Configure the Job List FB. Populate the job table with the production list of areas, attach the FB to a cyclic interrupt (OB35, 100 ms), and watch the index pointer cycle.
  7. Stress test. Run the cycle overnight with no writes from the CPU. Monitor DONE_NDR for stuck-at-0 (job never completes) and ERROR for transient faults. Capture the CPU diagnostic buffer at the end of the run for any unexplained STOP transitions.
  8. Document the map. Record the exact server IP, port, unit ID, list of polled areas, refresh intervals, and data buffer offsets for future maintenance. Archive the STEP 7 project to the project server.

Verification artifact: when DONE_NDR is observed true and the data buffer contains the expected values for every job in the cycle, the multi-area read is considered commissioned and ready for production.

Frequently Asked Questions

How do I read more than two Modbus TCP areas on a CPU 315-2 PN/DP?

Use the JobClient FB to sequence job submissions to a single MODBUS_PN instance, or load the additional block library from Siemens support article ID 62830463 and use the Job List FB to cycle through an array of pre-defined jobs automatically.

Can I delete the manual ENQ_ENR lines after switching to JobClient?

Yes. The JobClient FB owns the ENQ_ENR trigger lifecycle, so the four-line rising-edge block (A/R/O/O/S around ENQ_ENR) becomes redundant and can be removed when JobClient is used.

What DATA_TYPE value reads Holding Registers?

DATA_TYPE = 3 selects Holding Registers; the FB uses FC3 for reads and FC6 (LENGTH=1) or FC16 (LENGTH greater than 1) for writes. DATA_TYPE = 4 reads Input Registers with FC4.

What TCP port does Modbus TCP/IP use?

Modbus TCP/IP uses IANA-assigned TCP port 502. Open this port on any firewall between the CPU and the server, and verify it with a port scanner or Modbus test tool.

Why do I receive Modbus exception 02 ILLEGAL_DATA_ADDRESS?

Exception 02 means the server has no data at the requested START_ADDRESS. Verify the server's Modbus address map and confirm the zero-based START_ADDRESS in CONTROL_DAT matches the server's offset (subtract 40001 from 4xxxxx-style addresses, 30001 from 3xxxxx-style, and so on).

How do I poll multiple Modbus servers from one CPU?

Instantiate one MODBUS_PN FB per server, each with its own instance DB bound to a different remote IP address, then call each instance in OB1 or in a cyclic OB. Plan TCP connection resource usage against the CPU's per-firmware connection limit.

Where is the canonical Siemens documentation for the MODBUS_PN library?

The library and its documentation are published under Siemens support article ID 62830463. Refer to the article for installation instructions, release notes, and the exact block symbols of the installed library version.

Back to blog