Resolving S7-1200 MB_CLIENT Stale Data with Heartbeat Monitoring

David Krause26 min read
S7-1200SiemensTroubleshooting
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

1. Problem Summary

An S7-1200 CPU polls an Emerson Modbus TCP gateway through the MB_CLIENT instruction every second. When the Ethernet cable between the PLC and the gateway is pulled, the gateway powers down, or a managed switch port disables, the CPU keeps the last successfully received Modbus payload in the data block pointed to by MB_DATA_PTR. The KTP600 HMI reads that buffer and continues to display the pre-fault value. No LED, no alarm, no system diagnostic bubble — the network failure is invisible to the operator until somebody notices. On a power cycle the buffer is re-initialised to zero, so the HMI suddenly drops to a zero reading and a different alarm ("sensor lost") is raised. The end user cannot tell the difference between a legitimate zero process value and a cable break, and the HMI cannot flag the link as "stale" without an explicit watchdog. The PLC is online and reports no MB_CLIENT fault — the field engineer has no diagnostic to point at.

This article explains why MB_CLIENT behaves this way, maps the full set of STATUS codes that the instruction emits on a Modbus TCP cable fault, and provides a deterministic stale-data detection path: a server-side heartbeat register polled by the client, a sticky error latch in the PLC, and a banner on the HMI. The reference architecture is the field topology from the original incident report: S7-1200 CPU on 192.168.0.5, KTP600 Basic on 192.168.0.2, Emerson Modbus TCP gateway on 192.168.0.1, all on a single /24 subnet. The heartbeat is implemented in the gateway's user-mapped holding register 40099 and read by a second MB_CLIENT instance with a 5 s period.

2. Affected Hardware, Firmware, and TIA Portal Versions

Component Identified in the Issue Minimum Version That Supports the Solution Notes
CPU S7-1200 (e.g. CPU 1214C DC/DC/DC, CPU 1215C DC/DC/DC) Firmware V4.0 or higher (MB_CLIENT available since V4.0) Recommended: V4.4 or higher for TIA Portal V17 compatibility
HMI SIMATIC KTP600 Basic mono / color WinCC Basic V13 SP1 or higher; supports the symbolic pointer used by the watchdog Older KTP600 Basic panels without symbolic access must use a "DB number + byte offset" tag pointer
Gateway Emerson Modbus TCP/IP Gateway (Server role) Any Modbus TCP server that exposes a writable holding register (function 6) for the heartbeat Refer to the gateway's Modbus map PDF; register 40099 is the assumed location in this article
Engineering TIA Portal V13 SP1 minimum; V16 / V17 recommended The example code uses standard SCL compatible with V13 SP1 onward
PROFINET interface CPU PROFINET port (X1) Used as the Modbus TCP transport — no PROFINET device required on the gateway The InterfaceId field in TCON_IP_V4 must match the system constant for X1
Switch (if used) Unmanaged or managed Ethernet switch Any switch with auto-negotiation and no port-security (managed switches may strip BPDU/keepalive) Verify the switch does not disable the port on link flap
MB_CLIENT and MB_SERVER are part of the "Communication processor and Modbus TCP" library. They are not visible in the default LAD/FBD/ST palette in early TIA Portal versions. On older CPUs (firmware V2.x, e.g. CPU 1211C DC/DC/DC with order number 6ES7211-1AD30-0XB0) the instructions must be added from the "Instructions" task card under Communication → Communication processor → Modbus TCP. Firmware V4.0 and higher is required for the asynchronous MB_CLIENT behaviour described in this article — older firmware exposes only the legacy MB_MSG instruction which has a different error-handling model.

3. System Topology and Data Flow

S7-1200 CPU 192.168.0.5 MB_CLIENT x2 Watchdog FB KTP600 HMI 192.168.0.2 HMI tag polling Symbolic access Emerson Gateway 192.168.0.1:502 Modbus TCP Server Register 40099 (heartbeat) S7 HMI protocol Modbus TCP — process read (1 Hz) + heartbeat (0.2 Hz) Cable fault — last value persists in MB_DATA_PTR Stale-Data Watchdog (S7-1200 user code) 1. Read heartbeat register 40099 every 5 s 2. Compare to last value — unchanged for 5 reads → StaleData := TRUE 3. On DONE & !ERROR of MB_CLIENT → StaleData := FALSE 4. HMI reads ProcessData.StaleData and shows "Communication Error" banner

