Un tableau de bord se connecte à un broker MQTT à 08 h 00 et affiche 42 kW pour un compteur. Le compteur a pourtant perdu son alimentation à 18 h 00 la veille. Le broker a envoyé au tableau de bord le dernier message conservé pour le sujet du compteur, et rien dans le paquet MQTT n’indiquait que cette valeur datait de 14 heures.
Ce guide explique ce que prouve chaque réglage de livraison MQTT : les trois niveaux de QoS, les messages conservés, les sessions persistantes, le testament et l’expiration des messages. Les numéros de section renvoient à la norme OASIS MQTT 5.0. Les différences avec MQTT 3.1.1 sont signalées.
Ce guide fait partie de la série OPC UA et MQTT, qui commence par OPC UA, MQTT et Modbus : comparaison. Pour TLS, les certificats et les droits d’accès aux sujets, consultez le guide MQTTS.
Les trois niveaux de QoS
La QoS (qualité de service) définit l’échange d’accusés de réception sur chaque segment. La section 4.3 de la norme définit trois niveaux :
| QoS | Nom | Paquets par message sur un segment | Résultat sur ce segment |
|---|---|---|---|
| 0 | Au plus une fois | PUBLISH | Livré une fois ou perdu. Aucun accusé de réception. |
| 1 | Au moins une fois | PUBLISH, PUBACK | Livré, mais peut arriver plusieurs fois. |
| 2 | Exactement une fois | PUBLISH, PUBREC, PUBREL, PUBCOMP | Livré une fois, sans doublon sur ce segment. |
La QoS s’applique à chaque segment
Un message traverse deux segments : de l’éditeur au broker, puis du broker à chaque abonné. La QoS s’applique séparément à chacun. Le broker livre au niveau le plus bas entre la QoS du PUBLISH et la QoS maximale accordée à l’abonnement. Une mesure publiée en QoS 2 vers un abonnement QoS 0 arrive en QoS 0.
Un PUBACK est la réponse à un PUBLISH QoS 1 sur un seul saut ; sa réception ne prouve pas à elle seule que le message a été accepté. Il ne prouve pas qu’un abonné l’a reçu ni qu’une application l’a enregistré. En MQTT 5, le PUBACK porte un code de motif (section 3.4.2.1). Les codes 0x00 Success et 0x10 No matching subscribers indiquent tous deux l’acceptation. Un broker peut envoyer 0x10 s’il sait qu’aucun client n’est abonné au sujet, mais il n’y est pas obligé : 0x00 ne prouve donc pas qu’un abonné existe. Les codes à partir de 0x80 rejettent le message, par exemple 0x87 Not authorized et 0x97 Quota exceeded. Un éditeur qui considère tous les PUBACK comme des réussites perd ces mesures sans signaler d’erreur.
MQTT 3.1.1 n’a pas de codes de motif. Un broker qui refuse une publication doit soit l’accuser réception normalement, soit fermer la connexion (section 3.3.5 de la version 3.1.1). Une publication dont la réception est accusée peut donc tout de même avoir été refusée.
Choisir un niveau pour les mesures
| Données | Choix habituel |
|---|---|
| Mesures fréquentes pour lesquelles la perte d’un échantillon est acceptable | QoS 0 |
| Mesures d’énergie, compteurs et événements devant parvenir à destination | QoS 1, avec suppression des doublons par le récepteur |
| Commandes et transactions uniques | QoS 1 ou 2, une date d’expiration de la commande et un accusé de réception au niveau de l’application |
La QoS 1 est le choix habituel pour les données énergétiques. Elle demande deux paquets par message sur chaque segment, contre quatre pour la QoS 2. Certains services ne proposent pas la QoS 2 : AWS IoT Core ne prend en charge que la QoS 0 et 1.
Donnez à chaque commande une date d’expiration en plus de sa QoS. Une commande mise en file d’attente pendant une panne est envoyée quand le client se reconnecte. Un intervalle d’expiration de message MQTT 5 (section 3.3.2.3.3), ou une date limite de validité dans la charge utile, empêche la consigne de la veille d’atteindre l’installation le lendemain matin.
Doublons
En QoS 1, l’émetteur renvoie chaque PUBLISH non acquitté lors de la perte de connexion (section 4.4). Si le récepteur avait déjà traité le premier exemplaire, il reçoit la mesure deux fois.
Les champs du protocole n’identifient pas le doublon. Le broker positionne le drapeau DUP pour ses propres nouvelles tentatives sur le segment sortant ; il ne retransmet pas le drapeau DUP reçu (section 3.3.1.1). L’identifiant de paquet appartient à un seul segment et l’émetteur peut le réutiliser dès l’arrivée du PUBACK (section 2.2.1).
Supprimez les doublons à l’aide d’une clé applicative : identité de l’appareil, canal et horodatage de la mesure, ou numéro de séquence incrémenté par la source pour chaque mesure. Par exemple :
{"device":"meter-12","channel":"p_total","ts":"2026-09-23T14:05:00Z","seq":48213,"value":42.0,"unit":"kW"}Un récepteur qui enregistre les mesures avec une clé unique formée de l’appareil, du canal et de ts écarte le deuxième exemplaire. Écrivez l’horodatage en UTC, sous forme de texte RFC 3339 ou de millisecondes depuis l’époque Unix, et synchronisez l’horloge de la source avec NTP. Le guide des horodatages explique la différence entre l’heure de mesure et l’heure d’arrivée.
Messages conservés
Quand un PUBLISH porte le drapeau retain, le broker l’enregistre comme dernière valeur de ce sujet (section 3.3.1.3). Il conserve un message par sujet. Un nouveau message conservé remplace l’ancien. Un message conservé avec une charge utile vide le supprime. Si le message conservé a été publié en QoS 0, le broker peut l’écarter à tout moment.
Le broker envoie le message conservé à tout nouvel abonnement correspondant au sujet, avec le drapeau retain. Les messages arrivant ensuite d’un éditeur actif atteignent l’abonné avec ce drapeau effacé. Un abonné peut donc distinguer une valeur enregistrée d’une valeur reçue en direct. Le drapeau ne dit rien de l’âge de la valeur enregistrée. En MQTT 5, un abonnement avec Retain As Published = 1 reçoit le drapeau tel que l’éditeur l’a défini. Les ponts utilisent cette option : un abonné derrière un pont ne peut donc pas se fier au drapeau.
En MQTT 5, l’abonné choisit aussi s’il reçoit des messages conservés (section 3.8.3.1). Retain Handling 0 les envoie à chaque abonnement, 1 seulement lors d’un nouvel abonnement et 2 jamais. MQTT 3.1.1 ne propose pas cette option.
Conservez les états qu’un nouvel abonné doit connaître immédiatement, par exemple la configuration ou l’état de connexion d’un appareil. Conserver les mesures pose le problème présenté en introduction. Trois mesures l’évitent :
- Incluez l’horodatage de mesure dans chaque charge utile et demandez à l’abonné de le vérifier. Marquez une mesure comme périmée après un nombre défini d’intervalles manqués. Trois intervalles donnent 3 minutes pour un compteur qui transmet toutes les minutes.
- Définissez un intervalle d’expiration MQTT 5 égal au seuil de péremption, par exemple 180 s. Une fois ce délai écoulé, le broker écarte le message, y compris s’il était conservé. Il réduit également l’intervalle du temps passé en attente, afin que l’abonné puisse connaître la durée restante. MQTT 3.1.1 ne prévoit pas d’expiration de message : la vérification de l’horodatage constitue alors la seule protection.
- Publiez les mesures sans le drapeau retain et ne conservez qu’un sujet d’état.
Sessions et testament
Une session persistante conserve les abonnements d’un client lorsqu’il est déconnecté. Le broker met en file d’attente les messages QoS 1 et QoS 2 qui lui sont destinés et renvoie les messages non acquittés lors de la perte de connexion. La norme laisse facultative la mise en file des messages QoS 0 (section 4.1).
En MQTT 5, le client se connecte avec Clean Start = 0 et un Session Expiry Interval supérieur à 0, par exemple 86 400 s pour un jour. Clean Start = 1 supprime l’ancienne session. Si Session Expiry Interval vaut 0 ou n’est pas indiqué, la session se termine à la fermeture de la connexion (section 3.1.2.11.2). En MQTT 3.1.1, le client se connecte avec Clean Session = 0 et le protocole ne définit pas de date d’expiration de session. Dans les deux versions, le drapeau Session Present du CONNACK indique au client si le broker conservait encore sa session.
La file d’attente du broker est limitée. Mosquitto conserve jusqu’à 1 000 messages QoS 1 et 2 par client en plus des messages en transit (max_queued_messages) et écarte ceux qui dépassent cette limite. Par défaut, il ne met pas en file les messages QoS 0 destinés à un client déconnecté (queue_qos0_messages false). Un abonné recevant 6 mesures par minute remplit une file de 1 000 messages en moins de 3 heures. Dimensionnez la file selon le débit des messages et la durée maximale d’indisponibilité prévue, ou conservez l’historique chez l’éditeur.
Le testament est un message que le broker publie au nom du client si sa connexion s’achève sans DISCONNECT normal : panne réseau, keepalive manqué ou fermeture de socket (section 3.1.2.5). Un sujet d’état l’utilise ainsi :
- La passerelle se connecte avec un testament « offline » sur son sujet d’état, avec Will Retain = 1.
- Une fois connectée, elle publie « online » sur le même sujet avec le drapeau retain.
- Avant un arrêt planifié, elle publie « offline » avec le drapeau retain, puis se déconnecte.
Sans Will Retain, le broker n’envoie « offline » qu’aux abonnés présents. Le message « online » conservé reste associé au sujet, et un tableau de bord qui se connecte plus tard affiche la passerelle comme connectée. L’étape 3 est nécessaire : un DISCONNECT avec le code de motif 0x00 supprime le testament sans le publier.
MQTT 5 ajoute un Will Delay Interval (section 3.1.3.2.2). Si le client se reconnecte avant la fin de ce délai, le broker ne publie pas le testament. Un délai de 30 s évite qu’une brève coupure réseau affiche la passerelle comme hors ligne. Le broker publie le testament à la fin du délai ou à l’expiration de la session, selon ce qui arrive en premier. Si Session Expiry Interval vaut 0, la session prend fin à la déconnexion et le délai n’a aucun effet. MQTT 3.1.1 ne propose pas de délai de testament.
Liste de contrôle de la fraîcheur
Pour chaque sujet transportant des mesures, consignez :
- la QoS de publication et celle de chaque abonnement ;
- les codes de motif PUBACK que l’éditeur traite comme un échec ;
- si les messages sont conservés et pourquoi ;
- l’expiration éventuelle des messages ;
- l’emplacement de l’horodatage de mesure dans la charge utile et son format ;
- la clé utilisée par le récepteur pour supprimer les doublons ;
- l’expiration de la session et la limite de la file d’attente du broker ;
- le sujet d’état et le testament de la source, avec Will Retain activé ;
- l’âge maximal d’une mesure avant qu’un tableau de bord ou un calcul la considère comme périmée.
Le guide des données périmées explique comment marquer et traiter les mesures trop anciennes.
MQTT avec Edge
Edge, sur le Gateway ZGW-20, publie les mesures vers le broker MQTT d’une plateforme, sur MQTTS si elle l’exige. Edge publie en QoS 1 et considère la livraison comme terminée seulement à la réception du PUBACK du broker. Une déconnexion ou une erreur de publication place les mesures dans une file locale sur le Gateway. Par défaut, Edge réessaie les envois depuis cette file toutes les minutes tant que la destination est disponible. Une mesure retransmise conserve son horodatage d’origine. La file locale n’a pas de limite d’âge. Par défaut, une protection contre le manque d’espace disque suspend l’enregistrement de nouvelles mesures quand l’espace libre passe sous 10 % du système de fichiers, avec un plafond de 1 Gio, ou sous 256 Mio. Les mesures encore en mémoire au moment où le Gateway perd son alimentation ne se trouvent pas encore dans la file locale et peuvent être perdues. Le guide du stockage et de la retransmission explique son dimensionnement.
Un PUBACK seul ne prouve pas que la plateforme a accepté ou enregistré la mesure. Vérifiez les codes de motif avec MQTT 5 ; MQTT 3.1.1 ne peut pas signaler un refus d’autorisation de publication dans le PUBACK. Une retransmission après reconnexion peut aussi livrer une mesure deux fois : la plateforme doit supprimer les doublons à l’aide de l’appareil, du canal et de l’horodatage. Lors de la mise en service, retrouvez une mesure connue dans le stockage de la plateforme. Comparez son horodatage et sa valeur à ceux de la mesure dans Edge.
Questions fréquentes
Faut-il utiliser la QoS 1 ou la QoS 2 pour les données de capteurs ?
Utilisez la QoS 1 et supprimez les doublons chez le récepteur à l’aide de l’appareil, du canal et de l’horodatage de mesure. La QoS 2 exige quatre paquets par message sur chaque segment, ne supprime les doublons que sur ce segment et n’est pas proposée par certains services : AWS IoT Core prend en charge les QoS 0 et 1. Un éditeur qui renvoie des mesures après une panne peut encore créer des doublons invisibles pour la QoS 2.
Comment supprimer un message conservé ?
Publiez sur le même sujet un message avec le drapeau retain et une charge utile vide. Le broker supprime le message conservé et n’enregistre pas le message vide. Les abonnés déjà connectés reçoivent tout de même le message vide : ils doivent l’ignorer plutôt que l’interpréter comme une mesure.
Pourquoi un tableau de bord indique-t-il qu’un appareil en panne est connecté ?
Le plus souvent, parce que le testament a été envoyé sans Will Retain. Le broker publie « offline » uniquement aux abonnés présents ; le message « online » conservé reste sur le sujet pour les abonnés suivants. Autre cause courante : un testament qui ne se déclenche jamais. Un DISCONNECT normal le supprime, donc le client doit lui-même publier « offline » avant un arrêt planifié.