MQTT managé dans le cloud

Connecter un ESP32 à un broker MQTT cloud en TLS

Guide pas à pas pour faire passer un projet ESP32 d'un broker Mosquitto local à un broker MQTT cloud en TLS, avec nom d'utilisateur, mot de passe et certificat validé.

Illustration d'un broker MQTT cloud ESP32 montrant une carte ESP32 qui envoie de la télémétrie chiffrée en TLS vers un broker cloud
Essayez-le sur votre propre broker MQTTObtenez un broker MQTT privé et managé avec l’offre Free et partez d’exemples de code prêts à l’emploi.
Essayer gratuitement Exemples de code Aucune carte bancaire requise

Connecter un ESP32 à un broker MQTT cloud en TLS, c'est l'étape qui transforme un prototype de paillasse en un appareil déployable en dehors de votre réseau domestique. Dans ce tutoriel, vous allez faire passer un sketch ESP32 d'un broker Mosquitto local sur le port 1883 à un broker MQTT managé ConnectMQ Serverless sur le port 8883, avec une vraie validation de certificat, des identifiants, un topic de statut et des reconnexions fiables.

Ce qui change quand vous quittez le broker local

La plupart des projets ESP32 commencent avec Mosquitto sur un Raspberry Pi ou un ordinateur portable : TCP en clair sur le port 1883, sans authentification, accessible uniquement sur votre réseau local. Passer à un broker MQTT cloud change quatre choses :

Aspect Broker local Broker cloud
Hôte 192.168.1.20 broker.connectmq.com
Port 1883 (TCP en clair) 8883 (TLS)
Authentification Souvent aucune Nom d'utilisateur, mot de passe, client ID
Client réseau WiFiClient WiFiClientSecure
Certificat Aucun Certificat serveur validé par une CA
Accessibilité Réseau local uniquement Partout avec Internet

La logique MQTT elle-même (topics, publications et abonnements) reste identique. Si vous n'avez encore jamais connecté un ESP32 à MQTT, commencez par le tutoriel ESP32 MQTT puis revenez ici.

Prérequis

  • Une carte de développement ESP32 et un câble USB.
  • Arduino IDE 2.x ou PlatformIO avec le core Arduino pour ESP32 installé (voir la documentation officielle du core Arduino d'Espressif).
  • La bibliothèque PubSubClient de Nick O'Leary.
  • Un broker Serverless gratuit. Inscrivez-vous sur le portail, choisissez le plan Serverless (Free), créez un broker et copiez le nom d'utilisateur, le mot de passe et le client ID générés.

Étape 1 : récupérer le certificat de la CA racine

Le broker présente un certificat Let's Encrypt. L'ESP32 doit faire confiance à la racine qui a signé la chaîne. La chaîne Let's Encrypt remonte à ISRG Root X1 : c'est donc ce certificat qu'il faut embarquer. Vous pouvez vérifier la chaîne vous-même depuis n'importe quelle machine avec OpenSSL :

openssl s_client -connect broker.connectmq.com:8883 -servername broker.connectmq.com -showcerts </dev/null

Regardez les lignes issuer en haut de la sortie. Téléchargez ensuite ISRG Root X1 au format PEM depuis la page des certificats de Let's Encrypt et collez-le dans votre sketch sous forme de chaîne brute. Embarquer la racine, valable longtemps, plutôt que le certificat propre du serveur vous évite de reflasher les appareils chaque fois que le certificat serveur est renouvelé, soit tous les quelques mois.

Étape 2 : le sketch complet

Ce sketch se connecte au Wi-Fi, synchronise l'heure (la validation des certificats TLS exige une horloge correcte), se connecte au broker en TLS avec des identifiants, publie un statut online retained avec un Last Will, s'abonne à un topic de commandes et publie de la télémétrie toutes les dix secondes.

#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <PubSubClient.h>
#include <time.h>

// ---- Wi-Fi ----
const char* WIFI_SSID = "YOUR_WIFI_SSID";
const char* WIFI_PASS = "YOUR_WIFI_PASSWORD";

// ---- ConnectMQ ----
const char* MQTT_HOST = "broker.connectmq.com";
const uint16_t MQTT_PORT = 8883;
const char* MQTT_USER = "YOUR_USERNAME";
const char* MQTT_PASS = "YOUR_PASSWORD";
const char* MQTT_CLIENT_ID = "YOUR_CLIENT_ID";

const char* TOPIC_TELEMETRY = "devices/esp32-01/telemetry";
const char* TOPIC_STATUS    = "devices/esp32-01/status";
const char* TOPIC_COMMAND   = "devices/esp32-01/cmd";

// ISRG Root X1 (collez ici le PEM complet de Let's Encrypt)
static const char ROOT_CA[] PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
PASTE_ISRG_ROOT_X1_PEM_CONTENT_HERE
-----END CERTIFICATE-----
)EOF";

