IoT industriel

Report by exception (RBE) dans les systèmes MQTT : bandes mortes, heartbeats et exemple de passerelle

Le report by exception ne publie une valeur que lorsqu'elle change de manière significative. Découvrez comment bandes mortes, limites de débit et heartbeats fonctionnent ensemble, et exécutez une passerelle Python qui applique le RBE avant de publier sur MQTT.

Graphique comparant les échantillons interrogés et les publications en report by exception après un filtre à bande morte dans une passerelle MQTT
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

Le report by exception (RBE) consiste à ne publier une valeur que lorsqu'elle change d'une quantité significative, au lieu d'envoyer chaque échantillon interrogé. C'est l'une des techniques les plus simples et les plus efficaces des systèmes MQTT industriels. Dans cet article, vous verrez comment s'articulent bandes mortes, intervalles minimaux et heartbeats, et vous exécuterez une passerelle Python qui applique le RBE avant de publier sur un broker.

Interrogation périodique vs report by exception

Les SCADA traditionnels et de nombreuses intégrations Modbus ou OPC UA fonctionnent par interrogation (polling) : chaque variable est lue selon un cycle fixe et chaque résultat est transmis. C'est simple, mais la plupart des valeurs industrielles évoluent lentement. Une température de cuve lue chaque seconde pendant une heure produit 3 600 messages qui ne contiennent parfois qu'une poignée de changements significatifs.

MQTT est conçu pour le style inverse. Les clients publient lorsqu'il se passe quelque chose, et le broker livre la nouvelle valeur à chaque abonné. Le RBE combine les deux : la passerelle continue d'interroger l'équipement en local (parce que le protocole de l'équipement l'exige), mais ne publie en amont que lorsque la valeur a suffisamment changé pour compter.

Approche Messages envoyés Fraîcheur Risque typique
Publication à intervalle fixe Chaque échantillon Prévisible Bande passante, stockage et traitement gaspillés
Report by exception pur Uniquement sur changement Immédiate pour les changements Une valeur silencieuse ressemble à un équipement mort
RBE avec heartbeat Changements plus rafraîchissement périodique Immédiate, avec une obsolescence bornée Demande un peu plus de logique

En pratique, c'est la troisième ligne qu'il vous faut.

Les briques du report by exception

Bande morte

Une bande morte (deadband) est la variation minimale considérée comme « différente ». Elle filtre le bruit des capteurs et les dérives insignifiantes.

  • Bande morte absolue : publier si l'écart absolu entre la nouvelle valeur et la dernière valeur publiée atteint, par exemple, 0,5 degré.
  • Bande morte en pourcentage : publier si la variation dépasse un pourcentage de la dernière valeur ou, de façon plus robuste, de l'étendue de mesure de l'instrument. Le pourcentage de l'étendue est plus sûr près de zéro, là où un pourcentage de la valeur ferait paraître énormes de minuscules fluctuations.

Comparez toujours avec la dernière valeur publiée, pas avec le dernier échantillon. Comparer avec l'échantillon précédent laisse une dérive lente passer inaperçue indéfiniment, puisque chaque pas reste sous la bande morte.

Intervalle minimal (limitation de débit)

Un signal bruité ou oscillant peut tout de même franchir la bande morte à répétition. Un intervalle minimal, par exemple une seconde par variable, plafonne le débit. Réglez-le avec soin pour les alarmes et les changements d'état : vous les voulez généralement immédiatement, donnez-leur donc un intervalle minimal de zéro.

Heartbeat (intervalle maximal)

Si une valeur ne change jamais, le RBE ne la publie jamais, et les consommateurs ne peuvent pas distinguer « stable » de « passerelle morte ». Un heartbeat publie la valeur courante après un silence maximal, par exemple toutes les cinq minutes, même sans changement. Combiné à un Last Will MQTT sur le topic d'état de la passerelle, il permet aux abonnés de toujours distinguer une valeur calme d'une connexion perdue.

Changements de qualité

