Node-RED

Node-RED MQTT Topic Mapping: flache Geräte-Topics in eine ISA-95-Hierarchie überführen

So überführen Sie unübersichtliche, flache Geräte-Topics mit einer Lookup-Tabelle in Node-RED in einen strukturierten ISA-95-Namespace, normalisieren unterwegs die Payloads und publizieren alles ohne Schleifen erneut an einen ConnectMQ-Broker.

Node-RED-Flow für MQTT Topic Mapping, der flache Geräte-Topics auf einem Cloud-Broker in einen ISA-95-Topic-Baum überführt
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

Node-RED MQTT Topic Mapping ist der schnellste Weg, die flachen, herstellerspezifischen Topics, die Ihre Geräte bereits publizieren, in eine saubere ISA-95-Hierarchie zu überführen, die der Rest des Werks versteht. In diesem Tutorial bauen Sie einen kleinen Node-RED-Flow, der rohe Geräte-Topics abonniert, jedes Gerät in einer Mapping-Tabelle nachschlägt, den Payload normalisiert und ihn in einen strukturierten Namespace auf einem ConnectMQ-Broker publiziert.

Warum MQTT-Topics überhaupt umbenennen?

Die meisten Brownfield-Installationen wachsen organisch. Ein Temperatur-Gateway publiziert auf dev/th-0042/temp, ein Stromzähler nutzt meter7/kw, und ein SPS-Gateway schreibt plc/line2/status. Jedes Topic ergab für den, der das jeweilige Gerät konfiguriert hat, Sinn, aber zusammen verraten sie einem Konsumenten nichts darüber, wo im Werk die Daten entstehen.

Ein Unified Namespace (UNS) löst das, indem man sich auf eine einzige Hierarchie einigt, typischerweise entlang der ISA-95-Ebenen: Unternehmen, Standort, Bereich, Linie, Zelle oder Maschine und schließlich der Datenpunkt. Liegt jede Nachricht unter einem vorhersehbaren Pfad wie acme/izmir/packaging/line2/filler1/temperature, können Dashboards, Historians und Analysewerkzeuge mit Wildcards abonnieren, statt lange Ausnahmelisten zu pflegen. Ob das Werk in Izmir oder in Stuttgart steht, spielt dabei keine Rolle: Die ISA-95-Ebenen werden 1:1 zu den Topic-Ebenen des UNS. Den Hintergrund zu diesem Muster behandelt unser Leitfaden zu Sparkplug B und dem Unified Namespace.

Die Geräte selbst können Sie oft nicht umkonfigurieren. Die Firmware ist vielleicht gesperrt, der Hersteller existiert nicht mehr, oder ein Eingriff in eine Produktionssteuerung erfordert ein Wartungsfenster, das Sie dieses Quartal nicht bekommen. Node-RED sitzt in der Mitte und übernimmt die Übersetzung, ohne die Quelle anzufassen.

Die ISA-95-Ziel-Topic-Struktur entwerfen

Legen Sie die Zielstruktur fest, bevor Sie Code schreiben. Ein Mapping-Flow ist nur so gut wie der Namespace, in den er schreibt. Ein praxistaugliches Layout sieht so aus:

Ebene Beispielwert Hinweise
Unternehmen acme Oft durch den Broker oder ein festes Präfix vorgegeben
Standort izmir Eine pro physischem Werk
Bereich packaging Abteilung oder Halle
Linie line2 Produktionslinie oder Prozesseinheit
Maschine filler1 Zelle, Maschine oder Anlage
Datenpunkt temperature Ein Messwert oder Zustand pro Topic

