English | 简体中文
A thread-safe MQTT client for native ESP-IDF or Arduino ESP32. This library is compatible with arduino-esp32 v2/v3+ and ESP-IDF v4.x/v5.x C++.
- Features
- Non-Blocking Architecture
- Quick Start
- String Handling
- Required Global Callbacks
- Migration Guide from PubSubClient
- API Reference
- New Functions
- Building the ESP-IDF Example
- Background connection and event handling -
loopStart()returns immediately and noloop()polling call is needed - Thread-safe MQTT client based on the official
esp-mqttcomponent - TLS/SSL support for secure MQTT connections (port 8883)
- Uses standard C++
std::stringinstead of ArduinoString - Logging is performed using the standard ESP-IDF
ESP_LOGXmacros - Provides both specific topic subscriptions and a global "catch-all" message callback
- Interfaces inspired by EspMQTTClient
- CA cert support by dwolshin
- Arduino-esp32 v3+ support by dzungpv
loopStart() returns immediately. Connection, reconnection, and MQTT event handling
then run in the background task managed by esp-mqtt, so no loop() polling call is
required.
This does not mean that every API call is non-blocking. In particular, publish()
uses esp_mqtt_client_publish(), which can block while waiting for network access or
while sending a fragmented message. Message callbacks also run from the MQTT event
task and should therefore finish quickly.
#include <WiFi.h>
#include "ESP32MQTTClient.h"
ESP32MQTTClient mqttClient;
void onMqttConnect(esp_mqtt_client_handle_t client) {
if (mqttClient.isMyTurn(client)) {
mqttClient.subscribe("test/topic", [](const std::string &payload) {
Serial.printf("Received: %s\n", payload.c_str());
});
}
}
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
esp_err_t handleMQTT(esp_mqtt_event_handle_t event) {
mqttClient.onEventCallback(event);
return ESP_OK;
}
#else
void handleMQTT(void *handler_args, esp_event_base_t base,
int32_t event_id, void *event_data) {
(void)base;
(void)event_id;
auto *client = static_cast<ESP32MQTTClient *>(handler_args);
auto *event = static_cast<esp_mqtt_event_handle_t>(event_data);
client->onEventCallback(event);
}
#endif
void setup() {
Serial.begin(115200);
mqttClient.setURI("mqtt://broker.hivemq.com:1883");
mqttClient.setMqttClientName("ESP32_Client");
WiFi.begin("SSID", "PASSWORD");
mqttClient.loopStart(); // Returns immediately.
}
void loop() {
// Your code here - no need for mqttClient.loop()
delay(1000);
}// For TLS brokers like HiveMQ Cloud
const char* caCert = "-----BEGIN CERTIFICATE-----\n...";
void setup() {
// ... WiFi setup ...
mqttClient.setURL("broker.hivemq.cloud", 8883, "username", "password");
mqttClient.setCaCert(caCert); // Enable TLS verification
mqttClient.loopStart();
}This library uses std::string instead of Arduino String. Here's how to handle conversions:
// ✅ Correct - use std::string directly
std::string topic = "mytopic";
std::string payload = "Hello World";
mqttClient.publish(topic, payload, 0, false);
// ✅ Convert from Arduino String
String arduinoTopic = "mytopic";
String arduinoPayload = "Hello World";
mqttClient.publish(arduinoTopic.c_str(), arduinoPayload.c_str(), 0, false);
// ❌ This will cause compilation error
// String arduinoTopic = "mytopic";
// mqttClient.publish(arduinoTopic, payload, 0, false);Important: These callback functions must be global (not class methods, not lambdas):
// Required global callback for connection events
void onMqttConnect(esp_mqtt_client_handle_t client) {
if (mqttClient.isMyTurn(client)) {
// Subscribe to topics here
mqttClient.subscribe("test/topic", [](const std::string &payload) {
Serial.printf("Received: %s\n", payload.c_str());
});
}
}
// Required global event handler - ESP-IDF version dependent
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
esp_err_t handleMQTT(esp_mqtt_event_handle_t event) {
mqttClient.onEventCallback(event);
return ESP_OK;
}
#else
void handleMQTT(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data) {
(void)base;
(void)event_id;
auto *client = static_cast<ESP32MQTTClient *>(handler_args);
auto *event = static_cast<esp_mqtt_event_handle_t>(event_data);
client->onEventCallback(event);
}
#endifIf you're migrating from the popular PubSubClient library:
| PubSubClient | ESP32MQTTClient | Notes |
|---|---|---|
client.connect() |
mqttClient.loopStart() |
Connection startup returns immediately |
client.loop() |
Not needed | Runs automatically in background |
client.connected() |
mqttClient.isConnected() |
Check connection status |
client.setServer() + setCallback() |
mqttClient.setURL() + callbacks |
Similar setup pattern |
client.setBufferSize() |
mqttClient.setMaxPacketSize() |
Configure esp-mqtt buffer sizes |
Arduino String |
std::string |
Use .c_str() for conversion |
Before (PubSubClient):
PubSubClient client;
void setup() {
client.setServer("broker", 1883);
client.setCallback(messageCallback);
}
void loop() {
if (!client.connected()) {
client.connect("client"); // Blocks for 8+ seconds!
}
client.loop(); // Required in every loop
}After (ESP32MQTTClient):
ESP32MQTTClient mqttClient;
void setup() {
mqttClient.setURI("mqtt://broker:1883");
mqttClient.loopStart(); // Connection startup returns immediately.
}
void loop() {
// No mqttClient.loop() needed!
}Unless otherwise noted, configuration setters must be called before loopStart().
setURL(url, port, username, password)- Set broker connection detailssetURI(uri, username, password)- Set complete MQTT URIsetMqttClientName(name)- Set client IDsetCaCert(caCert)- Enable TLS with CA certificatesetClientCert(clientCert)- Set client certificatesetKey(clientKey)- Set client private keysetMaxPacketSize(size)→bool- Set the esp-mqtt incoming and outgoing buffer sizes (default: 1024 bytes). Complete incoming messages are reassembled up to 16 KiB by default; setting this above 16 KiB raises that reassembly limit to the requested size.setMaxOutPacketSize(size)→bool- Set the esp-mqtt outgoing buffer size (default: 1024 bytes)setKeepAlive(seconds)- Change keepalive interval (default: 120 seconds, the esp-mqtt default — the library does not set one itself)setTaskPrio(prio)- Set the priority of the MQTT background taskenablePersistence()- Request a persistent session from the broker (clean_session = 0)disablePersistence()- Do not request a persistent session (restores the default non-persistent behavior)enableLastWillMessage(topic, message, retain = false, qos = 0)- Set last will messagesetAutoReconnect(choice)- Enable/disable auto-reconnectdisableAutoReconnect()- Disable auto-reconnectenableDrasticResetOnConnectionFailures()- Restart the ESP32 when the MQTT connection is lost (#59)enableDebuggingMessages(enabled)- Enable debug loggingDEFAULT_PACKET_SIZE- Constant, the default buffer size in bytes (1024), used bysetMaxPacketSize()/setMaxOutPacketSize()
loopStart()- Start the background MQTT connection process and return immediatelyisConnected()- Check connection statusisMyTurn(client)- Check if event is for this clientgetClientName()→const char *- Get the configured client namegetURI()→const char *- Get the configured broker URIprintError(error_handle)- Log a decodedesp_mqtt_error_codes_terror
The bool returned by the methods below reports whether the request was accepted
by the local esp-mqtt client. It does not report a broker acknowledgement such as
PUBACK, SUBACK, or UNSUBACK.
If a subscribe or unsubscribe request cannot be submitted, the local callback
registration remains as it was before the call.
publish(topic, payload, qos, retain)→bool- Submit a message for publishing. The call may block; the fullstd::stringlength is used, so binary payloads containing embedded\0bytes are preserved.publish(topic, buffer, length, qos, retain)→bool- Submit a rawuint8_tbuffer with explicit length, for binary payloads (e.g. Protocol Buffers) without converting tostd::stringfirst. The call may block.subscribe(topic, callback, qos)→bool- Submit a subscription request with a payload callbacksubscribe(topic, callbackWithTopic, qos)→bool- Submit a subscription request with a topic+payload callbackunsubscribe(topic)→bool- Submit an unsubscribe requestsetOnMessageCallback(callback)- Set global message handler
Sets a global callback function that is invoked for any incoming message, regardless of the topic. This is useful for centralized logging or handling all messages in one place.
Example:
mqttClient.setOnMessageCallback([](const std::string &topic, const std::string &payload) {
ESP_LOGI("MAIN", "Global handler: %s: %s", topic.c_str(), payload.c_str());
});Enables or disables the automatic reconnection feature of the underlying ESP-IDF MQTT client. By default, auto-reconnect is enabled.
Example:
// Disable automatic reconnection
mqttClient.setAutoReconnect(false);The library includes a native ESP-IDF example in the examples/CppEspIdf directory. The top-level CMakeLists.txt registers the library as an ESP-IDF component using idf_component_register().
- Set up ESP-IDF: Ensure you have the ESP-IDF environment installed and configured.
- Configure Wi-Fi: Open
examples/CppEspIdf/main/main.cppand set your Wi-Fi SSID and password. - Build the project:
cd examples/CppEspIdf idf.py build