The diagram above shows the three devices on a common /24 subnet. The S7-1200 plays two roles: it is a Modbus TCP client to the Emerson gateway and a data concentrator (HMI server) to the KTP600. The dashed red line marks the failure mode the customer observed — the physical link is broken but the local buffer still holds the last good payload.

4. Root Cause: Why MB_CLIENT Holds the Last Value

MB_CLIENT is a function block (FB) that wraps the open PROFINET/TCP connection used for Modbus transport. Internally it issues a TCON (open connection), a TSEND (transmit request), a TRCV (receive response), and a TDISCON (close on error or operator request). The instruction updates MB_DATA_PTR only when a Modbus response is received, fully parsed, and validated against the expected MB_MODE / MB_DATA_ADDR / MB_DATA_LEN. If the request times out, the server returns a Modbus exception, or the TCP connection is reset, the instruction sets ERROR = 1 and loads the corresponding STATUS code — but it does not overwrite MB_DATA_PTR. The buffer holds whatever data was last successfully decoded.

This behaviour is documented in the Siemens application note "Modbus/TCP with MB_CLIENT and MB_SERVER" (entry ID 102020340). The instruction is implemented as an asynchronous FB: REQ is set on the rising edge, BUSY is asserted, the request is sent over the open TCP socket, the response is decoded, MB_DATA_PTR is updated, then DONE is pulsed for one scan. None of the documented error paths touch the user data buffer; only the status word, the connection state, and the internal socket descriptor change. This is intentional — clearing the buffer on every error would make it impossible to distinguish a real zero process value from a fault, and it would break retentive storage of the last good value across short transient errors.

The behaviour on power cycle is different because the data block that backs MB_DATA_PTR is re-initialised from the load memory. If the variables are configured with Retain (a checkbox in the DB properties), the values survive a power cycle. If they are non-retain (the default for global DBs), they are zeroed — which is exactly what the original poster observed. The HMI sees a sudden drop to zero and raises a "sensor lost" alarm, but it is not a real process event — it is the cold-start initialisation of the buffer.

The HMI cannot distinguish a "real" value from a "stale" value without external information. Three signals are available to break the ambiguity:

  1. The MB_CLIENT status word (DONE, BUSY, ERROR, STATUS) — tells you whether the last transaction succeeded, but does not age out the data.
  2. The PROFINET/TCP connection state — accessible through the online diagnostics (right-click the CPU → Online & Diagnostics → Connections) but not exposed to user code without extra blocks such as TCON with a status instance.
  3. A heartbeat or "lifebeat" register exposed by the Modbus server, incremented by the gateway on every read or write, and validated by the client on every read.

The Siemens application note recommends option 3 as the most portable and deterministic way to detect a dead link, because it works regardless of TCP keepalive timing, managed switch behaviour, and gateway firmware revisions. The same application note explicitly states that the buffer is not zeroed on error and that the customer must implement a lifebeat if "stale data" must be reported.

5. MB_CLIENT Instruction Anatomy and Connection Setup

The instruction is called as a multi-instance or as a single-instance DB. Typical call in SCL on a CPU 1214C with the PROFINET interface X1 bound to the Modbus gateway on 192.168.0.1:

// MB_CLIENT instance data block: "MB_CLIENT_DB" (auto-generated by TIA)
// Connection description: TCON_IP_V4 in DB "ModbusConnection"
#MB_CLIENT_DB(
    REQ          := #PollTrigger,        // BOOL, edge-triggered every cycle of OB1
    DISCONNECT   := FALSE,
    MB_MODE      := 0,                   // 0 = Read, 1 = Write
    MB_DATA_ADDR := 40001,               // Starting Modbus address (function 3)
    MB_DATA_LEN  := 10,                  // 10 holding registers
    MB_DATA_PTR  := "ProcessData".RxBuffer,
    CONNECT      := "ModbusConnection",  // TCON_IP_V4 structure
    DONE         => #MB_Done,
    BUSY         => #MB_Busy,
    ERROR        => #MB_Error,
    STATUS       => #MB_Status          // WORD
);