Einige Regeln halten den Namespace langfristig gesund:

  1. Verwenden Sie Kleinbuchstaben, ASCII und keine Leerzeichen, damit Topics sich in Abonnements und Filtern leicht eintippen lassen.
  2. Publizieren Sie einen Datenpunkt pro Topic oder ein klar definiertes JSON-Objekt pro Maschine, und bleiben Sie bei dieser Entscheidung.
  3. Legen Sie niemals Zeitstempel, Sequenznummern oder Werte ins Topic selbst.
  4. Behalten Sie die Tiefe im Griff. Auf dem öffentlichen MQTT-Broker unter broker.connectmq.com darf ein Topic höchstens 8 Ebenen haben, daher nutzt dieses Tutorial ein dreistufiges Demo-Präfix plus fünf ISA-95-Ebenen.

Für die Demo verwenden wir connectmq/demo/ab12cd als Präfix (ersetzen Sie ab12cd durch eine eigene Zufallszeichenfolge), Rohdaten unter connectmq/demo/ab12cd/raw/# und die strukturierte Ausgabe als connectmq/demo/ab12cd/<site>/<area>/<line>/<machine>/<point>. Denken Sie daran, dass der öffentliche Bereich für alle lesbar ist; senden Sie dorthin nur Testdaten.

So funktioniert Node-RED MQTT Topic Mapping

Der Flow besteht aus vier Teilen:

  1. Ein mqtt in-Node abonniert die Roh-Topics mit einer Wildcard.
  2. Ein Function-Node zerlegt das eingehende Topic, sucht das Gerät in einer Lookup-Tabelle, baut das neue Topic und normalisiert den Payload.
  3. Ein mqtt out-Node publiziert das Ergebnis. Ist sein eigenes Topic-Feld leer, verwendet er msg.topic, genau wie gewünscht.
  4. Ein zweiter Ausgang des Function-Nodes schickt nicht zugeordnete Nachrichten an einen Debug-Node, damit Sie neue oder falsch konfigurierte Geräte erkennen.

Wenn Ihnen die MQTT-Nodes selbst noch neu sind, beginnen Sie mit dem Node-RED-MQTT-Tutorial und kehren Sie dann hierher zurück.

Die Mapping-Tabelle

Halten Sie das Mapping an einer Stelle. Für eine Handvoll Geräte genügt ein JavaScript-Objekt im Function-Node. Bei Dutzenden laden Sie es aus einer Datei oder einer Retained Message in den globalen Kontext, damit Sie es ohne erneutes Deployment aktualisieren können.

Der Function-Node

Fügen Sie diesen Code in einen Function-Node ein und stellen Sie ihn auf 2 Ausgänge:

// Roh-Topics sehen so aus: connectmq/demo/ab12cd/raw/<deviceId>/<rawPoint>
const PREFIX = "connectmq/demo/ab12cd";

// deviceId -> ISA-95-Standort
const devices = {
    "th-0042":  { site: "izmir", area: "packaging", line: "line2", machine: "filler1" },
    "meter7":   { site: "izmir", area: "utilities", line: "main",  machine: "meter7" },
    "plc-l2":   { site: "izmir", area: "packaging", line: "line2", machine: "plc" }
};

// Roh-Datenpunkt -> normalisierter Name und Einheit
const points = {
    "temp":   { name: "temperature", unit: "C" },
    "hum":    { name: "humidity",    unit: "%" },
    "kw":     { name: "power",       unit: "kW" },
    "status": { name: "state",       unit: null }
};

const parts = msg.topic.split("/");
// parts: [connectmq, demo, ab12cd, raw, deviceId, rawPoint]
const deviceId = parts[4];
const rawPoint = parts[5];

const loc = devices[deviceId];
const point = points[rawPoint];

if (!loc || !point || parts.length !== 6) {
    node.warn("Unmapped topic: " + msg.topic);
    return [null, msg];
}

// Einfache Zahlen, Strings oder JSON-Objekte mit einem Feld "value" akzeptieren
let value = msg.payload;
if (typeof value === "object" && value !== null && "value" in value) {
    value = value.value;
}
const numeric = Number(value);

