Industrial IoT

Report by Exception (RBE) in MQTT-Systemen: Totbänder, Heartbeats und ein Gateway-Beispiel

Report by Exception veröffentlicht einen Wert nur, wenn er sich nennenswert ändert. Erfahren Sie, wie Totbänder, Ratenbegrenzungen und Heartbeats zusammenwirken, und betreiben Sie ein Python-Gateway, das RBE vor dem Veröffentlichen an MQTT anwendet.

Diagramm, das abgefragte Messwerte mit Report-by-Exception-Veröffentlichungen nach einem Totbandfilter in einem MQTT-Gateway vergleicht
Probieren Sie es auf Ihrem eigenen MQTT-Broker ausHolen Sie sich im Free-Tarif einen privaten, verwalteten MQTT-Broker und starten Sie mit fertigen Codebeispielen.
Kostenlos testen Codebeispiele Keine Kreditkarte erforderlich

Report by Exception (RBE) bedeutet, einen Wert nur dann zu veröffentlichen, wenn er sich um einen nennenswerten Betrag ändert, statt jeden abgefragten Messwert zu senden. Es ist eine der einfachsten und wirkungsvollsten Techniken in industriellen MQTT-Systemen und eine Voraussetzung dafür, dass ein Unified Namespace (UNS) auch bei Tausenden von Maschinendaten-Tags schlank und aktuell bleibt. In diesem Artikel erfahren Sie, wie Totbänder, Mindestintervalle und Heartbeats zusammenspielen, und betreiben ein Python-Gateway, das RBE vor dem Veröffentlichen an einen Broker anwendet.

Polling vs. Report by Exception

Klassisches SCADA und viele Modbus- oder OPC-UA-Integrationen arbeiten mit Polling: Jedes Tag wird in einem festen Zyklus gelesen, und jedes Ergebnis wird weitergeleitet. Das ist einfach, doch die meisten Industriewerte ändern sich langsam. OPC UA kennt dasselbe Prinzip übrigens bereits auf Protokollebene: Monitored Items können mit einem absoluten oder prozentualen Totband (Deadband-Filter) abonniert werden. Eine Tanktemperatur, die eine Stunde lang jede Sekunde gelesen wird, erzeugt 3.600 Nachrichten, die womöglich nur eine Handvoll relevanter Änderungen enthalten.

MQTT ist für den umgekehrten Stil ausgelegt. Clients veröffentlichen, wenn etwas passiert, und der Broker liefert den neuen Wert an jeden Subscriber aus. RBE verbindet beides: Das Gateway fragt das Gerät weiterhin lokal ab (weil das Geräteprotokoll es verlangt), veröffentlicht nach oben aber nur, wenn sich der Wert ausreichend geändert hat.

Ansatz Gesendete Nachrichten Aktualität Typisches Risiko
Veröffentlichen in festen Intervallen Jeder Messwert Vorhersehbar Verschwendete Bandbreite, Speicher und Rechenleistung
Reines Report by Exception Nur bei Änderung Sofort bei Änderungen Ein stiller Wert sieht genauso aus wie ein ausgefallenes Gerät
RBE mit Heartbeat Änderungen plus periodische Auffrischung Sofort, mit begrenzter Veraltung Erfordert etwas mehr Logik

Die dritte Zeile ist das, was Sie in der Praxis wollen.

Die Bausteine von Report by Exception

Totband

Ein Totband ist die minimale Änderung, die als „anders“ gilt. Es filtert Sensorrauschen und unbedeutende Drift heraus.

  • Absolutes Totband: Veröffentlichen, wenn die absolute Differenz zwischen dem neuen Wert und dem zuletzt veröffentlichten Wert mindestens, zum Beispiel, 0,5 Grad beträgt.
  • Prozentuales Totband: Veröffentlichen, wenn die Änderung einen Prozentsatz des letzten Werts übersteigt oder, robuster, einen Prozentsatz des Messbereichs. Ein auf den Messbereich bezogener Prozentsatz ist nahe null sicherer, weil ein Prozentsatz des Werts winzige Schwankungen dort riesig erscheinen ließe.

Vergleichen Sie immer mit dem zuletzt veröffentlichten Wert, nicht mit dem zuletzt gemessenen. Ein Vergleich mit dem vorherigen Messwert lässt eine langsame Drift für immer unbemerkt durchrutschen, weil jeder einzelne Schritt unter dem Totband liegt.

Mindestintervall (Ratenbegrenzung)

Ein verrauschtes oder schwingendes Signal kann das Totband trotzdem wiederholt überschreiten. Ein Mindestintervall, zum Beispiel eine Sekunde pro Tag, begrenzt die Rate. Stellen Sie es für Alarme und Zustandsänderungen mit Bedacht ein: Diese möchten Sie meist sofort haben, geben Sie ihnen also ein Mindestintervall von null.

Heartbeat (Maximalintervall)

