Node-RED

Mappage de topics MQTT avec Node-RED : transformer des topics plats en hiérarchie ISA-95

Apprenez à transformer des topics d'équipements plats et désordonnés en un espace de noms ISA-95 structuré grâce à une table de correspondance Node-RED, à normaliser les payloads au passage et à tout republier sur un broker ConnectMQ sans créer de boucles.

Flow de mappage de topics MQTT Node-RED convertissant des topics d'équipements plats en arborescence ISA-95 sur un broker cloud
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 mappage de topics MQTT avec Node-RED est le moyen le plus rapide de transformer les topics plats et propres à chaque fabricant que vos équipements publient déjà en une hiérarchie ISA-95 claire, compréhensible par le reste de l'usine. Dans ce tutoriel, vous allez construire un petit flow Node-RED qui s'abonne aux topics bruts des équipements, recherche chaque équipement dans une table de correspondance, normalise le payload et le republie dans un espace de noms structuré sur un broker ConnectMQ.

Pourquoi remapper des topics MQTT ?

La plupart des installations existantes se sont développées au fil de l'eau. Une passerelle de température publie sur dev/th-0042/temp, un compteur électrique utilise meter7/kw et une passerelle d'automate écrit sur plc/line2/status. Chaque topic avait du sens pour la personne qui a configuré l'équipement, mais ensemble ils n'apprennent rien à un consommateur sur l'emplacement des données dans l'usine.

Un Unified Namespace résout ce problème en s'accordant sur une hiérarchie unique, qui suit généralement les niveaux ISA-95 : entreprise, site, zone, ligne, cellule ou machine, et enfin le point de donnée. Lorsque chaque message se trouve à un chemin prévisible comme acme/izmir/packaging/line2/filler1/temperature, tableaux de bord, historiens et outils d'analyse peuvent s'abonner avec des wildcards au lieu de maintenir une longue liste d'exceptions. Le contexte de ce modèle est présenté dans notre guide sur Sparkplug B et l'Unified Namespace.

Souvent, vous ne pouvez pas reconfigurer les équipements eux-mêmes. Le firmware peut être verrouillé, le fabricant peut avoir disparu, ou toucher un contrôleur de production exige une fenêtre de modification que vous n'obtiendrez pas ce trimestre. Node-RED se place au milieu et assure la traduction sans toucher à la source.

Concevoir la structure de topics ISA-95 cible

Définissez la structure cible avant d'écrire la moindre ligne de code. Un flow de mappage ne vaut que par l'espace de noms dans lequel il écrit. Voici une organisation pratique :

Niveau Exemple de valeur Remarques
Entreprise acme Souvent implicite via le broker ou un préfixe fixe
Site izmir Un par usine physique
Zone packaging Service ou atelier
Ligne line2 Ligne de production ou unité de procédé
Machine filler1 Cellule, machine ou actif
Point de donnée temperature Une mesure ou un état par topic

Quelques règles maintiennent l'espace de noms en bonne santé dans la durée :

  1. Utilisez des minuscules, de l'ASCII et aucun espace, pour que les topics soient faciles à saisir dans les abonnements et les filtres.
  2. Publiez un point de donnée par topic, ou un objet JSON bien défini par machine, et restez cohérent dans ce choix.
  3. Ne placez jamais d'horodatages, de numéros de séquence ou de valeurs dans le topic lui-même.
  4. Maîtrisez la profondeur. Sur le serveur MQTT public gratuit broker.connectmq.com, un topic peut compter au maximum 8 niveaux : ce tutoriel utilise donc un préfixe de démo à trois niveaux plus cinq niveaux ISA-95.

Pour la démo, nous utiliserons connectmq/demo/ab12cd comme préfixe (remplacez ab12cd par votre propre chaîne aléatoire), les entrées brutes sous connectmq/demo/ab12cd/raw/# et la sortie structurée sous connectmq/demo/ab12cd/<site>/<area>/<line>/<machine>/<point>. N'oubliez pas que l'espace public est lisible par tous : n'y envoyez que des données de test.

Comment fonctionne le mappage de topics MQTT avec Node-RED

Le flow comporte quatre parties :

  1. Un nœud mqtt in s'abonne aux topics bruts avec un wildcard.
  2. Un nœud function découpe le topic entrant, trouve l'équipement dans une table de correspondance, construit le nouveau topic et normalise le payload.
  3. Un nœud mqtt out publie le résultat. Si son propre champ topic est vide, il utilise msg.topic, ce qui est exactement ce que nous voulons.
  4. Une seconde sortie du nœud function envoie les messages non mappés vers un nœud debug, afin de repérer les équipements nouveaux ou mal configurés.

Si vous débutez avec les nœuds MQTT eux-mêmes, commencez par le tutoriel Node-RED MQTT, puis revenez ici.

