1. Problem Overview
An S7-1200 CPU running TIA Portal is configured as a Modbus TCP client using the MB_CLIENT instruction from the "Modbus TCP" library. The instance is connected to a third-party field device, and the TCP session establishes without issue. The instance STATUS output cycles predictably through 7004 → 7005 → 7006:
- 7004 – Connection established and monitored. No job processing active.
- 7005 – Data was sent.
- 7006 – Data was received.
Despite this healthy-looking status sequence, the DONE output of the instance is observed as "always false" in the online watch table, BUSY toggles true/false with every request, and ERROR is also "always false". A second client (Modbus Poll on a PC) reaches the same field device without any problem, which proves the field device itself is functioning and reachable. The fault therefore lies inside the S7-1200 application logic and the way the application code interacts with the MB_CLIENT instance.
This article identifies the two independent root causes that produce the "DONE always false" symptom, shows the official Siemens status-code mapping that proves the job is actually completing, and gives a verified ladder/ST pattern for capturing DONE/ERROR so the symptom cannot be confused with a real communication failure.
2. MB_CLIENT STATUS Code Reference
The official TIA Portal reference for MB_CLIENT defines the STATUS output as the diagnostic word for the most recently completed (or currently running) job. The most relevant values for the scenario in this article are summarized in the table below.
| STATUS (hex) | STATUS (dec) | Meaning |
|---|---|---|
7000 |
28672 | No job active. Connection not yet established. |
7001 |
28673 | Job active, request being processed. |
7002 |
28674 | Job active, waiting for response from Modbus server. |
7003 |
28675 | Connection terminated. DISCONNECT was set or an error has occurred. |
7004 |
28676 | Connection established and monitored. No job processing active. |
7005 |
28677 | Data was sent to the Modbus server. |
7006 |
28678 | Data was received from the Modbus server. |
8382 |
33666 | Modbus exception code 02 received (illegal data address). Reported as transient value at job end. |
8383 |
33667 | Modbus exception code 03 (illegal data value). |
8384 |
33668 | Modbus exception code 04 (server device failure). |
80C8 |
32968 | No response from server within configured timeout. Reported only after the configured number of repeated attempts. |
80C9 |
32969 | TCP connection error (RST, FIN, or ARP failure). |
The official Siemens documentation for the STATUS parameter is published in the TIA Portal help under "MB_CLIENT – Communicating via PROFINET as a Modbus TCP client" at the TIA Portal v21 Modbus TCP reference.
7004 → 7005 → 7006 that repeats once per MB_MODE cycle is a positive indication that a request was sent, a valid Modbus TCP PDU was returned, and the connection is healthy. The status sequence does not, on its own, indicate whether the application layer produced a DONE or an ERROR – the application must latch those flags to see them.3. Root Cause #1 – REQ is Level-Controlled, Not Edge-Triggered
The first and most common cause of the "DONE always false" symptom is treating the REQ input as a one-shot pulse. The Siemens documentation states clearly that REQ is level-controlled:
"As long as the input REQ is set, the instruction sends communication requests. After the Modbus query has started, the instance DB is locked for other clients. Changes to the input parameters will not become effective until the server has responded or an error message has been output. If the parameter REQ is set again during an ongoing Modbus request, no additional transmission takes place afterwards."
Two implementation patterns that produce the symptom and must be avoided:
-
Clock-byte toggling of REQ. Code that writes
REQ := NOT REQon every OB1 cycle or on a fast clock bit such that theREQpulse is shorter than the Modbus round-trip. The instance DB is locked for the duration of the transaction, but the falling edge before completion means the next rising edge is registered as a new request which immediately overlaps with the still-locked DB. The result is that the request either never completes cleanly or the DONE pulse is dropped before user logic can observe it. -
Setting REQ false as soon as DONE is seen. This is harmless for the Modbus transaction itself, but if the code is doing something like
IF DONE THEN REQ := FALSEon the same scan and then using DONE to drive a one-cycle flag, the visible "DONE always false" symptom appears. The fix is to keepREQtrue continuously and use a separate edge-detected flag for "request issued" accounting.
The correct pattern is to hold REQ true continuously for the lifetime of the client session, allowing the instance to issue a new request as soon as the previous one completes. If cycle-by-cycle polling is required, the application can still monitor BUSY and ignore intermediate transitions.
4. Root Cause #2 – DONE/ERROR Pulse for One Scan Cycle
The second root cause is a behavioral nuance of the MB_CLIENT output flags. According to the reference manual:
"DONE and ERROR remain set for one cycle of the OB in which MB_CLIENT is called. If OB1 is the calling OB, DONE/ERROR are therefore visible for one OB1 cycle only."
For an S7-1200 with a typical OB1 cycle of 1–10 ms, and a Modbus request that takes 5–50 ms over a 100 Mbit/s PROFINET interface, the DONE pulse can fall entirely between two consecutive watch-table samples. The HMI tag that an operator expects to see "stick" will instead show a constant FALSE even though the instruction is completing successfully on every request.
This is the most important diagnostic insight of the article: a status sequence of 7004/7005/7006 cycling at the expected rate is proof that DONE is firing on every cycle – you simply cannot see it in a polled watch table. The fix is to latch DONE and ERROR in the user program.
5. Diagnosing the Symptom
Before changing any code, perform the following ordered checks. They are non-destructive and can be done on a live system.
- Confirm the field device is reachable. Open TIA Portal "Online > Accessible devices" and ping the IP of the field device. Confirm the same IP is reachable from a PC running Modbus Poll. Both checks succeeding rules out cabling, switch, and IP issues.
-
Confirm the
TCON_IP_v4connection is established. In the watch table, monitor the connection DB. A value of7004on theSTATUSword of the instance is by itself evidence that the TCP connection is up and idle. -
Confirm the
STATUScycle rate. Add a counter tagSTATUS_CHG_CNTthat increments on everySTATUSchange. For a healthy Modbus TCP client polling once per 50 ms, you should see the counter increase at roughly 20 Hz. A counter that does not move confirms the instance is not even attempting requests. -
Latch DONE and ERROR in the user program. Use the ST pattern in section 6 below. If
LATCH_DONErises to TRUE within a few hundred milliseconds of going online, the instruction is completing successfully and the previous "always false" reading was a measurement artifact. -
If DONE does not latch and ERROR does not latch either, then the instance is being starved of
REQor the inputs are being overwritten. Add a watch onREQand verify it is held TRUE continuously.
6. Latching DONE/ERROR in SCL
The minimal correct pattern in Structured Text for an S7-1200 calling MB_CLIENT from OB1 is shown below. It uses two static latches (lastD, lastE) to detect rising edges on the one-cycle outputs.
// Static latches in the instance FB or in a global DB
#lastD := #latchedDone;
#lastE := #latchedError;
IF #MODBUS_TCP_CLIENT[i].DONE AND NOT #lastD THEN
#latchedDone := TRUE;
#modbusOK := #modbusOK + 1; // diagnostic counter
END_IF;
IF #MODBUS_TCP_CLIENT[i].ERROR AND NOT #lastE THEN
#latchedError := TRUE;
#lastStatus := #MODBUS_TCP_CLIENT[i].STATUS; // capture 8382/8383/...
END_IF;
// Edge-triggered clear (operator reset or HMI button)
IF #resetLatches THEN
#latchedDone := FALSE;
#latchedError := FALSE;
END_IF;
Three engineering rules built into this pattern:
- REQ is held TRUE for the whole session; the
i-th instance is never clocked. -
DONEandERRORare latched on the rising edge. The latch survives until reset, which is what the watch table needs to actually see them. - The
STATUSword is captured the same scan as the ERROR rising edge, so exception codes such as8382(illegal data address) are preserved for diagnostics even thoughSTATUSitself is transient.
7. Latching DONE/ERROR in Ladder (LAD/FBD)
For a ladder implementation, use two SR flip-flops triggered on the rising edge of DONE and ERROR respectively. The set coil is driven by a P-contact (positive edge) on the instance output, and the reset coil is driven by an operator command.
| Network | Element | Operand | Description |
|---|---|---|---|
| N1 | Contact |
MB_CLIENT.DONE (P) |
Rising-edge detector on DONE. |
| N1 | Coil |
LATCH_DONE (S) |
Set sticky "request complete" flag. |
| N2 | Contact |
MB_CLIENT.ERROR (P) |
Rising-edge detector on ERROR. |
| N2 | Coil |
LATCH_ERROR (S) |
Set sticky "request failed" flag. |
| N2 | Move | |
Preserve transient exception code. |
| N3 | Contact | RESET_LATCHES |
Operator reset. |
| N3 | Coil |
LATCH_DONE (R), LATCH_ERROR (R) |
Clear latches. |
This ladder pattern is functionally equivalent to the ST snippet above and is the preferred form on S7-1200 CPUs that are programmed exclusively in LAD/FBD.
8. Common Companion Errors
When the latching pattern above is in place and LATCH_DONE still never rises, the failure has moved out of the "measurement" category and into a real application-layer fault. The most common offenders, in order of frequency, are:
| Symptom | Likely cause | Fix |
|---|---|---|
STATUS = 8382 transiently at end of transaction |
MB_MODE = 4 with a starting register address that the server does not expose. Modbus exception 02 (illegal data address). | Switch to MB_MODE = 4 (Read Input Registers, function code 04) for input-only devices, and verify the address range against the device map. |
STATUS = 80C8 after repeated attempts |
Server is reachable on TCP but does not reply to the request. Common on devices that require a function code they do not implement. | Confirm function code (01/02/03/04/05/06/15/16) is supported. For input-only sensors, use function code 04. |
STATUS = 80C9 intermittent |
Server or switch is closing the connection (FIN/RST). Often a TCP keep-alive mismatch. | Match the server's keep-alive / idle timeout. Set MB_CLIENT's connection parameters to a heartbeat below the server's idle timer. |
STATUS = 7003
|
DISCONNECT was pulsed or an unrecoverable error terminated the connection. | Hold DISCONNECT false; clear and re-issue REQ only after STATUS returns to 7004. |
| DONE pulses but read data is wrong | Byte order mismatch (Modbus is big-endian; S7-1200 is little-endian) or wrong unit ID. | Byte-swap pairs of words in the destination DB. Verify MB_UNIT_ID matches the server's slave/unit ID; broadcast/0 is often rejected. |
MB_MODE = 4 (Read Input Registers, function code 04). The original instance in the source case was attempting a holding-register read (MB_MODE = 0, function code 03), which on a read-only device will return Modbus exception 02 (8382) at the end of the otherwise-normal transaction. Switching to function code 04 is the standard fix.9. Step-by-Step Verification Procedure
Use the following ordered procedure on a live machine to convert the symptom into a measurable, signed-off result.
- Open the project in TIA Portal and go online with the S7-1200.
- Open the watch table and add
REQ,DONE,ERROR,BUSY,STATUS, and the two latch tagsLATCH_DONEandLATCH_ERRORfrom section 6 or 7. - Force
REQ = TRUEfor the affected instance. Confirm it stays TRUE (does not oscillate). - Start the watch table at a 100 ms update interval. Watch
STATUSfor 5 seconds and confirm it cycles through 7004 / 7005 / 7006. - Observe
LATCH_DONE. Within 200 ms it should transition from FALSE to TRUE.LATCH_ERRORshould remain FALSE. - If
LATCH_ERRORgoes TRUE, readLAST_ERR_STATUSand map it through the table in section 2. Most commonly this will be8382for a read of a non-existent register, or80C8for a server that does not respond to the chosen function code. - Apply the fix in section 8 that matches the captured status code, online in the running CPU. Re-latch and confirm
LATCH_DONErises and stays risen withLATCH_ERRORstaying FALSE. - Reset the latches via the operator button, and document the running STATUS in the HMI faceplate so the next shift can see "last successful poll at HH:MM:SS".
10. Commissioning Notes and Edge Cases
Several edge cases appear regularly in field commissioning and are worth calling out explicitly.
-
Multiple instances, single partner. An S7-1200 can run up to 16
MB_CLIENTinstances concurrently. Each instance has its ownTCON_IP_v4structure. If two instances point at the same partner IP, both will appear to establish a TCP connection but the partner's session table will fill. Symptoms on the second instance are similar to the one in this article – the partner simply drops the second connection. The official fix is to use a singleMB_CLIENTand multiplex the function codes, or to open a second session only if the partner explicitly supports it. -
Library version mismatch. The S7-1200 Modbus TCP library was substantially rewritten at V4.0. Earlier library versions (V3.x and below) used a different instruction name and had a different
STATUSword layout. Mixing an old instance DB with a new instruction produces a status of7003withDONEnever rising. The official Siemens upgrade path is documented in the TIA Portal online help under the same "Modbus TCP for the library versions V4.0 and later" topic. -
Watch table update interval. TIA Portal's default watch-table update interval is 500 ms. A Modbus request that completes in 30 ms is therefore invisible 16× out of 17 samples. Set the watch-table update interval to its minimum (100 ms) for any test that depends on observing
DONEin real time. -
Online vs. physical I/O behavior. When the CPU is stopped,
MB_CLIENTcloses its TCP connection. STATUS will return to7000andDONEwill not fire. This is correct behavior; it is not a fault. -
HMI tag polling. HMI tags derived directly from
DONEwill appear to flicker. Always bind HMI tags to the latched version. The HMI's polling interval (typically 250–1000 ms) makes the flicker look like a permanent FALSE.
11. Field-Proven Patterns
The following two patterns have proven robust across multiple site deployments of S7-1200 Modbus TCP clients and are recommended as the baseline for new projects.
11.1 Single client, polled
// OB1 SCL
IF "FIRST_SCAN" THEN
"MB_CLIENT_1".REQ := TRUE; // held true for entire session
"MB_CLIENT_1".DISCONNECT := FALSE;
END_IF;
// Latch outputs in a separate FC, e.g. FC100 "MB_DIAG_LATCH"
"MB_DIAG_LATCH"(client := "MB_CLIENT_1");
11.2 Multiple instances, single FB
// FB1000 "MB_CLIENT_HANDLER"
// Input: instance index, target IP, MB_MODE, MB_DATA_ADDR, MB_DATA_LEN
// Static: latches per index
// Behavior: holds REQ true, latches DONE/ERROR, exposes a method
// to clear latches and read the captured STATUS
Centralizing the latching logic into a single FB means every instance of MB_CLIENT in the project uses the same diagnostic pattern, and the HMI only needs to bind to one tag per instance.
12. Frequently Asked Questions
Why does DONE stay FALSE even though STATUS cycles 7004/7005/7006?
Because DONE is set TRUE for one OB1 cycle only. On a 1–10 ms OB1 and a 5–50 ms Modbus round-trip, the pulse is almost never aligned with a watch-table sample. Use a rising-edge latch to capture it; the cycling STATUS is itself proof the request is completing successfully.
Should MB_CLIENT's REQ be a one-shot pulse or held TRUE?
REQ is level-controlled, not edge-triggered. Hold REQ TRUE for the whole session; the instance will start a new request as soon as the previous one finishes. Toggling REQ before the response arrives can cause the request to be aborted and DONE to never fire.
What does STATUS 8382 mean and how do I fix it?
8382 is the transient status reported at the end of a transaction in which the server returned Modbus exception code 02 (illegal data address). The most common cause is asking for a register range the server does not expose. For input-only sensors, switch MB_MODE from 0 (Read Holding Registers, function code 03) to 4 (Read Input Registers, function code 04) and verify the address against the device's register map.
Why is ERROR also FALSE if the request is actually failing?
ERROR is pulsed for the same single cycle as DONE. If the request fails fast (for example, 8382 from the server), the ERROR pulse falls before any downstream code reads it. Latch ERROR the same way you latch DONE, and capture the STATUS word on the same scan to keep the failure code visible.
Does this apply to S7-1500 and S7-1200 G2 as well?
Yes. The same level-controlled REQ, one-cycle DONE/ERROR, and STATUS code mapping apply to the S7-1500 (library V3.x and later) and to the S7-1200 G2. The diagnostic pattern and the latching FB described in this article can be copied directly to those platforms.