Fondamentaux MQTT

Statut en ligne/hors ligne des appareils MQTT avec LWT et messages retenus

Un modèle de présence pas à pas pour les flottes MQTT : birth messages retenus, Last Will retenu, messages d'arrêt propre et un moniteur qui suit l'état de chaque appareil en temps réel.

Tableau de bord de flotte avec des voyants verts et gris indiquant le statut en ligne MQTT de chaque appareil
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

Savoir quels appareils sont connectés à l'instant présent est la première chose que demande tout opérateur, et le statut en ligne des appareils MQTT est étonnamment facile à mal implémenter. Le modèle fiable combine trois briques : un birth message retenu, un Last Will retenu et un message hors ligne explicite à l'arrêt. Dans ce guide, vous allez construire ce modèle pas à pas, ajouter un moniteur de flotte en Python et apprendre à éviter les cas limites qui font mentir les tableaux de bord.

Les briques de base

Chaque élément résout une partie du problème :

Brique Qui publie Quand Couvre
Birth message (online, retenu) L'appareil Juste après chaque connexion réussie L'appareil est actif, y compris après une reconnexion
Last Will (offline, retenu) Le broker, au nom de l'appareil Connexion perdue sans DISCONNECT Plantages, coupures de courant, pannes réseau
Message d'arrêt (offline, retenu) L'appareil Avant une déconnexion propre Redémarrages et arrêts planifiés
Flag retain sur les trois Tous Toujours Les tableaux de bord ouverts plus tard voient quand même l'état actuel

Omettez l'un d'eux et vous créez un angle mort. Sans birth message, un appareil qui se rétablit reste « hors ligne ». Sans will, les plantages passent inaperçus. Sans message d'arrêt, un arrêt planifié laisse l'appareil « en ligne » pour toujours, car un DISCONNECT propre indique au broker de supprimer le will. Si le mécanisme du will est nouveau pour vous, lisez d'abord MQTT Last Will and Testament.

Concevoir le topic de statut

Donnez à chaque appareil exactement un topic de statut, à un emplacement prévisible :

connectmq/demo/<prefix>/<site>/<device-id>/status

Un wildcard comme connectmq/demo/<prefix>/+/+/status renvoie alors le statut de toute la flotte en un seul abonnement. Comme tous les messages de statut sont retenus, un moniteur qui démarre reçoit immédiatement le dernier état connu de chaque appareil. Le fonctionnement de la rétention est expliqué dans les messages retenus MQTT.

Pour le payload, une simple chaîne online ou offline suffit pour de nombreux outils. Le JSON ajoute un contexte utile à l'exploitation :

{"state":"online","ts":1760600000,"fw":"2.4.1","ip":"10.0.4.17","reason":"boot"}

Pour le will, l'appareil ne peut pas connaître l'horodatage d'une panne future, alors restez simple : {"state":"offline","reason":"lwt"}. Le moniteur enregistre le moment où il le reçoit.

Étape 1 : l'appareil

Ce script paho-mqtt 2.x se comporte comme un appareil bien conçu. Il définit un will retenu avant de se connecter, publie un birth message retenu dans on_connect pour qu'il soit répété après chaque reconnexion, et publie un message hors ligne retenu lors d'un arrêt propre. Il se connecte au serveur MQTT public gratuit broker.connectmq.com, réservé aux tests et aux prototypes :

import json
import signal
import sys
import time
import paho.mqtt.client as mqtt

BROKER = "broker.connectmq.com"
DEVICE_ID = sys.argv[1] if len(sys.argv) > 1 else "pump-07"
STATUS = f"connectmq/demo/z8b2/plant1/{DEVICE_ID}/status"

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
                     client_id=f"z8b2-{DEVICE_ID}")
client.will_set(STATUS, json.dumps({"state": "offline", "reason": "lwt"}),
                qos=1, retain=True)
client.reconnect_delay_set(min_delay=1, max_delay=30)

def on_connect(client, userdata, flags, reason_code, properties):
    if reason_code.is_failure:
        print("connect failed:", reason_code)
        return
    birth = {"state": "online", "ts": int(time.time()), "reason": "connect"}
    client.publish(STATUS, json.dumps(birth), qos=1, retain=True)
    print("online")

def shutdown(signum, frame):
    bye = {"state": "offline", "ts": int(time.time()), "reason": "shutdown"}
    client.publish(STATUS, json.dumps(bye), qos=1, retain=True).wait_for_publish(timeout=5)
    client.disconnect()
    client.loop_stop()
    sys.exit(0)

client.on_connect = on_connect
signal.signal(signal.SIGINT, shutdown)
signal.signal(signal.SIGTERM, shutdown)