Wenn sich ein Wert nie ändert, veröffentlicht RBE ihn nie, und Konsumenten können „stabil“ nicht von „Gateway tot“ unterscheiden. Ein Heartbeat veröffentlicht den aktuellen Wert nach einer maximalen Stille, etwa alle fünf Minuten, auch ohne Änderung. Zusammen mit einem MQTT Last Will auf dem Status-Topic des Gateways können Subscriber einen ruhigen Wert jederzeit von einer verlorenen Verbindung unterscheiden.

Qualitätsänderungen

Eine Änderung der Datenqualität (ein fehlschlagender Sensor-Lesevorgang, ein Gerät, das offline geht) sollte unabhängig vom Totband immer veröffentlicht werden. Ein veralteter „GOOD“-Wert, der in Wahrheit von einem ausgefallenen Gerät stammt, ist schlimmer als gar kein Wert.

Totbänder wählen: praktische Richtlinien

  1. Gehen Sie von der Genauigkeit des Messgeräts aus. Ein Totband, das kleiner als die Sensorgenauigkeit ist, leitet nur Rauschen weiter.
  2. Fragen Sie den Konsumenten, welche Auflösung er braucht. Ein Dashboard, das ganze Grad anzeigt, benötigt keine Änderungen um 0,01 Grad.
  3. Verwenden Sie für diskrete Werte ein Totband von null: Zustände, Modi, Alarme, Zähler und Sollwerte.
  4. Seien Sie vorsichtig bei Summenzählern und Energiezählern. Verwenden Sie ein Totband in Einheiten des Zählers und einen regelmäßigen Heartbeat, damit abrechnungsähnliche Berechnungen genau bleiben.
  5. Überprüfen Sie die Totbänder nach dem Go-live, indem Sie die Nachrichtenraten pro Tag vergleichen; oft dominieren einige wenige gesprächige Tags den Datenverkehr.

Ein Python-Gateway mit Report by Exception

Dieses Beispiel simuliert fünf Tags, die alle 500 ms gelesen werden, wendet pro Tag Regeln für Totband, Mindestintervall und Heartbeat an und veröffentlicht Änderungen als Retained-JSON-Nachrichten auf dem öffentlichen MQTT-Broker broker.connectmq.com, der für Tests gedacht ist und keine Produktionsdaten tragen sollte. Es gibt aus, wie viele Messwerte gelesen und wie viele Nachrichten gesendet wurden, sodass Sie den Effekt direkt sehen. Es verwendet paho-mqtt 2.x und Python 3.10 oder neuer.

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            # absolut, in physikalischen Einheiten (0 = jede Änderung)
    min_interval: float = 1.0  # Sekunden zwischen Veröffentlichungen
    heartbeat: float = 300.0   # Sekunden, auch ohne Änderung veröffentlichen
    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                                   # erster Wert oder Qualitätsänderung
        if now - self.last_ts >= self.heartbeat:
            return True                                   # Heartbeat
        if now - self.last_ts < self.min_interval:
            return False                                  # Ratenbegrenzung
        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():
    """Geräteabfrage simulieren: leichtes Rauschen plus gelegentliche echte Änderungen."""
    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()

Verfolgen Sie das Ergebnis:

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

Die minütliche Zusammenfassungszeile zeigt, welcher Anteil der abgefragten Daten tatsächlich gesendet werden musste. Mit realistischen Totbändern ist die Reduktion groß, und die verbleibenden Nachrichten sind genau die, die Information tragen. Passen Sie die Totbänder an und beobachten Sie, wie sich das Verhältnis ändert.

Warum Retained Messages bei Report by Exception wichtig sind

Wenn Werte nur bei Änderung veröffentlicht werden, müsste ein Subscriber, der sich jetzt verbindet, sonst womöglich minutenlang auf das nächste Update warten. Jedes Tag als Retained Message zu veröffentlichen, löst das: Der Broker speichert den letzten Wert pro Topic und liefert ihn sofort an jeden neuen Subscriber aus. Ein Topic pro Tag funktioniert dafür besser als ein großer JSON-Block, weil jedes Tag seinen eigenen Retained-Letztwert behält. Wenn Sie die Zustellgarantien auffrischen möchten, lesen Sie MQTT-QoS-Stufen erklärt.

RBE in Sparkplug B

Report by Exception ist fest in der Sparkplug-B-Spezifikation verankert. Edge-Knoten senden beim Verbinden ein vollständiges Birth Certificate mit jeder Metrik und danach in DATA-Nachrichten nur noch geänderte Metriken. Verliert eine Host-Anwendung den Überblick, fordert sie ein Rebirth an. Damit zeigt Sparkplug dasselbe Verhalten „vollständiger Zustand beim Verbinden, danach Änderungen“ wie Retained Messages plus RBE in einfachem MQTT. ConnectMQ kann Sparkplug-B-Payloads dekodieren und in lesbare ISA-95-Topics erneut veröffentlichen, sodass RBE-optimierter Sparkplug-Verkehr leicht zu prüfen bleibt.