The MB_MODE and MB_DATA_ADDR pair encodes the Modbus function code and the starting address. Per the MB_CLIENT reference manual entry, MB_MODE = 0 issues function 03 (read holding registers) for addresses 40001 to 49999 / 4xxxx, and MB_MODE = 1 issues function 06 (write single register) or function 16 (write multiple registers) for the same range. The MB_DATA_ADDR is a zero-based offset into the function-code address space: register 40001 is passed as 40001 (not 0), register 40002 as 40002, etc. The CONNECT structure is built once during startup and never modified while the CPU is in RUN. The MB_DATA_PTR must point to a standard-access (non-optimised) DB; an optimised block ("S7-1200 motion" access) will generate a compile error at the call site, even though the optimised access model is the default in TIA Portal V17.

The TCON_IP_V4 connection structure looks like this when viewed in the data block:

Field Data Type Value for This Project Notes
InterfaceId HW_ANY (WORD) System constant for PROFINET interface X1 (e.g. 64 on CPU 1214C DC/DC/DC 6ES7214-1AG40-0XB0) Use the system constant from the device configuration, not a literal — the value differs between CPU types
ID CONN_OUC (WORD) 1 (arbitrary, must be unique on the CPU) Used in TCON / TDISCON / TSEND / TRCV if you drop down to the raw blocks
ConnectionType BYTE 16#0B (TCP/IP, no passive partner) Do not use 16#0C — that is UDP
ActiveEstablished BOOL TRUE S7-1200 is the active partner (Modbus client opens the socket)
RemoteAddress IP_V4 (ARRAY[1..4] of BYTE) [192, 168, 0, 1] IP of the Emerson gateway
RemotePort UINT 502 Modbus TCP standard port
LocalPort UINT 0 Auto-assign; do not set to 502 (collision with the server role)

Two separate TCON_IP_V4 structures are required if you run two MB_CLIENT instances — one for the process read, one for the heartbeat write. They can share the same InterfaceId and remote endpoint, but they must have different ID values. TIA Portal will raise W#16#80C3 (connection already exists) at runtime if two MB_CLIENT instances bind to the same ID.

6. MB_CLIENT Status Word and Error Code Mapping

The STATUS output of MB_CLIENT follows the same convention as every other asynchronous S7-1200 instruction: W#16#7xxx means "call in progress", W#16#0000 means "done without error", and W#16#0001 means "done with error — see the lower byte for the actual code". The complete set of codes relevant to a cable / link fault is:

STATUS (hex) Meaning Likely Field Cause Recommended Reaction
W#16#7000 No call active (REQ = 0) Polling is not running — the trigger is wrong, or the OB is not being called Inspect the trigger bit and OB scheduling
W#16#7001 First call pending Normal during start-up; the instruction is in the first internal state No action — wait one cycle
W#16#7002 Subsequent call pending Normal during multi-packet transactions (function 16 with len > 1 frame) No action — wait for DONE
W#16#0000 DONE = 1, no error Transaction completed, MB_DATA_PTR updated Clear the stale-data latch
W#16#0001 DONE = 1, with error (lower byte is the actual code) Generic done-with-error wrapper Read the lower byte and look up the code
W#16#80A1 Connection error Physical link is down, switch port disabled, gateway powered off Treat as a cable fault; raise the stale flag immediately
W#16#80A3 Connection aborted by partner Gateway TCP stack closed the socket (e.g. timeout on the server side, gateway reboot) Treat as a cable fault
W#16#80A4 Partner not reachable ARP failure, no route to host, IP conflict, switch is filtering the MAC Treat as a cable fault
W#16#80B0 TCP connection error Generic TCP/IP transport failure Treat as a cable fault
W#16#80B1 TCP connection request rejected Partner TCP listener not active, wrong port (default 502 not open on the gateway) Verify the gateway is configured as a Modbus server on port 502
W#16#80B2 TCP connection diagnostic error Internal TCON diagnostic buffer overflow Reduce the number of open connections on the CPU
W#16#80C0 Modbus data error Server returned an exception (see 80E0 / 80E1) Check the requested address and function code against the gateway map
W#16#80C3 Connection already exists Two MB_CLIENT instances pointing at the same CONNECT block / same ID Use a unique ID per MB_CLIENT instance
W#16#80C4 TCP connection error (internal) CPU resource exhaustion (open connections, sockets, instances) Count active connections in TIA Portal — limit is CPU-specific (CPU 1214C: 8 open connections)
W#16#80C8 No response from server Gateway not answering within the timeout — exact symptom of a cable pull Treat as a cable fault; raise the stale flag
W#16#80D0 MB_DATA_ADDR error Address out of range for the server (e.g. requesting 40099 on a gateway that exposes 40001..40020) Reduce the address range or map the register on the gateway
W#16#80D1 MB_MODE error MB_MODE not 0 or 1 Correct the input
W#16#80D2 MB_DATA_LEN error Length out of range (must be 1..125 for function 3) Reduce MB_DATA_LEN to 125 or less
W#16#80D3 MB_DATA_PTR error MB_DATA_PTR is NIL or points outside the DB Verify the pointer at compile time
W#16#80D4 MB_DATA_PTR alignment error Pointer offset is not on a WORD boundary Use a structured array indexed by an even offset
W#16#80E0 Modbus exception response Lower byte is the Modbus exception code (01 = illegal function, 02 = illegal data address, 03 = illegal data value, 04 = slave device failure) Cross-reference the exception code against the gateway log
W#16#80E1 Modbus illegal function Function code not supported by the gateway Verify the gateway supports the function
W#16#80E2 Modbus illegal data address Address not mapped in the gateway Verify the gateway configuration
W#16#80E3 Modbus illegal data value Write value out of range Clamp the write value before the call

W#16#80C8 is the code that will appear most often on a cable pull. Critically, it is set on the failing transaction, but as soon as REQ is re-triggered and the cable is still out, the code is overwritten with W#16#80A1 / W#16#80A4 on the next failing cycle. A pure status-code watchdog that only reads STATUS once per second will miss intermittent link flap. The robust solution is to keep a sticky "error latch" that is cleared only on a successful transaction (i.e. DONE = 1 and ERROR = 0). The heartbeat watchdog described in section 7 performs that latching implicitly: an unchanged heartbeat value across N polls cannot be caused by a single transient error — it is a persistent fault.

7. Heartbeat / Lifebeat Monitoring on the Modbus Server

The MB_CLIENT instruction does not provide a built-in application-layer timeout the way some SCADA drivers do, so the watchdog must be implemented in the PLC. The standard pattern is:

  1. Configure one writable holding register on the gateway (e.g. 40099) as a heartbeat counter. The Emerson Modbus TCP gateway exposes user-defined register maps — most firmware versions allow a "data simulator" register that increments on every write or that follows a formula based on internal state. Refer to the gateway's Modbus Register Map PDF shipped with the device for the exact location.
  2. From the S7-1200, write a monotonically increasing counter (a 16-bit word wrapped by ADD 1, MOD 32767) into that register on a fixed period (e.g. once per 5 seconds) using a second MB_CLIENT instance with MB_MODE = 1 and function 6 (write single register).
  3. In the same scan, read the register back and compare the value to the last value written. If the value did not change within N read cycles, the link is dead.

A simpler variant works on Modbus TCP servers that do not expose a writable heartbeat: poll a real process register whose value is known to change under normal conditions. The "lifebeat" register can be the gateway's own internal status word, the Modbus transaction counter, the gateway's uptime counter in seconds (commonly mapped to a 32-bit holding-register pair), or a system status coil. The Emerson gateway exposes such a register on most firmware versions — the exact address is in the Modbus map PDF. If the gateway does not expose any changing register, you must add a writable heartbeat — there is no other way to distinguish a real process value from a stale buffer.