client.connect(BROKER, 1883, keepalive=20)
client.loop_start()
while True:
    time.sleep(1)  # la vraie télémétrie serait publiée ici

Lancez-le avec python device.py pump-07, puis dans un autre terminal avec python device.py fan-02.

Étape 2 : le moniteur de flotte

Le moniteur s'abonne à tous les topics de statut et tient un tableau des appareils. Comme les statuts sont retenus, il connaît l'état complet de la flotte quelques secondes après son démarrage :

import json
import time
import uuid
import paho.mqtt.client as mqtt

BROKER = "broker.connectmq.com"
FILTER = "connectmq/demo/z8b2/+/+/status"
fleet = {}

def on_connect(client, userdata, flags, reason_code, properties):
    client.subscribe(FILTER, qos=1)

def on_message(client, userdata, msg):
    device = msg.topic.split("/")[-2]
    if not msg.payload:  # payload retenu vide = appareil retiré
        fleet.pop(device, None)
        return
    try:
        state = json.loads(msg.payload).get("state", "unknown")
    except ValueError:
        state = msg.payload.decode(errors="replace")
    previous = fleet.get(device, {}).get("state")
    fleet[device] = {"state": state, "since": time.strftime("%H:%M:%S")}
    tag = "retained" if msg.retain else "live"
    if previous != state:
        print(f"[{tag}] {device}: {previous} -> {state}")

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
                     client_id=f"monitor-{uuid.uuid4().hex[:8]}")
client.on_connect = on_connect
client.on_message = on_message
client.connect(BROKER, 1883, keepalive=30)
client.loop_forever()

Étape 3 : tester chaque mode de défaillance

Lancez le moniteur et les deux appareils, puis parcourez cette check-list en vérifiant que le moniteur réagit correctement à chaque fois :

  1. Redémarrage du moniteur. Arrêtez puis relancez le moniteur. Les deux appareils apparaissent immédiatement online, marqués retained.
  2. Arrêt propre. Appuyez sur Ctrl+C dans fan-02. Le moniteur affiche aussitôt offline avec la raison shutdown.
  3. Plantage. Tuez pump-07 avec kill -9. Le système ferme le socket, le broker publie le will et le moniteur affiche offline.
  4. Perte réseau silencieuse. Coupez le réseau de l'appareil sans tuer le processus. Après environ 1,5 fois le keepalive de 20 secondes, le will se déclenche.
  5. Rétablissement. Rétablissez le réseau. paho se reconnecte, on_connect s'exécute et le birth message repasse l'appareil online.
  6. Mise hors service. Publiez un message retenu vide sur le topic de statut. Le moniteur retire l'appareil.

Vous pouvez aussi suivre les mêmes topics en direct dans un navigateur : le client intégré au navigateur et l'explorateur de topics du portail ConnectMQ affichent les changements de statut en temps réel lorsque vous dirigez vos appareils vers votre propre broker.

Les cas limites qui font mentir les tableaux de bord

Client ID en double. Deux appareils avec le même ID s'éjectent mutuellement. Chaque prise de contrôle peut déclencher un will et un birth, si bien que le statut oscille sans arrêt. Des ID uniques sont indispensables ; consultez le guide du client ID MQTT.

Birth publié une seule fois au démarrage. Si vous publiez online en dehors du callback de connexion, un appareil qui se reconnecte après une microcoupure réseau reste offline pour toujours. Publiez-le à chaque connexion.

Will non retenu. Un tableau de bord ouvert après la panne n'en apprend jamais l'existence.

Reconnexions rapides qui font clignoter le statut. Un appareil qui se reconnecte en quelques secondes génère quand même une paire hors ligne/en ligne. En MQTT 5, un will delay interval supprime le will si la session reprend à temps.

Keepalive trop long. Avec un keepalive de 300 secondes, un appareil mort paraît en ligne pendant plus de sept minutes. Choisissez un keepalive adapté à la rapidité avec laquelle vous devez être informé.

« Connecté » ne veut pas dire « en bonne santé ». Un appareil peut conserver sa connexion MQTT alors que son capteur est en panne. Publiez un champ de santé ou de heartbeat séparé si vous avez besoin d'une vivacité au niveau applicatif.

Présence ou heartbeat : de quoi avez-vous besoin ?

La présence basée sur la connexion et les heartbeats applicatifs répondent à des questions différentes, et beaucoup de flottes ont besoin des deux.

Signal Répond à Délai de détection Coût
Birth + LWT sur le topic de statut L'appareil est-il connecté au broker ? Immédiat à 1,5 x keepalive Quasi nul ; uniquement les pings keepalive
Message heartbeat périodique La boucle applicative tourne-t-elle encore ? Un ou deux intervalles de heartbeat Une petite publication par intervalle
Contrôle de fraîcheur de la télémétrie Le capteur produit-il encore des données ? Dépend de la fréquence de publication attendue Aucun sur l'appareil ; logique côté consommateur