Im UNS folgt diese Topic-Struktur der ISA-95-Hierarchie (Enterprise → Site → Area → Line → Cell): Jedes Tag liegt unter einem Pfad wie acme/werk-stuttgart/kesselhaus/linie-1/kessel-1/temperature_c und trägt als Retained Message seinen letzten gültigen Wert. RBE sorgt dafür, dass MES, ERP-Anbindung und Dashboards nur echte Änderungen erhalten, während der Retained-Letztwert jedem neuen Konsumenten sofort den aktuellen Werkszustand liefert. Wie Sparkplug B und UNS zusammenspielen, beschreibt der Beitrag zu Sparkplug B und Unified Namespace.

Wo sich RBE am meisten lohnt

  • Volumenabgerechnete oder eingeschränkte Verbindungen. Bei Mobilfunk- und Satellitenverbindungen zählt jedes Byte. RBE ist meist die erste Optimierung, die man anwendet, wie unter MQTT über Mobilfunk beschrieben.
  • Store-and-Forward-Puffer. Weniger Nachrichten bedeuten, dass ein Ausfall den Puffer langsamer füllt und die Wiedergabe schneller geht; siehe Store-and-Forward für MQTT.
  • Historians und Speicher. Änderungsbasierte Daten lassen sich gut komprimieren und rekonstruieren das Signal dennoch innerhalb des Totbands.
  • Broker-Kontingente. Grenzen für Nachrichtenrate und Verbindungen gelten bei jedem Broker. Auf dem öffentlichen MQTT-Broker darf zum Beispiel jeder Client 10 Nachrichten pro Sekunde senden.

RBE wird meist in einem IIoT-Edge-Gateway direkt neben den Protokolltreibern umgesetzt. Das größere Architekturbild finden Sie im Leitfaden zu Industrial IoT mit MQTT.

Häufige Fehler

  1. Kein Heartbeat. Konsumenten können einen konstanten Wert nicht von einem toten Gateway unterscheiden.
  2. Vergleich mit dem vorherigen Messwert statt mit dem zuletzt veröffentlichten Wert, wodurch Drift unbemerkt durchrutscht.
  3. Totbänder auf diskreten Signalen. Eine Zustandsänderung von 1 auf 2 darf niemals gefiltert werden.
  4. Werte nicht retained senden. Neue Subscriber sehen bis zur nächsten Änderung nichts.
  5. Ein Totband für alle Tags. Stimmen Sie es pro Tag ab, basierend auf Genauigkeit und Bedarf der Konsumenten.

Intelligenter veröffentlichen mit ConnectMQ

RBE reduziert, was Sie senden; ein zuverlässiger Broker sorgt dafür, dass das Gesendete ankommt. ConnectMQ ist ein verwalteter MQTT-Broker mit Retained Messages, MQTT 5, TLS und einem Live-Topic-Explorer, mit dem Sie den Datenverkehr pro Tag prüfen können, und das obige Beispiel können Sie zunächst auf dem kostenlosen öffentlichen MQTT-Broker testen. Wenn Sie bereit sind, erstellen Sie unter app.connectmq.com einen kostenlosen Serverless-Broker, ganz ohne Kreditkarte.

Häufig gestellte Fragen

Was ist Report by Exception, einfach erklärt?

Es bedeutet, einen Wert nur dann zu senden, wenn er sich nennenswert ändert, statt bei jedem Lesevorgang. Ein Totband legt fest, was „nennenswert“ heißt, und ein Heartbeat stellt sicher, dass ein Wert trotzdem gelegentlich gesendet wird.

Gehen bei Report by Exception Daten verloren?

Änderungen, die kleiner als das Totband sind, werden bewusst verworfen. Wählen Sie Totbänder anhand der Sensorgenauigkeit und dessen, was Konsumenten tatsächlich brauchen, und halten Sie Rohdaten lokal vor, falls Ingenieure später die volle Auflösung benötigen.

Was ist ein gutes Heartbeat-Intervall?

Übliche Werte reichen von einer bis fünfzehn Minuten. Es sollte kürzer sein als die Zeit, nach der ein Konsument einen Wert als veraltet betrachten würde.

Sollten Alarme Report by Exception nutzen?

Ja, mit einem Totband von null und ohne Mindestintervall, sodass jeder Zustandswechsel sofort veröffentlicht wird.

Verwandte Artikel

Industrial DataOps: Werksdaten kontextualisieren und steuern

Was Industrial DataOps in der Praxis bedeutet: Kontextualisierung, Normalisierung, Qualitätsprüfungen und Governance von OT-Daten und wie Sie diese Schicht auf einem MQTT-Namespace betreiben.

8 Min. Lesezeit

Alle Artikel →

Ihr privater MQTT-Broker ist nur wenige Klicks entfernt

Konto anlegen, Broker-Zugangsdaten erhalten und das erste Gerät verbinden — ohne Server, die installiert, gepatcht oder skaliert werden müssen.