Resolving SIMATIC S7-1500 MQTT Error 80C4: TRCV Connection Drops

David Krause16 min read
SiemensTIA PortalTroubleshooting
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 Description

Engineers deploying the Siemens MQTT Client library (LMQTT) on SIMATIC S7-1500 or S7-1200 CPUs (firmware V2.5 and newer) frequently encounter a recurring failure mode: the CPU establishes an MQTT session with a broker such as Mosquitto, publishes a small burst of messages successfully, then exhibits one of two symptoms.

  • The LMQTT_Client function block reports ErrorID = 16#80A1 in its status output, or
  • The underlying Open User Communication instruction (typically TRCV, TCON, or TURCV) reports ErrorID = 16#80C4 and the TCP connection is silently reset.

The failure is non-deterministic. One or two subscribe-side messages are received correctly, then the connection drops after a random period ranging from a few seconds to several minutes. The symptoms appear identically against a local Mosquitto broker on TCP/1883 and against a remote broker such as test.mosquitto.org, which isolates the root cause to the PLC side rather than the broker or the network path.

The error pair (16#80A1 at the MQTT application layer / 16#80C4 at the TCP transport layer) is the canonical fingerprint of an MQTT session that is being torn down because the client is failing to honour the MQTT keep-alive timer. The broker closes the TCP socket; the PLC surfaces this as a transient communication fault at TRCV.

The same pattern has been reproduced on S7-1214C DC/DC/DC and S7-1217 CPUs running the application example "MQTT Client Application Example for SIMATIC S7-1500" (Siemens application example by Yu-Wen Chen, downloadable from Siemens Industry Online Support). It also reproduces on S7-1500 CPUs under PLCSIM Advanced 3.0, confirming that the failure is not hardware-specific.

2. Root Cause Analysis of Error 80C4

Error code 16#80C4 has a single, well-defined meaning in the TIA Portal Open User Communication instruction set (TCON, TDISCON, TSEND, TRCV, TUSEND, TURCV): temporary communication error — the connection cannot be established at this time, the interface is receiving new parameters, the connection is being established, the configured connection is being removed by a TDISCON instruction, or the connection is being terminated by a call with COM_RST = 1.

When 16#80C4 surfaces as the result of a TRCV operation during an otherwise-active MQTT session, the underlying causes most commonly observed in the field are listed below.

Sub-cause Mechanism How to confirm
MQTT keep-alive timeout Broker closes the TCP socket because no MQTT control packet (PINGREQ) was received within 1.5× the negotiated Keep-Alive. TRCV returns 16#80C4 on the next poll. Wireshark on SP/tap shows FIN, RST, or a PINGRESP timeout. Broker log shows "client disconnected due to keepalive" or "PingReq not received".
TCP RST from peer or firewall Broker, NAT router, or stateful firewall sends RST after idle timeout. The PLC reports 16#80C4 until the next successful TCON. Wireshark on tap shows RST,ACK from broker IP. Firewall log shows flow aged out.
TDISCON or COM_RST=1 collision User code (or the LMQTT FB) intentionally disconnects and re-uses the same IDB before the previous TCON handshake has fully released resources. Cross-reference TDISCON and COM_RST in the project. Check that TCON_BUSY is FALSE before a new TCON is called on the same IDB.
OB1 cycle starvation LMQTT background task is starved. Keep-alive pings are not transmitted within the keep-alive window because the cyclic OB is interrupted by higher-priority OBs (OB35, OB82, OB121) or by long synchronous operations. Use RUNTIME in the cyclic OB to measure cycle time. The cycle time must be < 50% of the configured MQTT Keep-Alive.
Duplicate connection ID on same IDB TCON called with an IDB that is already in the "establishing" state returns 16#80C4 until the handshake completes. Use distinct connection IDs per IDB. Verify with the program status of the TCON instruction.
Re-entrancy from re-init / download STOP-to-RUN transition or project download leaves the IDB in a half-open state. The first TRCV returns 16#80C4. Force a TDISCON + TCON cycle in the startup OB (OB100).

In MQTT terms, the protocol requires a CONNACK, SUBACK, PUBACK, and a periodic PINGREQ. The LMQTT library offloads the publish / subscribe work into a cyclic task driven by the user program. If the user program is blocked, the OB1 cycle is too long relative to the MQTT keep-alive, or the cyclic OB priority is lower than other interrupt OBs, the broker considers the client dead and resets the connection. This is the most common cause of the 80C4 symptom and is the first item to verify.

3. Affected Software, Firmware, and Library Versions

Component Tested range Notes
TIA Portal V16, V16 Update 3, V17, V17 Update 4, V18, V18 Update 1, V19 LMQTT is shipped as a global library. Re-compile after TIA Portal upgrade.
LMQTT library (Siemens) V1.0.x, V1.1.x, V1.2.x (S7-1500) and V1.0.x, V1.1.x (S7-1200) Later versions add a configurable Keep-Alive and a will-message flag. The first V1.0 release has known keep-alive issues; upgrade to V1.1 or later.
S7-1500 CPU firmware V2.5, V2.6, V2.7, V2.8, V2.9, V3.0, V3.1 Open User Communication with TLS requires V2.6 or newer.
S7-1200 CPU firmware V4.2, V4.3, V4.4, V4.5, V4.6, V4.7 S7-1200 supports MQTT only via the LMQTT library; the integrated MQTT client of newer CPU firmware is for native pub/sub and is not the same code path.
PLCSIM Advanced V3.0, V3.0 Update 1, V4.0, V4.0 Update 2 Reproduces 80C4 reliably with a local Mosquitto broker on 1883.
Mosquitto broker 1.6.x, 2.0.x, 2.1.x Default max_inflight_messages = 20, max_queued_messages = 1000. Idle timeout (default 120 s) is the cause of broker-side keep-alive enforcement.
Network equipment Managed switches, NAT routers, stateful firewalls TCP idle timers of 60 s on industrial firewalls are a frequent offender.

The application example used as the reference (S7-1500 with S7-1214C / S7-1217 substitution) is delivered as a TIA Portal V16 project. If the project is opened in a newer TIA Portal version, the library must be upgraded to the matching version using "Library -> Upgrade" before the project is downloaded to a newer firmware CPU.

4. TRCV Error 80C4 Reference Table

The TRCV instruction (and its UDP counterpart TURCV) returns 16#80C4 in the following four documented conditions. Engineers should look at the DONE, BUSY, and ERROR outputs together with STATUS to disambiguate the sub-cause.

STATUS Meaning Recommended action
16#80C4 Temporary communication error Wait for the next OB1 cycle and call TRCV again. If persistent, follow Section 8.
16#80C4 + DONE=TRUE Connection torn down by TDISCON or COM_RST=1 Check the user program for an unsolicited TDISCON or for an OB100 restart that issues COM_RST=1 on the same IDB.
16#80C4 + STATUS2=16#FFFF No partner endpoint (no TCON active) Issue TCON first. The LMQTT FB normally guards this; check that the FB is not being skipped by a conditional rung.
16#80C4 + STATUS2 non-zero Detailed internal state of the TCP stack Cross-reference STATUS2 against the S7-1500 system manual "Open User Communication" appendix.

Full reference: SIMATIC S7-1500 System Manual, Open User Communication and the TIA Portal online help for TRCV / TURCV.

5. LMQTT Client Error 80A1 Reference

The LMQTT client FB exposes its own status word. When 16#80A1 is reported at the application layer simultaneously with 16#80C4 at the transport layer, the broker has closed the connection. The most common LMQTT status codes are mapped below.

LMQTT status Meaning Likely underlying TCP error
16#0000 OK, no error
16#7000 No job active, idle
16#7001 First call, job started
16#7002 Job running, BUSY
16#80A0 CONNACK not received / TCON failed 16#80C4, 16#80C3, 16#80C5
16#80A1 Connection lost during active session 16#80C4, 16#80C3 (RST from peer), or keep-alive expiry
16#80A2 SUBACK returned failure
16#80A3 PUBACK / PUBREC timeout
16#80A4 Decoding error (malformed packet)
16#80A5 Broker refused connection, reason code in errorInfo MQTT reason 1 (protocol), 2 (client ID), 3 (server unavailable), 4 (bad credentials), 5 (not authorized)

The MQTT 3.1.1 reason codes used in the broker's CONNACK or DISCONNECT are standardized in MQTT 3.1.1 Reason Codes. The same codes apply to the PLC side because LMQTT implements MQTT 3.1.1.

6. MQTT Protocol Behaviour Behind the Drop

MQTT is built on TCP. After a successful CONNECT/CONNACK handshake, the client and broker exchange a Keep-Alive interval. The MQTT 3.1.1 specification states that the broker may close the connection if it does not receive a control packet from the client within 1.5× the Keep-Alive interval. The client must therefore send something — typically a PINGREQ — at least once per Keep-Alive interval. The LMQTT library handles this internally, but only if it is called often enough by the user program.

Critical values for the S7-1500 deployment:

Parameter Typical value Constraint
MQTT Keep-Alive 60 s (LMQTT default) PLC OB1 cycle time << Keep-Alive / 2 (i.e. < 30 s for a 60 s keep-alive)
Broker idle timeout 120 s (Mosquitto default) Must be ≥ 1.5 × Keep-Alive
TCP idle timeout on firewall 60 s (typical industrial firewall) Must be ≥ 1.5 × Keep-Alive. Often shorter than the broker's timeout.
QoS 0 vs QoS 1 QoS 0 has no PUBACK wait Use QoS 0 for telemetry to avoid PUBACK timeouts on flaky links
Client ID Unique per session Duplicate client ID causes the broker to disconnect the older session

Two non-obvious failure modes derive from this protocol behaviour:

  1. Firewall-induced RST. A stateful firewall that ages out the TCP flow at 60 s will send RST when the next packet arrives. The PLC sees RST and returns 16#80C4 at TRCV. Symptom is identical to broker keep-alive expiry.
  2. Subscribe-side starvation. LMQTT uses a single TCP socket for both publish and subscribe. A long-running publish (large payload, QoS 1 PUBACK wait) can starve the PINGREQ timer, even if the OB1 cycle is short.

7. Prerequisites for Diagnosis

  • TIA Portal V16 or newer, with the matching version of the LMQTT global library installed.
  • Target CPU: S7-1500 (any FW ≥ V2.5) or S7-1200 (any FW ≥ V4.2). For S7-1200, confirm the LMQTT variant matches the CPU firmware family.
  • Active Ethernet interface on the PLC, configured with an IP address in the same subnet as the MQTT broker. PROFINET or standard Ethernet both work; PROFINET is not required.
  • Mosquitto broker 1.6 or newer, reachable on TCP/1883 (no TLS for the first test).
  • Wireshark on a SPAN port or network tap, capturing TCP/1883 traffic. A managed switch with port mirroring is recommended.
  • MQTT.fx, MQTT Explorer, or mosquitto_sub -h <ip> -t '#' -v running on an engineering PC for parallel verification.
When port 1883 is used, no certificate is required. The "do I need a certificate on 1883?" question in the field report is answered: no, 1883 is plaintext MQTT. Certificates are only required for 8883 (MQTT over TLS).

8. Diagnostic Procedure

  1. Capture the broker's log. In Mosquitto, start the broker with -v -c /etc/mosquitto/mosquitto.conf and watch stderr. Look for lines such as Socket error on client, Client <id> disconnected due to keepalive, or Received PUBLISH from <id> followed by an abrupt close.
  2. Capture the network with Wireshark. Apply the display filter tcp.port == 1883 && ip.addr == <plc_ip>. Confirm the PLC sends a PINGREQ at the configured keep-alive interval. If the gap between PINGREQ packets is > 1.5 × Keep-Alive, the LMQTT background task is being starved.
  3. Inspect the TIA Portal program status. Open the LMQTT_Client DB and observe the status, errorInfo, and errorDescription tags. Confirm that 16#80A1 appears as the last status before the drop.
  4. Measure the OB1 cycle time. Add a small RD_SYS_T capture in OB1 and compute the delta over a known period. Compare with the Keep-Alive. If cycle time is > 30 % of Keep-Alive, shorten it.
  5. Verify cyclic OB priority. LMQTT should be called in a cyclic OB. If called in OB1, no other OB should starve it. If called in OB35, set the OB35 phase offset to ensure it does not collide with PROFIBUS / PROFINET interrupt OBs.
  6. Check the TCON IDB. In the program status of the TCON instance DB, confirm that TCON_BUSY is FALSE between sessions and that TCON_DONE latches briefly then resets. A perpetually busy TCON indicates a half-open state.
  7. Eliminate firewalls. Temporarily place the PLC, broker, and engineering PC on the same unmanaged switch to rule out a firewall-induced RST. If the problem disappears, raise the firewall's TCP idle timeout to ≥ 600 s.
  8. Re-test with a longer Keep-Alive. Set the LMQTT Keep-Alive to 240 s and re-test. If the drop interval is exactly 6 minutes, you are seeing the default 5-minute broker-side keep-alive; setting 240 s on the client will not help if the broker is enforcing 300 s. Match broker and client.

9. Step-by-Step Resolution

The resolution is structured by sub-cause. Address them in the order listed.

9.1 Match the MQTT Keep-Alive to the OB cycle

  1. Open the LMQTT DB. Set the keepAlive tag to 60 (s) for the first test, 240 (s) for a forgiving second test, or 900 (s) for a long-haul WAN test.
  2. In the broker's mosquitto.conf, set max_inflight_messages 200 and confirm idle_timeout is ≥ 1.5 × Keep-Alive. Default Mosquitto idle_timeout is 120 s, so Keep-Alive must be ≤ 80 s.
  3. In TIA Portal, place the LMQTT_Client call in a cyclic OB of priority 1 (OB1) or a dedicated OB35. Confirm OB1 cycle time < 200 ms typical, and definitely < 1 s.
  4. Download the project to the CPU in STOP, then perform a warm restart.

9.2 Force a clean TCP teardown on restart

In OB100, add the following logic so that every cold / warm restart issues an explicit TDISCON on the LMQTT IDB before TCON is re-issued by the LMQTT FB itself:

// OB100 snippet - clean restart handling
IF "LMQTT_Connect".CONNECTED THEN
    "LMQTT_TDISCON_DB"(REQ := TRUE, ID := "LMQTT_Connect".ID);
    "LMQTT_TDISCON_DB"(REQ := FALSE);
END_IF;

This eliminates the 16#80C4 fingerprint on the first TRCV after a STOP-to-RUN transition.

9.3 Increase firewall TCP idle timeout

For industrial firewalls (e.g. SCALANCE S, FortiGate, Palo Alto, Cisco ASA), set the TCP session timeout for flows to TCP/1883 to a value ≥ 1.5 × MQTT Keep-Alive. A 600 s timeout is a safe engineering choice. The 16#80C4 signature typically disappears once this is done, without changing any PLC code.

9.4 Avoid duplicate Client IDs

When two PLCs or one PLC and one HMI use the same Client ID against the same broker, the broker drops the older session on the new CONNECT. The dropped session returns 16#80A1 at the application layer and 16#80C4 at the transport layer. Use a Client ID derived from the CPU's serial number (read via Get_IM_Data or stored in a global DB):

// Build a unique client ID from CPU serial + slot
"clientIdPrefix" := 'S71500_';
"clientIdSuffix" := INT_TO_STRING("hw".serialNumber);
"clientId" := CONCAT_STRING2("clientIdPrefix", "clientIdSuffix");

9.5 Upgrade the LMQTT library

If the project was built against LMQTT V1.0.x, upgrade to V1.1 or newer. Earlier versions do not support a configurable Keep-Alive and have known issues with reconnection after a broker-side close. The upgrade procedure is:

  1. Open the project in TIA Portal V17 or newer.
  2. Library → Global libraries → Open library → select the new LMQTT version.
  3. Right-click the LMQTT_Client FB in the program → "Replace with library version".
  4. Recompile and download the project.

10. Verification and Acceptance Test

  1. Open MQTT Explorer or run mosquitto_sub -h <ip> -t '#' -v on the engineering PC.
  2. Force the PLC to publish one message every 5 s for at least 30 minutes.
  3. Confirm in the broker log that the connection remains open for the full 30 minutes with no Client <id> disconnected messages.
  4. In Wireshark, confirm that PINGREQ packets arrive at the configured interval (default 60 s) without gaps > 1.5 × Keep-Alive.
  5. In TIA Portal online → program status, confirm that LMQTT_Connect.status remains 16#7000 (idle) and that errorInfo stays at 0.
  6. Subscribe to a topic from the PLC, send 100 messages from MQTT Explorer, and confirm the PLC receives all 100 without any 16#80A1 / 16#80C4 events in the diagnostic buffer.

Acceptance is reached when the connection survives the 30-minute soak test with no 16#80C4 events and no LMQTT 16#80A1 events.

11. Edge Cases and Field-Proven Cautions

  • PLCSIM Advanced 3.0 reproduces 80C4 on every restart. The simulator's virtual network adapter occasionally drops the TCP flow when the engineering PC is suspended. The 30-minute soak test on PLCSIM Advanced is therefore a worst-case scenario; a passing result on PLCSIM is a strong positive signal.
  • DNS resolution. If the broker address is given as a hostname (e.g. test.mosquitto.org) and the PLC does not have a DNS server configured, the connection will hang silently and the LMQTT FB will time out. Always use IP addresses for the first test.
  • PROFINET vs standard Ethernet. The PROFINET interface of an S7-1500 CPU can be used for MQTT, but the PROFINET IRT sync is irrelevant. Do not enable IRT for an MQTT link; it adds jitter without benefit.
  • QoS 1 burst on a lossy link. When the network drops packets, the QoS 1 PUBACK timer can starve the PINGREQ. Either drop to QoS 0 for telemetry, or use QoS 2 sparingly.
  • Retained messages on subscribe. If the broker is configured with retain_available true and the topic carries a retained message, the LMQTT client will receive the retained message first. This is normal and should not be confused with a 80C4 drop.
  • Will message. The LMQTT library supports a last-will-and-testament. Verify that the will message is not pointing at a topic that requires authentication that the client does not have, as the broker will reject the will at connect time and return reason code 5 (not authorized), surfaced as LMQTT 16#80A5.
  • Time-of-day clock. The MQTT Keep-Alive timer in LMQTT is driven by the user program's call rate, not by the CPU's time-of-day clock. If the program is suspended (e.g. test mode in TIA Portal), the keep-alive will not be sent and the broker will drop the session on the next event.

12. Preventive Checklist and FAQ

Pre-commissioning checklist

  • OB1 cycle time < 200 ms measured and recorded.
  • MQTT Keep-Alive configured to 60 s; broker idle_timeout ≥ 90 s.
  • Firewall TCP idle timeout ≥ 600 s for flows on TCP/1883.
  • Client ID unique per PLC, derived from the CPU serial.
  • LMQTT library version V1.1 or newer.
  • OB100 issues TDISCON on restart for clean reconnection.
  • 30-minute soak test passed in PLCSIM Advanced and on the real CPU.
  • Wireshark capture archived in the project documentation folder.

What does ErrorID 80C4 mean on a SIMATIC S7-1500 TRCV instruction?

16#80C4 is the canonical "temporary communication error" returned by the TIA Portal Open User Communication instructions (TCON, TDISCON, TRCV, TSEND, TUSEND, TURCV). It indicates that the connection cannot be used at the moment, either because the interface is being re-initialised, a TCON is still in progress, or a TDISCON / COM_RST=1 has just torn it down. In an MQTT context it almost always means the broker has closed the socket, most often because the MQTT Keep-Alive expired.

Why do I see 80A1 in the LMQTT client and 80C4 in TRCV at the same time?

The LMQTT client surfaces a transport-layer disconnect as 16#80A1, and the underlying TRCV reports 16#80C4 because the TCP socket was reset. They are two views of the same event. Look at the broker log and a Wireshark capture to determine whether the broker issued a keep-alive timeout, the firewall sent a RST, or the PLC issued an unsolicited TDISCON.

Do I need a TLS certificate when using port 1883?

No. Port 1883 is plaintext MQTT and no certificate is required. Certificates are only needed for port 8883 (MQTT over TLS) and for mutual TLS on top of 8883. The "do I need a certificate on 1883" question in the field reports is answered as: no, you do not, and the 16#80C4 / 16#80A1 errors are not caused by a missing certificate.

What is the recommended MQTT Keep-Alive for an S7-1500 deployment?

Start with 60 s. The PLC's OB1 cycle time must be significantly shorter than the Keep-Alive — a 200 ms OB1 cycle is comfortable. Set the Mosquitto broker idle_timeout to at least 90 s. For WAN / cellular deployments where the firewall ages out flows at 60 s, raise the firewall timeout to 600 s rather than increasing the MQTT Keep-Alive above 90 s.

How do I confirm the fix is working before signing off?

Run a 30-minute soak test: publish one message every 5 s from the PLC, subscribe from MQTT Explorer, and confirm that the broker log shows no Client disconnected lines, the TIA Portal diagnostic buffer shows no 16#80C4 or 16#80A1 events, and a Wireshark capture shows PINGREQ packets at the configured interval with no gaps > 1.5 × Keep-Alive.

Back to blog