The reference design in the Siemens application note "Modbus/TCP with MB_CLIENT and MB_SERVER" uses a 16-bit counter written by the server (not the client) and incremented every 200 ms. The client reads the counter, stores the last-seen value, and raises "stale data" if the difference is greater than 5 counts. That same logic applies here — the only difference is who writes the counter (server-internal vs. client-driven). A client-driven counter has the advantage of working with any Modbus server, including the Emerson gateway, and provides a deterministic round-trip test of the entire TCP path.

Do not confuse the Modbus TCP server keepalive (a TCP-level feature that sends an empty ACK after a configurable idle period, default 30 s on most stacks) with the application-level heartbeat. TCP keepalive only proves the socket is open — it does not prove the Modbus application is responsive. A managed switch that has learned the MAC and is forwarding frames will keep the TCP socket alive even if the gateway application is hung. The application-level heartbeat is the only proof that the gateway firmware is still serving Modbus requests.

8. Step-by-Step Implementation on TIA Portal V16 / V17

8.1 Prerequisites

  • TIA Portal V16 or higher installed and licensed.
  • S7-1200 CPU with firmware V4.2 or higher (V4.4 recommended for current TIA Portal versions).
  • The CPU is added to the project with the PROFINET interface configured (IP 192.168.0.5, subnet 255.255.255.0).
  • MB_CLIENT instruction is available in the Instructions task card under Communication → Communication processor → Modbus TCP.
  • Emerson gateway is configured with IP 192.168.0.1, Modbus TCP server enabled on port 502, and the heartbeat register mapped to address 40099 (function code 3 for read, function code 6 for write).
  • The KTP600 HMI is added with IP 192.168.0.2 and the S7-1200 connection is configured with the correct rack / slot.

8.2 Build the Connection Description DB

  1. In the project tree, add a new global DB named ModbusConnection.
  2. Inside the DB, create a tag of data type TCON_IP_V4 (found in the "PLC data types" library under Communication).
  3. Set the fields: InterfaceId = system constant for PROFINET X1 (use the lookup from the device configuration, e.g. "Local PROFINET interface_1"), ID = 1, ConnectionType = 16#0B, ActiveEstablished = TRUE, RemoteAddress = [192, 168, 0, 1], RemotePort = 502, LocalPort = 0.
  4. Add a second DB ModbusConnection_Heartbeat with the same fields but ID = 2.

8.3 Build the Process Data DB

  1. Add a new global DB named ProcessData.
  2. Disable the Optimised block access checkbox (standard access is required by MB_DATA_PTR).
  3. Create an array RxBuffer of type ARRAY[0..9] of WORD (10 holding registers).
  4. Create a tag Heartbeat of type WORD, a tag HeartbeatLast of type WORD, a tag HeartbeatDelta of type INT, and a tag StaleData of type BOOL.

8.4 Call MB_CLIENT in OB1

  1. Open OB1 "Main" and add a network for the process read job.
  2. Drag MB_CLIENT into the network. TIA Portal will auto-generate the instance DB MB_CLIENT_DB.
  3. Wire REQ to a 1 Hz clock bit from the system clock (use the system bit %M0.5 for a 1 Hz square wave on S7-1200, or generate it in a cyclic OB).
  4. Wire MB_MODE = 0, MB_DATA_ADDR = 40001, MB_DATA_LEN = 10, MB_DATA_PTR = "ProcessData".RxBuffer, CONNECT = "ModbusConnection".
  5. Wire the DONE / BUSY / ERROR / STATUS outputs to tags in a separate diagnostics DB.

8.5 Add the Heartbeat Watchdog

  1. Add a second MB_CLIENT instance named MB_CLIENT_Heartbeat with MB_MODE = 1 (write), MB_DATA_ADDR = 40099, MB_DATA_LEN = 1, MB_DATA_PTR = "ProcessData".Heartbeat, triggered every 5 seconds by a separate clock bit. Note: the write of the counter is initiated by the client; the read-back is the next cycle of the same MB_CLIENT instance with MB_MODE = 0. If you use a single instance with multiplexed mode, make sure the watchdog is gated on the DONE of the read cycle, not the write cycle.
  2. In a cyclic OB (e.g. OB35 with a 100 ms period, if available, or OB1 main scan), add the watchdog logic shown in section 9.
  3. Add the HMI tag pointer: in the KTP600 connection, point a new tag StaleData to DB number . byte offset corresponding to "ProcessData".StaleData in the S7-1200. The byte offset can be found in the DB editor under Properties → Information → Offset.
  4. On the HMI screen, add a text field or icon that shows a "Communication Error" banner driven by the StaleData tag. Use the HMI's built-in "Visibility" animation to overlay the banner on top of the normal value display when StaleData = 1.