La table de correspondance

Gardez la correspondance en un seul endroit. Pour quelques équipements, un objet JavaScript dans le nœud function suffit. Pour plusieurs dizaines, chargez-la depuis un fichier ou un retained message MQTT dans le contexte global, afin de pouvoir la mettre à jour sans redéployer.

Le nœud function

Collez ce code dans un nœud function et réglez-le sur 2 sorties :

// Les topics bruts ressemblent à : connectmq/demo/ab12cd/raw/<deviceId>/<rawPoint>
const PREFIX = "connectmq/demo/ab12cd";

// deviceId -> emplacement ISA-95
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" }
};

// nom de point brut -> nom de point normalisé et unité
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];
}

// Accepter des nombres simples, des chaînes ou des objets JSON avec un champ "value"
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];

Avec retain: true, tout nouvel abonné reçoit immédiatement la dernière valeur de chaque point de donnée, ce qui correspond au comportement attendu par la plupart des consommateurs d'un Unified Namespace. Le champ source conserve une trace du topic d'origine, ce qui aide beaucoup au débogage.

Flow importable

Vous pouvez importer le flow complet via Menu → Import. Après l'import, collez le code de la fonction ci-dessus dans le nœud function et remplacez le client ID et le préfixe par vos propres valeurs.

[
  {"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":[]}
]

Le nœud mqtt out laisse qos et retain vides afin que les valeurs définies dans le nœud function soient utilisées.

Tester le mappage avec mosquitto

Ouvrez un terminal et observez la partie structurée de l'arborescence :

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

Dans un second terminal, simulez des équipements côté brut :

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"

Les deux premiers messages devraient apparaître sous izmir/packaging/line2/filler1/temperature et izmir/utilities/main/meter7/power. Le troisième devrait s'afficher comme non mappé dans la barre latérale debug de Node-RED.

Éviter les boucles et autres pièges

L'erreur la plus courante dans les flows de remappage consiste à s'abonner à un wildcard qui correspond aussi à la sortie. Si le nœud mqtt in écoute connectmq/demo/ab12cd/# et que le flow republie sous le même préfixe, chaque message republié est reçu de nouveau, remappé de nouveau et finit par inonder le broker. Séparez les données brutes et structurées dans des branches distinctes de l'arborescence, et abonnez-vous uniquement à la branche brute.

Avec MQTT 5, vous pouvez aussi activer l'option no local sur le nœud mqtt in, pour que le client ne reçoive jamais les messages qu'il a lui-même publiés. C'est un filet de sécurité, pas un substitut à une bonne conception des topics, car une seconde instance Node-RED verrait toujours ces messages.

Autres pièges à vérifier :

Symptôme Cause probable Solution
Rien n'est republié La fonction renvoie null sur les deux sorties Vérifiez la sortie debug et le test parts.length
Le topic de sortie contient undefined Équipement présent dans la table mais niveau manquant Vérifiez que chaque entrée de la table comporte les quatre niveaux
D'anciennes valeurs réapparaissent après un renommage Retained messages sur l'ancien topic Publiez un payload retenu vide sur l'ancien topic pour l'effacer
Des chaînes au lieu de nombres en aval Le payload brut contenait des unités comme 23.7C Supprimez les unités dans le nœud function avant Number()
Déconnexions sur le broker public Plus de 10 messages par seconde et par client Limitez le débit avec un nœud delay ou utilisez votre propre broker

Passer le flow en production

