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
- Partez de la précision de l'instrument. Une bande morte inférieure à la précision du capteur ne fait que transmettre du bruit.
- 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é.
- Utilisez une bande morte nulle pour les valeurs discrètes : états, modes, alarmes, compteurs et consignes.
- 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.
- 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
- Pas de heartbeat. Les consommateurs ne peuvent pas distinguer une valeur stable d'une passerelle morte.
- 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.
- Des bandes mortes sur des signaux discrets. Un changement d'état de 1 à 2 ne doit jamais être filtré.
- Ne pas retenir les valeurs. Les nouveaux abonnés ne voient rien jusqu'au prochain changement.
- 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.