const out = {
    topic: [PREFIX, loc.site, loc.area, loc.line, loc.machine, point.name].join("/"),
    payload: {
        value: isNaN(numeric) ? value : numeric,
        unit: point.unit,
        ts: new Date().toISOString(),
        source: msg.topic
    },
    retain: true,
    qos: 1
};

return [out, null];

Mit retain: true erhält jeder neue Abonnent sofort den letzten Wert jedes Datenpunkts, und genau so erwarten die meisten Konsumenten eines Unified Namespace, dass sich der Baum verhält. Das Feld source hält die Spur zum ursprünglichen Topic fest, was beim Debuggen enorm hilft.

Importierbarer Flow

Sie können den kompletten Flow über Menü → Import importieren. Fügen Sie nach dem Import den obigen Funktionscode in den Function-Node ein und ändern Sie Client-ID und Präfix auf Ihre eigenen Werte.

[
  {"id":"b1","type":"mqtt-broker","name":"public broker","broker":"broker.connectmq.com","port":"1883","clientid":"nr-mapper-ab12cd","autoConnect":true,"usetls":false,"protocolVersion":"5","keepalive":"60","cleansession":true},
  {"id":"in1","type":"mqtt in","name":"raw devices","topic":"connectmq/demo/ab12cd/raw/#","qos":"1","datatype":"auto-detect","broker":"b1","nl":false,"rap":true,"rh":0,"inputs":0,"x":140,"y":100,"wires":[["fn1"]]},
  {"id":"fn1","type":"function","name":"map to ISA-95","func":"return [msg, null];","outputs":2,"x":340,"y":100,"wires":[["out1"],["dbg1"]]},
  {"id":"out1","type":"mqtt out","name":"publish ISA-95","topic":"","qos":"","retain":"","broker":"b1","x":560,"y":80,"wires":[]},
  {"id":"dbg1","type":"debug","name":"unmapped","active":true,"complete":"true","x":550,"y":140,"wires":[]}
]

Im mqtt out-Node bleiben qos und retain leer, damit die im Function-Node gesetzten Werte verwendet werden.

Das Mapping mit mosquitto testen

Öffnen Sie ein Terminal und beobachten Sie die strukturierte Seite des Baums:

mosquitto_sub -h broker.connectmq.com -p 1883 -i sub-ab12cd -v -t "connectmq/demo/ab12cd/izmir/#"

Simulieren Sie in einem zweiten Terminal Geräte auf der Rohseite:

mosquitto_pub -h broker.connectmq.com -p 1883 -i pub-ab12cd -t "connectmq/demo/ab12cd/raw/th-0042/temp" -m "23.7"
mosquitto_pub -h broker.connectmq.com -p 1883 -i pub-ab12cd -t "connectmq/demo/ab12cd/raw/meter7/kw" -m '{"value": 4.2}'
mosquitto_pub -h broker.connectmq.com -p 1883 -i pub-ab12cd -t "connectmq/demo/ab12cd/raw/unknown-1/temp" -m "19"

Die ersten beiden Nachrichten sollten unter izmir/packaging/line2/filler1/temperature und izmir/utilities/main/meter7/power erscheinen. Die dritte sollte in der Debug-Seitenleiste von Node-RED als nicht zugeordnet auftauchen.

Schleifen und andere Fallstricke vermeiden

Der häufigste Fehler in Mapping-Flows ist eine Wildcard, die auch die Ausgabe abdeckt. Lauscht der mqtt in-Node auf connectmq/demo/ab12cd/# und publiziert der Flow unter demselben Präfix zurück, wird jede neu publizierte Nachricht erneut empfangen, erneut gemappt und überflutet schließlich den Broker. Halten Sie Roh- und strukturierte Daten in getrennten Zweigen des Baums und abonnieren Sie nur den Rohzweig.

Mit MQTT 5 können Sie am mqtt in-Node außerdem die Option no local aktivieren, sodass der Client niemals Nachrichten erhält, die er selbst publiziert hat. Das ist ein Sicherheitsnetz, aber kein Ersatz für ein gutes Topic-Design, denn eine zweite Node-RED-Instanz würde die Nachrichten trotzdem sehen.