WiFiClientSecure net;
PubSubClient mqtt(net);

unsigned long lastPublish = 0;
unsigned long lastReconnectAttempt = 0;
unsigned long reconnectDelay = 1000;

void connectWiFi() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  Serial.print("Connecting to Wi-Fi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.printf("\nWi-Fi connected, IP %s\n", WiFi.localIP().toString().c_str());
}

void syncTime() {
  configTime(0, 0, "pool.ntp.org", "time.google.com");
  Serial.print("Syncing time");
  time_t now = time(nullptr);
  while (now < 1700000000) {
    delay(500);
    Serial.print(".");
    now = time(nullptr);
  }
  Serial.println(" done");
}

void onMessage(char* topic, byte* payload, unsigned int length) {
  String msg;
  for (unsigned int i = 0; i < length; i++) msg += (char)payload[i];
  Serial.printf("Command on %s: %s\n", topic, msg.c_str());
  if (msg == "led:on")  digitalWrite(LED_BUILTIN, HIGH);
  if (msg == "led:off") digitalWrite(LED_BUILTIN, LOW);
}

bool connectMqtt() {
  Serial.print("Connecting to ConnectMQ... ");
  bool ok = mqtt.connect(MQTT_CLIENT_ID, MQTT_USER, MQTT_PASS,
                         TOPIC_STATUS, 1, true, "offline");
  if (!ok) {
    Serial.printf("failed, state=%d\n", mqtt.state());
    return false;
  }
  Serial.println("connected");
  mqtt.publish(TOPIC_STATUS, "online", true);
  mqtt.subscribe(TOPIC_COMMAND, 1);
  return true;
}

void setup() {
  Serial.begin(115200);
  pinMode(LED_BUILTIN, OUTPUT);
  connectWiFi();
  syncTime();

  net.setCACert(ROOT_CA);
  mqtt.setServer(MQTT_HOST, MQTT_PORT);
  mqtt.setCallback(onMessage);
  mqtt.setKeepAlive(30);
  mqtt.setBufferSize(512);
}

void loop() {
  if (WiFi.status() != WL_CONNECTED) {
    connectWiFi();
  }

  if (!mqtt.connected()) {
    unsigned long now = millis();
    if (now - lastReconnectAttempt > reconnectDelay) {
      lastReconnectAttempt = now;
      if (connectMqtt()) {
        reconnectDelay = 1000;
      } else {
        reconnectDelay = min(reconnectDelay * 2, 60000UL);
      }
    }
    return;
  }

  mqtt.loop();

  if (millis() - lastPublish > 10000) {
    lastPublish = millis();
    char payload[128];
    snprintf(payload, sizeof(payload),
             "{\"uptime_s\":%lu,\"rssi\":%d,\"heap\":%u}",
             millis() / 1000, WiFi.RSSI(), ESP.getFreeHeap());
    mqtt.publish(TOPIC_TELEMETRY, payload);
    Serial.println(payload);
  }
}