9. Sample SCL Code for the Stale-Data Watchdog

// Watchdog network — runs in OB1 or OB35
// Inputs:
//   "ProcessData".Heartbeat          : WORD, value read from gateway register 40099
//   "ProcessData".HeartbeatLast      : WORD, last value seen on a successful read
//   "ProcessData".HeartbeatDelta     : INT, count of unchanged reads
//   #MB_Heartbeat_Done               : BOOL, DONE of the heartbeat MB_CLIENT call
//   #MB_Heartbeat_Error              : BOOL, ERROR of the heartbeat MB_CLIENT call
//   "Diagnostics".MaxStaleReads      : INT, threshold (e.g. 5)

IF "ProcessData".Heartbeat <> "ProcessData".HeartbeatLast THEN
    // Heartbeat changed — link is alive
    "ProcessData".HeartbeatLast  := "ProcessData".Heartbeat;
    "ProcessData".HeartbeatDelta := 0;
    "ProcessData".StaleData      := FALSE;
ELSE
    // Heartbeat did not change — increment the age counter
    "ProcessData".HeartbeatDelta := "ProcessData".HeartbeatDelta + 1;
    IF "ProcessData".HeartbeatDelta > "Diagnostics".MaxStaleReads THEN
        "ProcessData".StaleData := TRUE;
    END_IF;
END_IF;

// Reset the stale flag on a clean transaction
IF #MB_Heartbeat_Done AND NOT #MB_Heartbeat_Error THEN
    "ProcessData".StaleData := FALSE;
    "ProcessData".HeartbeatDelta := 0;
END_IF;

// On a power cycle, the DB is non-retain, so initialise the latch
IF "FirstScan" THEN
    "ProcessData".HeartbeatLast  := 0;
    "ProcessData".HeartbeatDelta := 0;
    "ProcessData".StaleData      := TRUE;  // assume stale until proven otherwise
END_IF;

The FirstScan bit is a system bit set on the first OB1 scan after restart (%M0.0 in the system clock memory of the S7-1200). The same logic can be expressed in ladder logic with a comparison EQ + a counter CTU + a reset coil; SCL is preferred for readability and for upload to the KTP600 HMI as a single block. The threshold MaxStaleReads = 5 with a 5 s heartbeat period gives a 25 s detection window — long enough to ride out a single Modbus transaction error, short enough to alert the operator before the process drifts significantly.

9.1 Ladder-Logic Equivalent

// Network 1 — heartbeat age counter (CTU)
A "FirstScan"
S "ProcessData".StaleData       // assume stale on power-up

A "ProcessData".HeartbeatChanged // from a comparison EQ + edge detect
JCN _NO1
L 0
T "ProcessData".HeartbeatDelta  // reset the age counter
"ProcessData".StaleData := FALSE // clear the stale flag
_NO1: NOP 0

// Network 2 — increment the age counter on unchanged heartbeat
A "ProcessData".HeartbeatUnchanged // from a comparison EQ + edge detect
CU "ProcessData".HeartbeatCTU       // counts up on each unchanged read

// Network 3 — raise the stale flag when the threshold is exceeded
L "ProcessData".HeartbeatCTU.CV     // current count value
L "Diagnostics".MaxStaleReads        // threshold (e.g. 5)
>I                               // greater than
="ProcessData".StaleData            // raise the flag