Weitere Fallstricke, die Sie prüfen sollten:

Symptom Wahrscheinliche Ursache Lösung
Nichts wird neu publiziert Function gibt auf beiden Ausgängen null zurück Debug-Ausgabe und die Prüfung von parts.length kontrollieren
Ausgabe-Topic enthält undefined Gerät in der Tabelle, aber eine Ebene fehlt Prüfen, dass jeder Tabelleneintrag alle vier Ebenen hat
Alte Werte tauchen nach einer Umbenennung wieder auf Retained Messages auf dem alten Topic Leeren Retained-Payload auf das alte Topic publizieren, um es zu löschen
Nachgelagert Strings statt Zahlen Roh-Payload enthielt Einheiten wie 23.7C Einheiten im Function-Node vor Number() entfernen
Verbindungsabbrüche auf dem öffentlichen Broker Mehr als 10 Nachrichten pro Sekunde und Client Mit einem Delay-Node drosseln oder eigenen Broker verwenden

Den Flow in die Produktion bringen

Der öffentliche Broker eignet sich für Experimente, Produktionsdaten gehören aber auf einen privaten Broker mit Zugangsdaten und TLS. Legen Sie bei ConnectMQ einen Broker an, kopieren Sie den generierten Benutzernamen, das Passwort und die Client-ID aus dem Portal und stellen Sie den Broker-Konfigurations-Node um auf:

  • Server broker.connectmq.com (oder Ihren Broker-Host aus dem Portal), Port 8883, Use TLS aktiviert
  • Client-ID YOUR_CLIENT_ID, Benutzername YOUR_USERNAME, Passwort YOUR_PASSWORD
  • Protokoll MQTT V5

Das Broker-Zertifikat stammt von Let's Encrypt, daher validiert es der Standard-Truststore der Node.js-Laufzeit von Node-RED, ohne dass Sie eine CA-Datei hochladen müssen. Unser Leitfaden zu Node-RED MQTT über TLS führt Schritt für Schritt durch die TLS-Einstellungen.

Sobald die strukturierten Topics fließen, erkennt das ConnectMQ-Portal den ISA-95-förmigen Namespace und zeigt Ihre Geräte gruppiert nach Standort, Bereich, Linie und Maschine; im Live-Topic-Explorer prüfen Sie den Baum, ohne einen separaten Client zu öffnen. Sprechen einige Ihrer Quellen bereits Sparkplug B, brauchen Sie für sie Node-RED womöglich gar nicht: Der Broker kann Sparkplug B dekodieren und in ISA-95-Topics neu publizieren, sodass Node-RED nur die einfachen, flachen Topics übernehmen muss. Für SPS-Quellen zeigt der Node-RED-Modbus-Leitfaden, wie Sie Register lesen und in denselben Mapping-Flow einspeisen.

Da ein verwalteter Broker Verfügbarkeit, Zertifikate und Verbindungslimits übernimmt, wird der Mapping-Flow zum einzigen beweglichen Teil, den Sie pflegen. Das ist ein großer Teil des Grundes, warum Teams von einer selbst betriebenen Mosquitto-Box auf einen verwalteten MQTT-Broker wechseln, sobald der Namespace wächst.

Die Mapping-Tabelle skalieren