Quelques explications sur les choix effectués :

  • Statut retained et Last Will. Le broker publie offline à votre place si l'ESP32 décroche sans déconnexion propre, et les nouveaux abonnés voient toujours le dernier statut. Ce modèle est détaillé dans ESP32 MQTT Last Will.
  • Backoff non bloquant. Le délai entre les tentatives de reconnexion double d'une seconde jusqu'à une minute, pour qu'une flotte ne martèle pas le broker après une panne.
  • Taille du buffer. Le buffer par défaut de 256 octets de PubSubClient rejette silencieusement les messages plus gros ; 512 octets laissent de la marge.
  • Certaines cartes n'ont pas LED_BUILTIN. Définissez-le avec la broche de la LED de votre carte, souvent GPIO 2.

Étape 3 : vérifier depuis votre ordinateur

Ouvrez le client intégré au navigateur ou l'explorateur de topics du portail et abonnez-vous à devices/#. Vous devriez voir la télémétrie toutes les dix secondes et online en retained sur le topic de statut. Pour envoyer une commande en ligne de commande :

mosquitto_pub -h broker.connectmq.com -p 8883 --capath /etc/ssl/certs \
  -u YOUR_USERNAME -P YOUR_PASSWORD -i YOUR_CLIENT_ID-cli \
  -t devices/esp32-01/cmd -m 'led:on' -q 1

Utilisez un client ID différent de celui de l'ESP32. Si votre plan lie un seul client ID aux identifiants, utilisez plutôt le client intégré au navigateur du portail, ou créez des identifiants supplémentaires pour vos outils.

Tester sans compte

Pour essayer le sketch avant de vous inscrire, utilisez le serveur MQTT public gratuit, prévu pour les tests et les prototypes : gardez le port 8883 et la même CA racine, passez un nom d'utilisateur et un mot de passe vides (mqtt.connect(clientId)), choisissez n'importe quel client ID unique et non vide, et préfixez chaque topic par une valeur unique comme connectmq/demo/x8q2m/esp32-01/.... L'espace public est lisible par tout le monde : ne publiez rien de sensible ni aucune donnée de production, et notez ses limites de 64 Ko par message et de 10 messages par seconde par client.

Dépannage

Symptôme Cause probable Solution
state=-2 immédiatement Échec de la négociation TLS Vérifiez la CA racine, la synchronisation de l'heure et le port 8883
Blocage pendant la synchronisation de l'heure NTP bloqué Autorisez l'UDP 123 ou utilisez un serveur NTP accessible
state=4 Nom d'utilisateur ou mot de passe incorrect Recopiez les identifiants depuis le portail
state=5 Non autorisé Le client ID ne correspond pas aux identifiants émis
Se connecte puis décroche en boucle Client ID dupliqué Vérifiez que chaque appareil et chaque outil utilise son propre ID
Les gros messages n'arrivent jamais Buffer de PubSubClient trop petit Augmentez setBufferSize
Fonctionne à la maison, échoue sur site Port 8883 sortant bloqué Demandez à l'informatique d'ouvrir le 8883, ou utilisez WSS sur 8084 avec un client MQTT over WebSocket

Pendant le développement uniquement, net.setInsecure() désactive la validation du certificat et aide à déterminer si la CA est en cause. Ne livrez jamais de firmware avec cette option. Pour les problèmes TLS plus poussés, le guide ESP32 MQTT SSL va plus loin.

Mémoire, consommation et cycle de vie des certificats

Le TLS sur un ESP32 est confortable mais pas gratuit, et quelques choix de conception évitent des ennuis par la suite.

Utilisation du tas (heap). Une session TLS nécessite une part non négligeable de RAM pour les buffers et la négociation. Sur un ESP32 standard, cela ne pose pas de problème pour une connexion, mais si votre sketch fait aussi tourner un serveur web, une caméra ou de gros documents JSON, surveillez ESP.getFreeHeap() dans le payload de télémétrie ci-dessus. Une valeur qui baisse régulièrement signale généralement une fuite dans votre propre code, comme une concaténation de String dans une boucle, plutôt que dans la pile TLS.