Un appareil dont la boucle principale est bloquée peut continuer à répondre aux pings keepalive si le client MQTT tourne dans son propre thread : le statut de connexion reste alors online. Un heartbeat publié depuis la boucle principale, ou un consommateur qui signale les appareils dont la télémétrie n'est plus fraîche, permet de détecter ce cas. Gardez les heartbeats non retenus et d'une fréquence modérée ; une fois par minute suffit largement pour la plupart des supervisions.

Utiliser les topics de statut dans d'autres outils

Comme le modèle repose simplement sur des messages MQTT retenus, tout outil compatible MQTT peut l'exploiter sans intégration particulière. Dans Node-RED, un nœud mqtt in abonné au wildcard de statut alimente un tableau de bord ou un flux de notification. L'intégration MQTT de Home Assistant peut associer un topic de statut à un paramètre de disponibilité avec les payloads online et offline. L'entrée MQTT consumer de Telegraf peut écrire les changements d'état dans une base de séries temporelles pour le reporting de disponibilité. Chacun de ces outils se connecte au broker comme un client MQTT ordinaire : la seule exigence est de s'accorder sur les noms de topics et les valeurs de payload. Les chaînes simples online et offline sont le choix le plus portable si vous comptez utiliser des outils du marché ; le JSON apporte un contexte plus riche à votre propre code.

Statut côté broker sur ConnectMQ

Le modèle ci-dessus est portable vers n'importe quel broker. En complément, ConnectMQ publie l'état de connexion sur $SYS/broker/connection/{clientId}/state, auquel un moniteur peut s'abonner comme source indépendante qui ne dépend pas d'une configuration correcte du will dans le firmware de l'appareil. La vue des appareils du portail organise vos appareils selon les niveaux ISA-95, et des règles d'alerte à seuil peuvent vous avertir lorsque des valeurs dépassent des limites. En production, utilisez TLS sur le port 8883 avec YOUR_USERNAME, YOUR_PASSWORD et YOUR_CLIENT_ID issus du portail. Un broker MQTT managé fait tourner l'infrastructure de présence à votre place ; pour les fondamentaux, consultez le tutoriel MQTT.

Démarrer avec ConnectMQ

Créez un broker gratuit sur app.connectmq.com avec l'offre Serverless, sans carte bancaire. Déployez les scripts de l'appareil et du moniteur avec vos identifiants, et vous disposez en quelques minutes d'une présence de flotte fiable sur un broker MQTT cloud.

Questions fréquentes

Pourquoi mon appareil apparaît-il en ligne après avoir été arrêté ?

Un DISCONNECT propre supprime le Last Will. Publiez vous-même un message hors ligne retenu avant de vous déconnecter.

Pourquoi mon appareil reste-t-il hors ligne après sa reconnexion ?

Le birth message n'est probablement publié qu'une seule fois au démarrage. Publiez-le dans le callback de connexion pour que chaque reconnexion mette à jour le statut.

En combien de temps un appareil hors ligne est-il détecté ?

Immédiatement si la connexion TCP se ferme. Si l'appareil disparaît silencieusement, après environ 1,5 fois l'intervalle de keepalive.

Les messages de statut doivent-ils être retenus ?

Oui. Retenir les messages de birth, de will et d'arrêt garantit que tout client qui s'abonne plus tard reçoit immédiatement l'état actuel.

Articles liés

Qui a inventé MQTT ? L'histoire, des oléoducs à l'IoT

MQTT a été inventé en 1999 par Andy Stanford-Clark (IBM) et Arlen Nipper (Arcom) pour surveiller des oléoducs par satellite. Suivez son parcours vers l'open source, OASIS, l'ISO et les brokers MQTT managés d'aujourd'hui.

8 min de lecture

Pourquoi votre client MQTT se déconnecte sans cesse (et comment y remédier)

Les boucles de reconnexion sont l'un des problèmes MQTT les plus courants. Découvrez les cinq causes habituelles, des client ID en double aux timeouts de keep alive et aux paquets trop volumineux, et suivez une méthode de diagnostic qui identifie la vôtre.

9 min de lecture

Taille maximale d'un message MQTT : limites et stratégies pour les gros payloads

MQTT peut techniquement transporter un paquet de 256 Mo, mais les brokers, les bibliothèques clientes et les appareils imposent des limites bien plus basses. Découvrez où se trouvent les vraies limites, ce qui se passe quand on les dépasse et comment envoyer de gros payloads.

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.