Un changement de qualité des données (échec de lecture d'un capteur, équipement hors ligne) doit toujours être publié, quelle que soit la bande morte. Une valeur « GOOD » périmée provenant en réalité d'un équipement en panne est pire que pas de valeur du tout.

Choisir ses bandes mortes : conseils pratiques

  1. Partez de la précision de l'instrument. Une bande morte inférieure à la précision du capteur ne fait que transmettre du bruit.
  2. Demandez au consommateur la résolution dont il a besoin. Un tableau de bord qui affiche des degrés entiers n'a pas besoin de variations de 0,01 degré.
  3. Utilisez une bande morte nulle pour les valeurs discrètes : états, modes, alarmes, compteurs et consignes.
  4. Soyez prudent avec les totalisateurs et les compteurs d'énergie. Utilisez une bande morte exprimée dans l'unité du compteur et un heartbeat régulier pour que les calculs de type facturation restent exacts.
  5. Revoyez les bandes mortes après la mise en service en comparant les débits de messages par variable ; quelques variables bavardes dominent souvent le trafic.

Une passerelle Python avec report by exception

Cet exemple simule cinq variables lues toutes les 500 ms, applique par variable des règles de bande morte, d'intervalle minimal et de heartbeat, et publie les changements sous forme de messages JSON retenus sur la zone de test du serveur MQTT public gratuit broker.connectmq.com. Il affiche combien d'échantillons ont été lus par rapport au nombre de messages envoyés, pour que vous voyiez l'effet directement. Il utilise paho-mqtt 2.x et Python 3.10 ou plus récent.

import json
import random
import time
from dataclasses import dataclass
from datetime import datetime, timezone

import paho.mqtt.client as mqtt

BROKER = "broker.connectmq.com"
PORT = 1883
CLIENT_ID = "rbe-gateway-6e41"
BASE = "connectmq/demo/rbe-6e41/plant1/boiler1"
POLL_S = 0.5


@dataclass
class Tag:
    name: str
    deadband: float            # absolue, en unités physiques (0 = tout changement)
    min_interval: float = 1.0  # secondes entre deux publications
    heartbeat: float = 300.0   # secondes, publier même sans changement
    last_value: float | None = None
    last_quality: str | None = None
    last_ts: float = 0.0

    def should_publish(self, value, quality, now):
        if self.last_value is None or quality != self.last_quality:
            return True                                   # première valeur ou changement de qualité
        if now - self.last_ts >= self.heartbeat:
            return True                                   # heartbeat
        if now - self.last_ts < self.min_interval:
            return False                                  # limitation de débit
        if self.deadband == 0:
            return value != self.last_value
        return abs(value - self.last_value) >= self.deadband

    def mark(self, value, quality, now):
        self.last_value, self.last_quality, self.last_ts = value, quality, now


TAGS = {
    "temperature_c": Tag("temperature_c", deadband=0.5),
    "pressure_bar":  Tag("pressure_bar", deadband=0.05),
    "flow_m3h":      Tag("flow_m3h", deadband=0.2),
    "burner_on":     Tag("burner_on", deadband=0, min_interval=0),
    "alarm_code":    Tag("alarm_code", deadband=0, min_interval=0),
}

state = {"temperature_c": 82.0, "pressure_bar": 3.20, "flow_m3h": 12.0,
         "burner_on": 1, "alarm_code": 0}


def poll_device():
    """Simule une lecture d'équipement : léger bruit plus changements réels occasionnels."""
    state["temperature_c"] += random.gauss(0, 0.08)
    state["pressure_bar"] += random.gauss(0, 0.008)
    state["flow_m3h"] += random.gauss(0, 0.03)
    if random.random() < 0.005:
        state["burner_on"] = 1 - state["burner_on"]
    quality = "BAD" if random.random() < 0.002 else "GOOD"
    return {k: round(v, 3) for k, v in state.items()}, quality


def on_connect(client, userdata, flags, reason_code, properties):
    print("Connected:", reason_code)
    client.publish(f"{BASE}/status", "online", qos=1, retain=True)


client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=CLIENT_ID)
client.on_connect = on_connect
client.will_set(f"{BASE}/status", "offline", qos=1, retain=True)
client.connect(BROKER, PORT, keepalive=60)
client.loop_start()

samples = sent = 0
started = time.monotonic()
try:
    while True:
        values, quality = poll_device()
        now = time.monotonic()
        for name, value in values.items():
            samples += 1
            tag = TAGS[name]
            if tag.should_publish(value, quality, now):
                payload = {
                    "v": value,
                    "q": quality,
                    "ts": datetime.now(timezone.utc).isoformat(timespec="milliseconds"),
                }
                client.publish(f"{BASE}/{name}", json.dumps(payload), qos=1, retain=True)
                tag.mark(value, quality, now)
                sent += 1
        if now - started >= 60:
            print(f"last 60 s: {samples} samples read, {sent} messages published "
                  f"({100 * sent / samples:.1f} %)")
            samples = sent = 0
            started = now
        time.sleep(POLL_S)
except KeyboardInterrupt:
    client.loop_stop()
    client.disconnect()

Observez le résultat :

mosquitto_sub -h broker.connectmq.com -p 1883 -i rbe-6e41-viewer -t "connectmq/demo/rbe-6e41/#" -v

