ArduinoMqttClient is the official MQTT library maintained by Arduino, and it runs on everything from the UNO R4 WiFi and Nano 33 IoT to MKR boards and ESP32. This guide walks through the ArduinoMqttClient API in the order you will use it: configure, connect, poll, publish, receive and set a will message. Every example connects to a real broker so you can test as you read.
Why Choose ArduinoMqttClient
ArduinoMqttClient wraps any Arduino Client object, such as WiFiClient, WiFiSSLClient, EthernetClient or a cellular client, and adds MQTT 3.1.1 on top. Its standout design choice is that both outgoing and incoming messages are handled as streams, so you never need a buffer the size of your largest message.
| Capability | ArduinoMqttClient |
|---|---|
| Protocol | MQTT 3.1.1 |
| Publish QoS | 0, 1 and 2 |
| Subscribe QoS | 0, 1 and 2 |
| Retained messages | Yes |
| Will message | Yes, streamed with beginWill() / endWill() |
| Payload handling | Streamed (print() / read()) |
| Event model | Polling with poll(), callback on message |
| Maintainer | Arduino |
If you already know PubSubClient, the PubSubClient Arduino tutorial is a useful comparison point; the two libraries solve the same problem with different trade-offs.
Installing the Library
Open Tools → Manage Libraries in the Arduino IDE, search for "ArduinoMqttClient" and install it. You also need the networking library for your board:
| Board | Network library | Plain client | TLS client |
|---|---|---|---|
| UNO R4 WiFi | WiFiS3 | WiFiClient |
WiFiSSLClient |
| Nano 33 IoT, MKR WiFi 1010 | WiFiNINA | WiFiClient |
WiFiSSLClient |
| ESP32 | WiFi (core) | WiFiClient |
WiFiClientSecure |
| Boards with Ethernet shield | Ethernet | EthernetClient |
Not built in |
Configuring the Client
Create the network client first, then pass it to MqttClient:
#include <WiFiNINA.h> // or WiFiS3.h, WiFi.h ...
#include <ArduinoMqttClient.h>
WiFiClient net;
MqttClient mqttClient(net);
Before calling connect(), set the session parameters:
mqttClient.setId("acm-51e09b"); // client ID, never empty
mqttClient.setUsernamePassword("YOUR_USERNAME", "YOUR_PASSWORD"); // optional
mqttClient.setKeepAliveInterval(30 * 1000L); // milliseconds!
mqttClient.setConnectionTimeout(10 * 1000L); // milliseconds
mqttClient.setCleanSession(true);
Note the units: unlike PubSubClient, keep alive and timeout values are in milliseconds. Passing 30 instead of 30000 becomes a keep alive of zero seconds after integer division, which disables keep alive entirely, so dead connections go unnoticed by the broker and your will message never fires.
Connecting and Reading connectError()
connect(host, port) returns 1 on success and 0 on failure. When it fails, connectError() tells you why:
| connectError() | Constant | Meaning and fix |
|---|---|---|
| -2 | MQTT_CONNECTION_REFUSED |
Socket could not open: Wi-Fi, DNS, port or TLS problem |
| -1 | MQTT_CONNECTION_TIMEOUT |
No CONNACK in time: wrong port type or poor link |
| 0 | MQTT_SUCCESS |
Connected |
| 1 | MQTT_UNACCEPTABLE_PROTOCOL_VERSION |
Broker rejected MQTT 3.1.1 (rare) |
| 2 | MQTT_IDENTIFIER_REJECTED |
Empty or disallowed client ID |
| 3 | MQTT_SERVER_UNAVAILABLE |
Broker not accepting connections now |
| 4 | MQTT_BAD_USER_NAME_OR_PASSWORD |
Credentials wrong |
| 5 | MQTT_NOT_AUTHORIZED |
Client not permitted, e.g. client ID does not match credentials |
A complete, reusable connection function:
bool connectMqtt(const char* host, int port) {
Serial.print("Connecting to ");
Serial.print(host);
Serial.print(':');
Serial.println(port);
if (!mqttClient.connect(host, port)) {
Serial.print("MQTT connect failed, connectError = ");
Serial.println(mqttClient.connectError());
return false;
}
Serial.println("MQTT connected");
return true;
}
For testing, use broker.connectmq.com on port 1883, the public MQTT test broker. It accepts anonymous connections with any unique, non-empty client ID. Keep your topics under a unique prefix such as connectmq/demo/<your-id>/ and publish nothing sensitive.
Why poll() Matters
poll() is the heartbeat of ArduinoMqttClient. Each call:
- Reads incoming bytes from the network client and parses complete packets.
- Calls your
onMessagecallback (or makes the message available toparseMessage()). - Answers QoS 1 and QoS 2 handshakes for incoming messages.
- Sends a PINGREQ when the keep alive interval is about to expire.
If poll() is not called for longer than the keep alive interval, the broker closes the connection. Avoid delay() calls longer than a fraction of your keep alive, and replace them with millis()-based timing:
unsigned long lastSend = 0;
void loop() {
mqttClient.poll();
if (millis() - lastSend >= 10000) {
lastSend = millis();
// publish here
}
}
Publishing Messages
Publishing is a three-step stream:
// Plain QoS 0 message
mqttClient.beginMessage("connectmq/demo/acm-51e09b/temperature");
mqttClient.print(22.7);
mqttClient.endMessage();
// Retained, QoS 1
mqttClient.beginMessage("connectmq/demo/acm-51e09b/status", true, 1);
mqttClient.print("online");
mqttClient.endMessage();
The full signature is beginMessage(topic, retain = false, qos = 0, dup = false). There is also an overload that takes the payload size up front, beginMessage(topic, size, retain, qos, dup). When you know the exact size, use it: the library can then write data straight to the network instead of buffering the whole message first, which matters for large payloads on boards with little RAM.
endMessage() returns 1 on success. For QoS 1 and 2, the library handles the acknowledgement packets during subsequent poll() calls.
Because print() accepts numbers, strings and floats, you can build JSON without a temporary buffer:
mqttClient.beginMessage(topicTelemetry);
mqttClient.print("{\"t\":");
mqttClient.print(temperature, 2);
mqttClient.print(",\"h\":");
mqttClient.print(humidity, 1);
mqttClient.print("}");
mqttClient.endMessage();
Receiving Messages
Subscribe after a successful connect, and again after every reconnect when you use a clean session:
mqttClient.onMessage(onMqttMessage);
mqttClient.subscribe("connectmq/demo/acm-51e09b/cmd/#", 1);
The callback receives only the message size. You read the topic and stream the payload:
void onMqttMessage(int messageSize) {
String topic = mqttClient.messageTopic();
Serial.print("Topic: ");
Serial.print(topic);
Serial.print(", QoS ");
Serial.print(mqttClient.messageQoS());
Serial.print(", retained ");
Serial.println(mqttClient.messageRetain());
char payload[128];
int n = 0;
while (mqttClient.available() && n < (int)sizeof(payload) - 1) {
payload[n++] = (char)mqttClient.read();
}
payload[n] = '\0';
Serial.println(payload);
}
Any bytes you do not read are discarded when the next message arrives. That makes oversized messages safe: you simply stop reading at your buffer limit.
As an alternative to the callback, you can call int size = mqttClient.parseMessage(); in loop() and handle messages inline when size is greater than zero. Use one style or the other, not both.
Will Messages
A will message (Last Will and Testament) is published by the broker if your device disconnects without saying goodbye, for example after a power cut or Wi-Fi loss. Configure it before connect():
const char willTopic[] = "connectmq/demo/acm-51e09b/status";
mqttClient.beginWill(willTopic, true, 1); // retained, QoS 1
mqttClient.print("offline");
mqttClient.endWill();
if (connectMqtt("broker.connectmq.com", 1883)) {
mqttClient.beginMessage(willTopic, true, 1);
mqttClient.print("online");
mqttClient.endMessage();
}
Publishing a retained online on the same topic right after connecting gives every dashboard an accurate presence indicator: online while connected, offline as soon as the broker detects the device is gone, which happens after roughly one and a half keep alive intervals.
A Complete Reconnect-Safe Pattern
Put the pieces together with this checklist:
- Connect Wi-Fi and wait for a valid IP address.
- Set ID, credentials, keep alive and will.
- Connect; on failure print
connectError()and retry after a delay that grows with each failure. - On success, register
onMessage, subscribe, and publishonlineretained. - In
loop(), check Wi-Fi, checkmqttClient.connected(), callpoll(), and publish on amillis()timer.
Board-specific versions of this pattern are in the Arduino UNO R4 WiFi MQTT tutorial and the Nano 33 IoT MQTT guide with WiFiNINA. For a broader project, the ESP32 IoT tutorial shows how the device side fits with dashboards and alerts.
Common ArduinoMqttClient Mistakes
- Calling
beginWill()afterconnect(). The will is part of the CONNECT packet, so it must be configured first. - Subscribing only in
setup(). After a reconnect with a clean session, the broker has forgotten your subscriptions. Subscribe in your connect function instead. - Reusing one client ID on several boards. The broker disconnects the older session every time another board connects, producing an endless reconnect loop.
- Publishing from inside
onMessage. It works, but finish reading the incoming payload first; otherwise the unread bytes are lost. - Forgetting
endMessage(). Without it nothing is sent, and the nextbeginMessage()call fails.
Production: TLS and Credentials
For real deployments, switch to a TLS client and port 8883 and authenticate with your own credentials. A managed MQTT broker such as ConnectMQ issues a username, password and client ID for each broker you create in the portal; the client ID is bound to those credentials, so set it exactly with setId(). ConnectMQ's certificate comes from Let's Encrypt, which the Wi-Fi modules on current Arduino boards trust out of the box. Review MQTT security best practices before you ship.
Get Started with ConnectMQ
ConnectMQ is a cloud MQTT broker with MQTT 3.1.1 and 5.0, TLS and WebSockets, plus a portal that includes an in-browser MQTT client, live topic explorer and threshold alerts. It is the easiest way to see what your ArduinoMqttClient sketch is really sending. Create a free Serverless broker at app.connectmq.com, no credit card required.
Frequently Asked Questions
Is ArduinoMqttClient better than PubSubClient?
Neither is universally better. ArduinoMqttClient supports QoS 1 and 2 publishing and streams payloads, while PubSubClient is smaller and has a huge base of examples. Pick ArduinoMqttClient for official Arduino boards and QoS needs.
Does ArduinoMqttClient support MQTT 5?
No, it implements MQTT 3.1.1. Modern brokers serve 3.1.1 and 5.0 clients side by side, so your device can still share topics with MQTT 5 applications.
Why does my ArduinoMqttClient connection drop every minute?
Usually poll() is not called often enough, the keep alive was set in seconds instead of milliseconds, or another client uses the same client ID.
What is the maximum message size?
The library streams payloads, so the limit is mostly your broker's. The public broker at broker.connectmq.com accepts up to 64 KB per message; the free Serverless plan allows 256 KB.