Gardez la connexion ouverte. C'est la négociation qui coûte cher. Se connecter, publier un message et se déconnecter toutes les quelques secondes consomme bien plus d'énergie et de temps que de maintenir une session ouverte avec un keep alive de 30 à 60 secondes.

Appareils sur batterie. Si la carte passe en deep sleep entre deux mesures, chaque réveil paie l'association Wi-Fi, le NTP et une négociation TLS. Réduisez ce coût en vous réveillant moins souvent et en regroupant plusieurs mesures dans une seule publication, et envisagez de stocker la dernière heure NTP en mémoire RTC pour éviter une synchronisation complète lors des sommeils courts.

Expiration du certificat racine. Embarquer la CA racine plutôt que le certificat feuille du serveur signifie que les renouvellements de routine ne touchent jamais vos appareils. Les racines finissent toutefois par changer : prévoyez donc un moyen de mettre à jour le certificat sur le terrain, soit par des mises à jour de firmware OTA, soit en stockant le PEM en flash, où un processus de provisionnement pourra le remplacer.

Checklist de mise en production

  1. Stockez les identifiants dans la NVS ou provisionnez-les en fabrication, pas dans le gestionnaire de versions.
  2. Donnez à chaque appareil son propre client ID.
  3. Conservez la CA racine plutôt qu'un certificat feuille.
  4. Utilisez un backoff de reconnexion avec jitter.
  5. Publiez un statut retained avec un Last Will.
  6. Gardez des payloads petits et structurés.
  7. Surveillez les appareils dans le tableau de bord du portail et définissez des règles d'alerte à seuil pour les indicateurs clés.

La vue d'ensemble de la sécurité se trouve dans les bonnes pratiques de sécurité MQTT.

Connectez votre ESP32 à ConnectMQ

Créez un broker Serverless gratuit sur app.connectmq.com, sans carte bancaire, collez les identifiants dans le sketch ci-dessus, et votre ESP32 communique avec le cloud en TLS. ConnectMQ est un broker MQTT managé : les certificats, les correctifs et la capacité sont gérés pour vous pendant que vous vous concentrez sur le firmware.

Questions fréquentes

Pourquoi mon ESP32 a-t-il besoin de l'heure exacte pour le TLS ?

La validation des certificats vérifie leurs dates de validité. Sans NTP, l'horloge de l'ESP32 démarre en 1970, tous les certificats paraissent invalides et la négociation échoue.

Puis-je utiliser AsyncMqttClient au lieu de PubSubClient ?

Oui. AsyncMqttClient est non bloquant et prend en charge TLS selon votre core et vos options de compilation, tandis que PubSubClient avec WiFiClientSecure reste la configuration la plus simple et la plus répandue.

Quel certificat racine dois-je embarquer pour ConnectMQ ?

ISRG Root X1, la racine de Let's Encrypt. Elle a une longue durée de vie : les renouvellements du certificat serveur n'exigent donc pas de mise à jour du firmware.

Le TLS ralentit-il sensiblement l'ESP32 ?

La négociation prend un instant et utilise de la RAM supplémentaire, mais une fois connecté, le surcoût par message est faible. Gardez la connexion ouverte au lieu de vous reconnecter à chaque publication.

Articles liés

Broker MQTT BYOC : le modèle Bring Your Own Cloud expliqué

Un broker MQTT BYOC tourne dans votre compte cloud mais est exploité par un fournisseur. Découvrez le fonctionnement du modèle, la répartition des responsabilités et comment préparer votre compte.

9 min de lecture

Messages MQTT par seconde : comment estimer et mesurer le débit

La planification du débit MQTT se résume à quelques formules et à une mesure honnête. Découvrez comment estimer les messages entrants et sortants par seconde, tenir compte des pics et faire correspondre le résultat à une offre de broker.

8 min de lecture

Tous les articles →

Votre broker MQTT privé, à quelques clics

Créez un compte, récupérez les identifiants de votre broker et connectez votre premier appareil — aucun serveur à installer, mettre à jour ou dimensionner.