OpenPLC

OpenPLC

Open in ChatGPT
Ask ChatGPT about this page
Open in Claude
Ask Claude about this page

Troubleshooting

Troubleshooting

Arranged by what you are seeing, not by what is wrong.

It never connects

Read net.LAST_ERROR first. It carries the operating system's own words,
because no status code can say which of forty things went wrong at a socket.

what it says what it is
Connection refused nothing is listening on that host and port. Wrong port, or the broker is not running
Name or service not known DNS. Use an address, or fix the resolver
No route to host wrong subnet, or a firewall
Connection timed out a firewall dropping rather than refusing
that transport needs a board, not the Runtime TRANSPORT names an Arduino link on the Runtime
set HOST to the broker the pin is empty

If LAST_ERROR is empty and STATE sits at MQTT_CONNECTING, the socket
opened and the broker has not answered. That is a broker that accepted a
connection and then said nothing — check it is actually an MQTT broker on that
port, and not, say, a web server.

Check it from a desk first:

mosquitto_pub -h 10.0.0.20 -t test -m hello

If that fails too, the PLC is not the problem.

It connects, then drops, then connects again

MQTT_STATUS.RECONNECTS climbing is the symptom.

Another device has the same CLIENT_ID STATUS shows MQTT_SESSION_TAKEN_OVER. Two devices are displacing each other for ever. Check this first — it is the commonest cause and it looks healthy from either end
Keepalive too short for the link MQTT_KEEPALIVE_TIMEOUT. Raise KEEP_ALIVE on MQTT_CONFIG, especially on cellular
The broker is restarting its own log will say
A NAT or firewall dropping idle connections lower KEEP_ALIVE so traffic keeps the mapping alive. The opposite fix to the one above, which is why the log matters

It connects but nothing arrives

Check the filter against the topic. Wildcards are + for one level and
# for the rest, and # must be last. plant/level does not match
plant/intake/level.

Check something is publishing. From a desk:

mosquitto_sub -h 10.0.0.20 -t 'plant/#' -v

If that shows nothing, the problem is upstream of this PLC.

Check MQTT_SUBSCRIBE.SUBSCRIBED. False means the subscription is not
established — usually not connected, sometimes refused by an access rule, in
which case STATUS says MQTT_NOT_AUTHORIZED.

Check MALFORMED. Climbing means messages are arriving and none of them
parse as VALUE's type. A publisher sending 23,5 to a REAL, or JSON to a
number. Use MQTT_MESSAGE to see the raw text.

A value arrives but QUALITY is never GOOD

EXPECT is shorter than the publisher's own interval. Quality is
judged against the interval you asked for, so asking for a value every
second from something that publishes every minute is permanently overdue.

Set it to a little more than the publisher's PERIOD.

QUALITY is UNKNOWN and stays UNKNOWN

Nothing has ever arrived on that filter. This is a commissioning fault,
not a running one — a typo in the topic, a publisher never deployed, a
wildcard that does not match what you think. Take it to a desk with a drawing
rather than a torch to the plant.

Publishing says DONE but nobody sees it

MQTT_NO_MATCHING_SUBSCRIBERS on STATUS means the broker took it and
nobody was listening. The link is fine.

Check retain. A subscriber that connects afterwards hears nothing unless
the message was retained.

Check the prefix if a bridge is involved. REMOTE_PREFIX := 'sites/site7/'
means the far side sees sites/site7/plant/intake/level, not
plant/intake/level.

BUSY never clears

On MQTT_PUBLISH at QoS 1 or 2, BUSY stays until the broker acknowledges.
If it never does:

  • the link dropped mid-flight — STATUS becomes MQTT_CONNECTION_LOST
  • the broker's receive maximum is exhausted — MQTT_QUOTA_EXCEEDED_BY_PEER

On MQTT_REQUEST, BUSY clears at TIMEOUT with
MQTT_KEEPALIVE_TIMEOUT. If you left TIMEOUT at T#0s that is five seconds.

STATUS says MQTT_NOT_SUPPORTED_BY_PROTOCOL

You asked for something 3.1.1 cannot express, on a 3.1.1 connection. Check
MQTT_STATUS.VERSION — if it says 4, the fallback happened.

MQTT_REQUEST is the usual one: Response Topic and Correlation Data do not
exist before 5.0, and sending the request stripped would mean the responder
has nowhere to reply to. Refused rather than sent into the void.

A Will delay is the other. The node refuses to connect at 3.1.1 with a
delay set, rather than dropping it — a delay silently lost turns a flapping
link into a stream of false offline alarms, which is the exact failure the
delay was set to prevent.

STATUS says MQTT_WRONG_TASK

The block is in a different task from its client - or, for a SPARKPLUG_*
block, from its node; for MQTT_BROKER_USER, from its broker. It does nothing
until it is moved: OpenPLC runs each task on a thread of its own, and a block
in another task would be inside the client while the client's own scan is.