10. Verifying the Solution on the HMI

  1. Download the project to the CPU and the HMI.
  2. Go online, force the polling bit to 1, and watch the ProcessData.Heartbeat tag in the watch table. It should increment by 1 every 5 seconds (if you used the write-back scheme) or follow whatever pattern the gateway produces.
  3. Pull the Ethernet cable from the gateway. Within MaxStaleReads × 5 s (25 s with the values above), the StaleData tag should go TRUE. The HMI will switch the value display to a "Communication Error" banner.
  4. Reconnect the cable. The next successful read will reset StaleData to FALSE and the HMI will resume the normal display.
  5. Power cycle the CPU. On restart, StaleData is TRUE (forced by FirstScan) and the HMI shows the banner until the first successful read completes — typically < 2 s with a 1 s process poll.
  6. Verify the MB_CLIENT STATUS in the watch table: it should show W#16#80C8 on the first failing transaction after the cable pull, then W#16#80A1 on subsequent transactions. The exact sequence depends on the gateway's TCP keepalive setting (default 30 s on the Emerson gateway).
If the HMI banner does not appear, check the tag pointer in the KTP600 connection. The KTP600 must poll the symbolic tag ProcessData.StaleData — older KTP600 Basic panels (firmware < V12) do not support symbolic access and require a "DB number + byte offset" pointer. The byte offset is shown in the DB editor under Properties → Information → Offset for the StaleData tag. Also confirm the polling cycle on the HMI connection is < 2 s — a 5 s cycle will mask the watchdog response.

11. Edge Cases and Alternate Architectures

Single MB_CLIENT with multiplexed read/write. Some sites prefer one MB_CLIENT instance and toggle MB_MODE between reads and writes. In that case the heartbeat write and the process read share the same instance. The DONE / ERROR / STATUS outputs must be demultiplexed by mode. The watchdog logic does not change, but the threshold must be doubled because each heartbeat cycle now takes two transactions (one write, one read-back).

Modbus TCP gateway behind a managed switch with port-security. If the switch disables the port on a link-down event, the TCP socket is reset by the switch, and the S7-1200 will see STATUS = W#16#80A3. The heartbeat is the only way to detect the failure from user code, because the port-security event is not exposed as a tag. Always disable port-security on the switch port facing the PLC for predictable diagnostics.

Emerson gateway with a built-in "comms OK" coil. Many Emerson gateways expose a Modbus coil or register that mirrors the upstream serial link status. Polling that register and combining it with a heartbeat gives a two-level health check: the heartbeat proves the IP/TCP path is alive, the "comms OK" register proves the field device on the gateway is alive. Map the two signals to two HMI tags for a "PLC ↔ Gateway" banner and a "Gateway ↔ Field" banner.

Watchdog timing budget. The Modbus TCP standard does not define an application-layer timeout; the TCP stack on the S7-1200 waits for the server's keepalive (default 30 s on most stacks, configurable in the gateway). The general rule of thumb is: detection time = heartbeat period × MaxStaleReads. For a 5 s heartbeat and 5 reads, detection is 25 s — well below the TCP keepalive. For a safety-relevant signal, shorten the heartbeat to 1 s and the tolerance to 3 reads (3 s total). For a non-critical monitoring loop, 30 s and 3 reads is acceptable.

Retain behaviour of the watchdog tags. The StaleData flag should be non-retain: a retain flag would remember the fault across a power cycle and could mask a freshly-recovered link. The HeartbeatLast tag should be retain only if the gateway retains its own counter across a power cycle — otherwise the comparison will fail on the first read after a restart and raise a false stale alarm. The HeartbeatDelta counter must be non-retain (it represents runtime state, not configuration).

Multi-slave operation. If the S7-1200 polls more than one Modbus server, give each server its own CONNECT description, its own MB_CLIENT instance, and its own heartbeat register. Do not share a CONNECT block across instances — TIA Portal will raise W#16#80C3 at runtime. The total number of open connections is CPU-specific: CPU 1211C supports 4, CPU 1212C supports 4, CPU 1214C supports 8, CPU 1215C supports 16, CPU 1217C supports 32. Plan the number of Modbus slaves + HMI connections + any other TCP/UDP sockets against this budget.

Alternative: using MB_UDT or MB_RED_CLIENT. The S7-1200 also exposes MB_UDT (used to build custom Modbus function codes beyond the standard 03 / 06 / 16) and on the S7-1500 MB_RED_CLIENT (for redundant Modbus connections). Neither is required for the heartbeat watchdog described in this article — a second MB_CLIENT instance is simpler and supported on the S7-1200 firmware V4.0 onward.