Wenn die Zahl der Geräte über das hinauswächst, was Sie in einem Function-Node bearbeiten möchten, lagern Sie die Tabelle aus:

  • Retained Konfigurations-Topic: Publizieren Sie das Mapping als Retained-JSON-Nachricht, etwa auf config/topic-map, abonnieren Sie es in einem separaten Flow und speichern Sie es mit global.set("topicMap", msg.payload). Die Mapping-Funktion liest es mit global.get("topicMap"). Änderungen wirken ohne erneutes Deployment.
  • Datei oder Datenbank: Lesen Sie beim Start mit einem file in-Node eine CSV- oder JSON-Datei ein oder fragen Sie eine kleine Tabelle ab; das passt zu Teams, die bereits ein Anlagenverzeichnis pflegen.
  • Musterregeln: Kodieren Geräte-IDs ihren Standort, etwa iz-pk-l2-filler1-temp, kann eine Regel auf Basis von split den Großteil der Tabelle ersetzen, ergänzt um eine kleine Tabelle für Ausnahmen.

Was Sie auch wählen: Halten Sie den Ausgang für nicht zugeordnete Nachrichten mit etwas Sichtbarem verbunden. Neue Geräte tauchen unauffällig auf, und eine Warnung über ein nicht zugeordnetes Topic ist oft das einzige Zeichen dafür, dass ein Gateway ersetzt oder umbenannt wurde.

Ihr Node-RED Topic Mapping auf ConnectMQ betreiben

Ein sauberer Namespace braucht einen verlässlichen Broker darunter. Registrieren Sie sich unter app.connectmq.com für den kostenlosen Serverless-Plan ohne Kreditkarte, legen Sie einen Broker an und richten Sie Ihren Mapping-Flow per TLS auf Port 8883 darauf aus. Sie erhalten MQTT 5, Retained Messages für den letzten Wert jedes Datenpunkts und eine Geräteansicht, die Ihre ISA-95-Ebenen ab der ersten Nachricht versteht. Einen breiteren Überblick über Hosting-Optionen bietet unser Leitfaden zum Cloud-MQTT-Broker.

Häufig gestellte Fragen

Kann Node-RED Topics ohne Function-Node umschreiben?

Ja. Ein Change-Node kann msg.topic mit einem JSONata-Ausdruck setzen, und das reicht für einfache Präfix-Wechsel. Ein Function-Node ist leichter lesbar, sobald Sie Lookup-Tabelle, Validierung und Payload-Normalisierung im selben Schritt brauchen.

Sollte ich ein Topic pro Wert oder ein JSON-Objekt pro Maschine publizieren?

Beides funktioniert in einem Unified Namespace. Ein Wert pro Topic ist für Dashboards und Alarmregeln einfacher, ein Objekt pro Maschine reduziert die Zahl der Nachrichten; wählen Sie eine Konvention und wenden Sie sie überall an.

Wie verhindere ich, dass der Flow seine eigenen Nachrichten erneut publiziert?

Abonnieren Sie nur einen Rohzweig, in den der Flow nie schreibt, und aktivieren Sie am mqtt in-Node zusätzlich die MQTT-5-Option no local als Absicherung.

Benennt ConnectMQ Topics selbst um?

ConnectMQ publiziert Sparkplug-B-Nachrichten in ISA-95-Topics neu, führt aber keine beliebigen Regeln zum Umschreiben von Topics aus. Für flache oder herstellerspezifische Topics führen Sie das Mapping wie hier gezeigt in Node-RED oder einem anderen MQTT-Client aus.

Verwandte Artikel

Node-RED als OPC-UA-Client zu MQTT mit ISA-95-Topics

Mit node-red-contrib-opcua OPC-UA-Variablen auf einer SPS oder einem Simulationsserver abonnieren, Node-IDs auf MQTT-Topics im ISA-95-Stil abbilden und Werte mit Qualität und Zeitstempel publizieren.

9 Min. Lesezeit

Node-RED Diagramm-Verlauf: MQTT-Chartdaten über Neustarts hinweg behalten

Dashboard-Diagramme vergessen alles, wenn Node-RED neu startet. Dieser Leitfaden zeigt zwei zuverlässige Wege, den Node-RED Diagramm-Verlauf für MQTT-Daten zu behalten: dateibasierten Context-Speicher und eine kleine SQLite-Tabelle, beide beim Start in Dashboard 2.0 zurückgespielt.

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.