Le broker public convient aux expérimentations, mais les données de production ont leur place sur un broker privé avec identifiants et TLS. Créez un broker sur ConnectMQ, copiez depuis le portail le nom d'utilisateur, le mot de passe et le client ID générés, et modifiez le nœud de configuration du broker ainsi :

  • Serveur broker.connectmq.com (ou l'hôte de votre broker indiqué dans le portail), port 8883, Use TLS activé
  • Client ID YOUR_CLIENT_ID, nom d'utilisateur YOUR_USERNAME, mot de passe YOUR_PASSWORD
  • Protocole MQTT V5

Le certificat du broker est délivré par Let's Encrypt : le magasin de confiance par défaut du runtime Node.js de Node-RED le valide sans qu'il soit nécessaire d'importer un fichier CA. Notre guide Node-RED MQTT sur TLS détaille les réglages TLS étape par étape.

Une fois les topics structurés en circulation, le portail ConnectMQ détecte l'espace de noms de forme ISA-95 et affiche vos équipements regroupés par site, zone, ligne et machine, et l'explorateur de topics en direct vous permet de vérifier l'arborescence sans ouvrir de client séparé. Si certaines de vos sources parlent déjà Sparkplug B, vous n'aurez peut-être pas du tout besoin de Node-RED pour elles : le broker peut décoder Sparkplug B et le republier dans des topics ISA-95, si bien que Node-RED n'a plus qu'à traiter les topics plats ordinaires. Pour les sources automates, le guide Node-RED Modbus montre comment lire des registres et les injecter dans le même flow de mappage.

Comme un broker managé prend en charge la disponibilité, les certificats et les limites de connexions, le flow de mappage devient la seule pièce mobile à maintenir. C'est en grande partie pour cette raison que les équipes passent d'un serveur Mosquitto auto-hébergé à un broker MQTT managé lorsque l'espace de noms grandit.

Faire évoluer la table de correspondance

Lorsque le nombre d'équipements dépasse ce que vous voulez éditer dans un nœud function, sortez la table :

  • Topic de configuration retenu : publiez la correspondance sous forme de message JSON retenu, par exemple sur config/topic-map, abonnez-vous-y dans un flow séparé et stockez-la avec global.set("topicMap", msg.payload). La fonction de mappage la lit avec global.get("topicMap"). Les mises à jour prennent effet sans redéploiement.
  • Fichier ou base de données : lisez un fichier CSV ou JSON au démarrage avec un nœud file in, ou interrogez une petite table, ce qui convient aux équipes qui gèrent déjà un registre d'actifs.
  • Règles par motif : si les identifiants d'équipement encodent leur emplacement, comme iz-pk-l2-filler1-temp, une règle fondée sur un découpage peut remplacer l'essentiel de la table, avec une petite table pour les exceptions.

Quel que soit votre choix, gardez la sortie des messages non mappés reliée à quelque chose de visible. Les nouveaux équipements apparaissent discrètement, et un avertissement de message non mappé est souvent le seul signe qu'une passerelle a été remplacée ou renommée.

Faites tourner votre mappage de topics Node-RED sur ConnectMQ

Un espace de noms propre a besoin d'un broker fiable en dessous. Inscrivez-vous sur app.connectmq.com pour l'offre gratuite Serverless, sans carte bancaire, créez un broker et connectez-y votre flow de mappage via TLS sur le port 8883. Vous bénéficiez de MQTT 5, de retained messages pour la dernière valeur de chaque point de donnée et d'une vue des équipements qui comprend vos niveaux ISA-95 dès le premier message. Pour un panorama plus large des options d'hébergement, lisez notre guide du broker MQTT cloud.

Questions fréquentes

Node-RED peut-il réécrire des topics sans nœud function ?

Oui. Un nœud change peut définir msg.topic avec une expression JSONata, ce qui suffit pour de simples remplacements de préfixe. Un nœud function est plus lisible dès que vous avez besoin d'une table de correspondance, d'une validation et d'une normalisation du payload dans la même étape.

Faut-il publier un topic par valeur ou un objet JSON par machine ?

Les deux fonctionnent dans un Unified Namespace. Une valeur par topic simplifie les tableaux de bord et les règles d'alerte, tandis qu'un objet par machine réduit le nombre de messages ; choisissez une convention et appliquez-la partout.

Comment empêcher le flow de republier ses propres messages ?

Abonnez-vous uniquement à une branche brute dans laquelle le flow n'écrit jamais, et activez l'option MQTT 5 no local sur le nœud mqtt in comme protection supplémentaire.

ConnectMQ remappe-t-il les topics de lui-même ?

Il republie les messages Sparkplug B dans des topics ISA-95, mais n'exécute pas de règles arbitraires de réécriture de topics. Pour les topics plats ou propres à un fabricant, effectuez le mappage dans Node-RED ou dans un autre client MQTT, comme montré ici.

Articles liés

Node-RED Dashboard 2.0 avec MQTT : jauges, graphiques et commandes

Un tutoriel pratique Node-RED Dashboard 2.0 : abonnez-vous à une télémétrie MQTT en direct, affichez-la sur des jauges et un graphique en courbe, et pilotez un appareil avec un interrupteur qui publie des commandes et reflète l'état réel.

9 min de lecture

Client Node-RED OPC UA vers MQTT avec des topics ISA-95

Utilisez node-red-contrib-opcua pour vous abonner aux variables OPC UA d'un automate ou d'un serveur de simulation, associer les node IDs à des topics MQTT de style ISA-95 et publier les valeurs avec leur qualité et leur horodatage.

10 min de lecture

Historique des graphiques Node-RED : conserver les données MQTT après un redémarrage

Les graphiques du Dashboard oublient tout quand Node-RED redémarre. Ce guide présente deux méthodes fiables pour conserver l'historique des graphiques Node-RED pour les données MQTT : le stockage de contexte sur fichier et une petite table SQLite, toutes deux rejouées dans Dashboard 2.0 au démarrage.

8 min de lecture

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.