Move it into the network task, beside its client. Where the machine's program
needs the value, hand it over through a global (page 10, "Trading values with
the machine").

SPARKPLUG_NODE says "another layer owns the client's Will"

A connection carries one Will, and a SPARKPLUG_HOST or a second
SPARKPLUG_NODE on the same MQTT_CLIENT has it. Give the second one an
MQTT_CLIENT of its own.

An MQTT_PRESENCE on that client is the other way round: the node keeps the
Will and the presence block says MQTT_REFUSED - Sparkplug's birth and death
certificates are the presence.

Everything works but feels slow

Page 10. The node's task interval is almost always the answer.

It works on the Runtime and not on a board

Ethernet needs MQTT_ENABLE_ETHERNET, a define in the board package's
compiler flags. Page 03 explains why it is a define rather than automatic.

Cellular needs modbee-gsm, which is a separate library, and a GSM_MODEM in the
program. Until the modem has a data connection (GSM_MODEM.READY) the client
says why on LAST_ERROR and keeps retrying.

TLS on a board is the same on Wi-Fi, Ethernet and cellular on an ESP32 (page
03). It makes the same checks as the Runtime, and says why it refused in a
short fixed line. Where the TLS engine gave a code, it follows the line in
hexadecimal - TLS: broker refused us -0x7780 - and is mbedTLS's own error
number, for when the line is not enough.

LAST_ERROR on a board What to do
a board needs the CA in MQTT_CA_CERT_PEM MQTT_TLS_VERIFY with no CA defined. For the site's own broker use MQTT_TLS_PINNED with TRUST_NEW; for a signed one, add the CA to the board package
no pinned key yet - set TRUST_NEW once to learn it MQTT_TLS_PINNED on a link that has never connected
cannot connect to the broker nothing answered on HOST and PORT: the broker is down, the port is wrong, or the network is not up. On cellular the modem's own reason is shown instead when it has one
TLS: certificate not trusted MQTT_CA_CERT_PEM is not the CA that signed this broker - or the broker sends its certificate without the intermediate
TLS: wrong host name HOST is not a name on the certificate. Use the name it was issued to, not the address
TLS: bad certificate the broker's certificate failed some other check (its key usage, its signature) or would not read
TLS: not the pinned key the broker was replaced, or HOST is wrong. BROKER_KEY shows the key it presented. To accept a replacement, switch TRUST_NEW off and on
TLS: broker sent no cert pinned, and the broker sent no certificate to compare
TLS: broker refused us the broker ended the handshake. Most often it wants a client certificate (MQTT_CLIENT_CERT_PEM), or refuses the one sent
TLS: no common version/cipher the broker allows only TLS 1.3, or only cipher suites a board does not offer. Allow TLS 1.2 on the broker
TLS handshake timed out the handshake did not finish within the connect timeout: a slow or lossy link (cellular at the edge of coverage), or a broker that accepts and then says nothing. Raise CONNECT_TIMEOUT on MQTT_CONFIG for a slow link
TLS handshake failed any other handshake failure; the code says which
TLS: out of memory the board could not find the memory for a TLS session (about 40 kB, in PSRAM where the board has it)
TLS: CA certificate unreadable / TLS: client cert/key unusable MQTT_CA_CERT_PEM, or MQTT_CLIENT_CERT_PEM and MQTT_CLIENT_KEY_PEM, are not PEM, or a certificate came without its key
the last connect is still finishing the attempt before outlived CONNECT_TIMEOUT and its socket is still opening; the next attempt waits for it. Raise CONNECT_TIMEOUT on MQTT_CONFIG for a slow link
no memory for the connect task the board could not find the few kB of internal memory the connect runs in; it tries again after the backoff. Something else is holding the internal memory
broker closed the connection / TLS: connection dropped the connection ended after it was up: the broker closed it, or the network went
TLS over Ethernet needs an ESP32, or SSLClient / the modem's TLS checks no certificate a board without the ESP32's TLS engine: only what its medium offers

On the Runtime LAST_ERROR carries the TLS library's own words instead, and a
key that is not the pinned one names both fingerprints.

Check MQTT_ARDUINO is placed and that MAC and IP are set. A board
with no MAC cannot open an Ethernet link, and every board on a segment needs
its own.

It says not authorised

STATUS = MQTT_NOT_AUTHORIZED and LAST_ERROR reading "the broker refused
the connection: ..."
means the broker answered the CONNECT with a no. The TCP
connection worked; the credential did not.

Check USERNAME and PASSWORD on MQTT_CONFIG. They are separate pins
and either can be set without the other — which is legal MQTT and what a
token-based broker wants, and also what a typo looks like.

Check the block is placed. MQTT_CONFIG costs nothing until you place it,
so a program with no MQTT_CONFIG sends no credential at all and an
authenticating broker refuses it.

LAST_ERROR says "Not authorized" and not "Bad user name or password".
Those are two different reason codes and most brokers send the second only
when they can distinguish the cases. Mosquitto sends 0x87 Not authorized for
both, so the text does not tell you which of the two pins is wrong.

It will not retry, and that is deliberate. A password does not become
correct by itself, and a PLC repeating a rejected one every second is how a
site gets itself banned. Fix the pins and cycle CONNECT, or set
RETRY_REJECTED_CREDENTIAL := TRUE if the credential genuinely is expected to
become valid on its own. Page 03.

The broker block reports and does nothing

MQTT_BROKER runs on a board now, so this is no longer "it needs the Runtime".
Four things make it report instead:

No network library on the include path. WiFi comes with the ESP32 core;
Ethernet needs MQTT_ENABLE_ETHERNET, same as the node block.

CERT_FILE or KEY_FILE set on a board. They are paths, and a board has
no file system this block may assume. Clear them and let it mint its own, or
serve plain.

SECURITY other than MQTT_PLAIN with no wolfSSL on the include path.
Serving plain needs nothing installed; serving TLS needs an engine.

The port is already taken by another block or another program. LISTENING
stays false and the block retries every two seconds rather than giving up.

Page 11.

The bridge block reports and does nothing

MQTT_BRIDGE needs the Runtime, and that one is real: it is two simultaneous
connections, two sessions and two send queues, usually with TLS on one of them.
On a board that is most of the heap before the ladder logic gets any.

On the Runtime it refuses three things at start rather than half-applying them
— no rules, MQTT_TLS_PINNED on a block with no key pin, and a mapping that
would loop. Page 12 says what each means.

Next: 10 Scan rate

Last updated 2 hours ago
Was this helpful?
Thanks!