I wanted to get other Arduino base clients (e.g. my SeeedStudio XiaoESP32S3) for Azure Event Grid MQTT Broker working (for MQTT 5 support) so installed the esp-mqtt-arduino library.
The library doesn’t support client authentication with certificates, so I added two methods setClientCert and setClientKey to the esp-mqtt-arduino.h and esp-mqtt-arduino.cpp files
class Mqtt5ClientESP32 {
public:
Mqtt5ClientESP32();
~Mqtt5ClientESP32();
//...
void useCrtBundle(bool enable = true);
void setCACert(const char* cert, size_t len = 0);
void setClientCert(const char* cert, size_t len = 0);
void setClientKey(const char* key, size_t len = 0);
void setInsecure(bool enable = true);
void setKeepAlive(uint16_t seconds);
private:
void Mqtt5ClientESP32::setClientCert(const char* cert, size_t len)
{
insecure_ = false;
cfg_.credentials.authentication.certificate = cert;
if (cert) {
cfg_.credentials.authentication.certificate_len = len ? len : strlen(cert) + 1;
} else {
cfg_.credentials.authentication.certificate_len = 0;
}
cfg_.broker.verification.skip_cert_common_name_check = false;
}
void Mqtt5ClientESP32::setClientKey(const char* key, size_t len)
{
insecure_ = false;
cfg_.credentials.authentication.key = key;
if (key) {
cfg_.credentials.authentication.key_len = len ? len : strlen(key) + 1;
} else {
cfg_.credentials.authentication.key_len = 0;
}
cfg_.broker.verification.skip_cert_common_name_check = false;
}
I had started with the basic_mqtt5_cert example stripping it back to the bare minimum hacking out all the certificate bundle support et.c
#include <WiFi.h>
#include <esp-mqtt-arduino.h>
#include <esp_log.h>
#include "sdkconfig.h"
#include "../secrets.h"
#include "../constants.h"
Mqtt5ClientESP32 mqtt;
volatile bool mqttReady = false;
volatile bool mqttSubscribed = false;
void setup() {
Serial.begin(9600);
delay(5000);
Serial.setDebugOutput(true);
Serial.println("[BOOT] Starting MQTT5 demo");
esp_log_level_set("*", ESP_LOG_INFO);
esp_log_level_set("MQTT_CLIENT", ESP_LOG_VERBOSE);
WiFi.onEvent([](WiFiEvent_t event, WiFiEventInfo_t info){
(void)info;
Serial.printf("[WiFi event] id=%d\n", event);
});
Serial.printf("[WiFi] Connecting to %s\n", WIFI_SSID);
WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
uint8_t attempts = 0;
while (WiFi.status() != WL_CONNECTED) {
Serial.printf("[WiFi] status=%d attempt=%u\n", WiFi.status(), attempts++);
delay(500);
}
Serial.print("[WiFi] Connected, IP: ");
Serial.println(WiFi.localIP());
// Sync time for TLS
Serial.println("\[NTP] synchronising");
configTime(0, 0, "pool.ntp.org", "time.nist.gov");
Serial.print("*");
while (time(nullptr) < 100000) {
delay(500);
Serial.print("*");
}
Serial.println("\[NTP] synchronised");
Serial.printf("[MQTT] Init broker %s as %s\n", MQTT_SERVER_URL,MQTT_CLIENTID);
mqtt.begin(MQTT_SERVER_URL, MQTT_CLIENTID);
mqtt.setKeepAlive(45);
mqtt.setCACert(CA_ROOT_PEM);
mqtt.setClientCert(CLIENT_CERT_PEM);
mqtt.setClientKey(CLIENT_KEY_PEM);
mqtt.setInsecure(false);
mqtt.onMessage([](const char* topic, size_t topic_len, const uint8_t* data, size_t len){
Serial.printf("[MSG] %.*s => %.*s\n", (int)topic_len, topic, (int)len, (const char*)data);
});
mqtt.onConnected([]{
Serial.println("[MQTT] Connected event");
mqttReady = true;
Serial.println("[MQTT] Subscribing to ssl/mqtt5");
if (mqtt.subscribe("ssl/mqtt5", 1, true)) {
Serial.println("[MQTT] Subscribe request sent");
} else {
Serial.println("[MQTT] Subscribe request failed");
}
});
mqtt.onDisconnected([]{
Serial.println("[MQTT] Disconnected event");
mqttReady = false;
});
Serial.println("[MQTT] Connecting...");
if (!mqtt.connect()) {
Serial.println("[MQTT] Connect start failed");
}
}
void loop() {
static unsigned long lastPublishMs = 0;
const unsigned long now = millis();
if (mqttReady && (now - lastPublishMs) >= 60000) {
const char* msg = "Hello from Arduino MQTT5 ESP32!";
Serial.println("[MQTT] Publishing demo message");
if (mqtt.publish(MQTT_TOPIC_PUBLISH, (const uint8_t*)msg, strlen(msg))) {
Serial.println("[MQTT] Publish queued (next in ~60s)");
} else {
Serial.println("[MQTT] Publish failed");
}
lastPublishMs = now;
}
delay(10);
}
It was important to put the setClientCert & setClient after the mqtt.begin because it resets the configuration
void Mqtt5ClientESP32::begin(const char* uri, const char* client_id,
const char* user, const char* pass, bool use_v5) {
connected_ = false;
insecure_ = false;
cfg_.broker.address.uri = uri;
if (client_id) cfg_.credentials.client_id = client_id;
if (user) cfg_.credentials.username = user;
if (pass) cfg_.credentials.authentication.password = pass;
cfg_.broker.verification.use_global_ca_store = false;
cfg_.broker.verification.certificate = nullptr;
cfg_.broker.verification.certificate_len = 0;
cfg_.broker.verification.skip_cert_common_name_check = false;
cfg_.session.last_will.topic = "devices/esp32/lwt";
cfg_.session.last_will.msg = "offline";
cfg_.session.last_will.qos = 1;
cfg_.session.last_will.retain = true;
cfg_.session.protocol_ver =
#if CONFIG_MQTT_PROTOCOL_5
use_v5 ? MQTT_PROTOCOL_V_5 : MQTT_PROTOCOL_V_3_1_1;
#else
MQTT_PROTOCOL_V_3_1_1;
(void)use_v5; // MQTT v5 support disabled at build time
#endif
}
I tried increasing the log levels to get more debugging information, adding delays on startup to make it easier to see what was going on, trying different options of protocol support.
After hours of trying I gave up.