La ligne de synthèse affichée chaque minute montre quelle part des données interrogées devait réellement être envoyée. Avec des bandes mortes réalistes, la réduction est importante, et les messages restants sont ceux qui portent de l'information. Ajustez les bandes mortes et observez l'évolution du ratio.

Pourquoi les messages retenus comptent avec le report by exception

Quand les valeurs ne sont publiées que sur changement, un abonné qui se connecte maintenant risque d'attendre plusieurs minutes la prochaine mise à jour. Publier chaque variable sous forme de retained message règle ce problème : le broker conserve la dernière valeur par topic et la livre immédiatement à chaque nouvel abonné. Un topic par variable fonctionne mieux qu'un gros bloc JSON unique, car chaque variable conserve sa propre dernière valeur retenue. Pour réviser les garanties de livraison, consultez les niveaux de QoS MQTT expliqués.

Le RBE dans Sparkplug B

Le report by exception est intégré à la spécification Sparkplug B. Les nœuds edge envoient un birth certificate complet avec toutes les métriques à la connexion, puis uniquement les métriques modifiées dans les messages DATA. Si une application hôte perd le fil, elle demande un rebirth. Sparkplug offre ainsi le même comportement « état complet à la connexion, changements ensuite » que les retained messages combinés au RBE en MQTT classique. ConnectMQ peut décoder les payloads Sparkplug B et les republier dans des topics ISA-95 lisibles, si bien que le trafic Sparkplug optimisé par le RBE reste facile à inspecter.

Là où le RBE rapporte le plus

  • Liaisons facturées au volume ou contraintes. Sur les connexions cellulaires et satellite, chaque octet compte. Le RBE est généralement la première optimisation à appliquer, comme décrit dans MQTT sur réseau cellulaire.
  • Tampons store-and-forward. Moins de messages signifie qu'une coupure remplit le tampon plus lentement et qu'il se vide plus vite ; voir store-and-forward pour MQTT.
  • Historiens et stockage. Les données basées sur les changements se compressent bien et permettent toujours de reconstruire le signal à la bande morte près.
  • Quotas du broker. Des limites de débit de messages et de connexions s'appliquent sur tout broker. Sur le serveur MQTT public gratuit, par exemple, chaque client peut envoyer 10 messages par seconde.

Le RBE est généralement implémenté dans une passerelle edge IIoT, à côté des pilotes de protocoles. Pour une vision plus large de l'architecture, consultez le guide de l'IoT industriel avec MQTT.

Erreurs courantes

  1. Pas de heartbeat. Les consommateurs ne peuvent pas distinguer une valeur stable d'une passerelle morte.
  2. Comparer avec l'échantillon précédent au lieu de la dernière valeur publiée, ce qui laisse passer les dérives inaperçues.
  3. Des bandes mortes sur des signaux discrets. Un changement d'état de 1 à 2 ne doit jamais être filtré.
  4. Ne pas retenir les valeurs. Les nouveaux abonnés ne voient rien jusqu'au prochain changement.
  5. Une seule bande morte pour toutes les variables. Réglez-la par variable, selon la précision et les besoins des consommateurs.

Publiez plus intelligemment avec ConnectMQ

Le RBE réduit ce que vous envoyez ; un broker fiable garantit que ce que vous envoyez arrive. ConnectMQ est un broker MQTT managé avec retained messages, MQTT 5, TLS et un explorateur de topics en direct pour vérifier le trafic par variable, et vous pouvez d'abord tester l'exemple ci-dessus sur le serveur MQTT public gratuit. Quand vous êtes prêt, créez un broker Serverless gratuit sur app.connectmq.com, sans carte bancaire.

Questions fréquentes

Qu'est-ce que le report by exception, en termes simples ?

C'est le fait de n'envoyer une valeur que lorsqu'elle change de manière significative, plutôt qu'à chaque lecture. Une bande morte définit ce qui est « significatif » et un heartbeat garantit qu'une valeur est tout de même envoyée de temps en temps.

Le report by exception fait-il perdre des données ?

Il écarte par conception les variations inférieures à la bande morte. Choisissez les bandes mortes en fonction de la précision des capteurs et des besoins réels des consommateurs, et conservez les données brutes en local si les ingénieurs risquent d'avoir besoin de la pleine résolution plus tard.

Quel intervalle de heartbeat choisir ?

Les choix courants vont d'une à quinze minutes. Il doit être plus court que la durée au-delà de laquelle un consommateur considérerait une valeur comme périmée.

Les alarmes doivent-elles utiliser le report by exception ?

Oui, avec une bande morte nulle et sans intervalle minimal, afin que chaque transition soit publiée immédiatement.

Articles liés

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.