Modbus exception code decoding. When STATUS = W#16#80E0, the lower byte of STATUS is the Modbus exception code returned by the gateway. Decoding: 01 = Illegal Function, 02 = Illegal Data Address, 03 = Illegal Data Value, 04 = Slave Device Failure, 05 = Acknowledge (long operation in progress), 06 = Slave Device Busy, 08 = Memory Parity Error, 0A = Gateway Path Unavailable, 0B = Gateway Target No Response. Log these codes to the diagnostics DB for trend analysis.

12. Reference Configuration Summary

Item Value
PLC IP 192.168.0.5
HMI IP 192.168.0.2
Gateway IP 192.168.0.1
Modbus TCP port 502 (default)
Process read Function 03, address 40001, length 10
Heartbeat write Function 06, address 40099, length 1
Heartbeat read-back Function 03, address 40099, length 1
Polling period (process) 1 s
Polling period (heartbeat) 5 s
Stale threshold (MaxStaleReads) 5 reads (25 s total)
DB access mode Standard (non-optimised)
MB_DATA_PTR ProcessData.RxBuffer (ARRAY[0..9] of WORD)
Connection type TCON_IP_V4, ID 1 (process), ID 2 (heartbeat)
HMI tag for stale alarm ProcessData.StaleData (BOOL, non-retain)
STATUS code on cable pull (typical) W#16#80C8 → W#16#80A1 → W#16#80A4

13. FAQ

Why does MB_CLIENT not zero out the buffer when the cable is pulled?

MB_CLIENT is an asynchronous instruction that only writes to MB_DATA_PTR when a Modbus response is successfully parsed. On a timeout, exception, or TCP reset, the instruction sets ERROR and STATUS but does not touch the user data. This is by design — clearing the buffer on every error would make it impossible to distinguish a real zero process value from a fault. The reference design in the Siemens Modbus/TCP application note documents the same behaviour.

Which STATUS code do I get on a cable pull?

The most common code is W#16#80C8 (no response from server) on the first failing transaction, followed by W#16#80A1 / W#16#80A4 (connection error / partner not reachable) on subsequent transactions. The exact sequence depends on the TCP keepalive setting on the gateway and on whether the gateway is fully powered off or just the Ethernet interface is down. Always read the STATUS word, do not rely on a fixed code.

Can I use MB_CLIENT with an optimised data block?

No. MB_DATA_PTR must point to a standard-access (non-optimised) data block. TIA Portal raises a compile error if you try to bind it to an ARRAY inside an optimised block. Keep the Modbus data DB non-optimised and put the watchdog tags in a separate block (optimised or not) if you want to use symbolic access from the HMI on the watchdog bits.

What is the right heartbeat period?

Trade off responsiveness against Modbus load. For a 1 s process scan, a 5 s heartbeat with a 5-read tolerance (25 s total) is the standard Siemens recommendation. For a safety-relevant signal, drop the heartbeat to 1 s and the tolerance to 3 reads. The heartbeat read and write together consume 2 transactions per period — make sure the gateway can handle the load (most Emerson gateways accept 100+ transactions per second).

Why does the buffer go to zero on a power cycle?

The default is non-retain storage for global DBs. The buffer is re-initialised to its declared initial value (usually 0) when the CPU transitions from STOP to RUN. To keep the last value across a power cycle, open the DB properties, enable Retain for the buffer tags, and download the project again. Be aware that retaining the buffer is rarely what you want for a stale-data watchdog — the customer-visible "sensor lost" alarm on power-up is actually the right behaviour for a safety-conscious design, and the FirstScan-initialised StaleData flag will mask it gracefully.

Is there a built-in TCP keepalive timeout I can rely on instead of a heartbeat?

No — not for application-level fault detection. The TCP keepalive only proves the socket is open at the IP layer. A managed switch that has learned the MAC and is forwarding broadcast frames will keep the TCP socket alive even if the gateway firmware is hung, the field device is disconnected, or the gateway application has crashed. The application-level heartbeat is the only proof that the gateway is still serving Modbus requests, and the only reliable way to detect a "stuck" gateway from the PLC user code.

Back to blog