Les topics MQTT ISA-95 placent la hiérarchie des équipements de l'usine directement dans le chemin du topic, si bien que acme/dallas/packaging/line-02/filler/telemetry indique à chaque abonné exactement d'où vient une valeur. Dans ce guide, vous verrez comment encoder chaque niveau ISA-95, comment concevoir la feuille du topic, comment planifier les abonnements avec wildcards avant d'en avoir besoin et quels pièges provoquent le plus de reprises une fois un namespace en production.
Pourquoi encoder l'ISA-95 dans les topics MQTT
Les topics MQTT sont des chaînes libres séparées par des slashs, et le broker MQTT ne leur attribue aucun sens au-delà de la correspondance avec les abonnements. Cette liberté est dangereuse à l'échelle d'une usine. Si chaque intégrateur invente sa propre structure, les consommateurs finissent avec une table de correspondance par source.
La hiérarchie des équipements ISA-95 résout ce problème en fournissant une structure qui existe déjà dans la documentation de l'usine, le MES et le système de maintenance. L'encoder dans les topics vous apporte trois propriétés gratuitement :
- La découvrabilité. Quiconque connaît l'usine peut trouver une valeur en parcourant l'arborescence.
- Le contexte. Le topic porte l'adresse de l'actif, les payloads n'ont donc pas besoin de la répéter.
- L'abonnement sélectif. Les wildcards correspondent naturellement à des périmètres organisationnels : un site, une zone, toutes les remplisseuses.
Si vous avez besoin d'un rappel sur les niveaux eux-mêmes, lisez d'abord l'article sur la hiérarchie des équipements ISA-95.
Le modèle de base
Le modèle canonique place les niveaux du plus large au plus étroit, suivis d'un espace de noms qui indique le type de données contenu dans le message :
<enterprise>/<site>/<area>/<work center>/<work unit>/<namespace>[/<detail>]
acme/dallas/packaging/line-02/filler/telemetry
acme/dallas/packaging/line-02/filler/state
acme/dallas/packaging/line-02/filler/alarms
acme/dallas/packaging/line-02/filler/events/batch-complete
acme/dallas/packaging/line-02/filler/cmd/stop
Règles pour chaque niveau
| Niveau | Règle | Exemple |
|---|---|---|
| Entreprise | Un nom court et stable ; à omettre seulement si le broker ne sert qu'une seule société | acme |
| Site | Un code d'emplacement qui survit aux réorganisations | dallas, izmir |
| Zone | Zone fonctionnelle ou produit | packaging, utilities |
| Centre de travail | Ligne, cellule, cellule de process, unité ou zone de stockage | line-02, cell-03 |
| Unité de travail | Machine ou unité, idéalement par numéro d'actif | filler, cnc-01 |
| Espace de noms | Vocabulaire fixe | telemetry, state, alarms, events, cmd |
Gardez une profondeur fixe pour les topics d'équipement. Lorsque chaque unité de travail se trouve au niveau cinq, +/+/+/+/+/state renvoie l'état de toutes les machines de l'entreprise, et la position de chaque niveau dans le chemin est garantie.
Concevoir la feuille
L'espace de noms et tout ce qui le suit concentrent l'essentiel des débats de conception. Il existe deux grandes options pour la télémétrie.
Un objet par équipement. Toutes les valeurs courantes d'une machine vont dans un seul document JSON sur .../filler/telemetry. C'est compact, les valeurs liées restent cohérentes dans le temps, et il ne faut qu'un seul retained message par machine.
{ "ts": "2026-10-16T08:15:02Z", "speed_bpm": 118.4, "temp_c": 41.2, "good_count": 18342, "v": 1 }
Un topic par mesure. Chaque valeur a son propre topic, comme .../filler/telemetry/speed_bpm, avec un payload scalaire ou un petit JSON. Cela correspond naturellement aux outils orientés tags comme les systèmes SCADA, et les consommateurs peuvent s'abonner exactement aux signaux dont ils ont besoin.
Les deux sont valables. Choisissez par espace de noms et documentez-le. Un hybride courant consiste en un objet pour la télémétrie et des valeurs retenues par topic pour state et quelques KPI clés.
Planifier vos abonnements avec wildcards
Avant de figer la structure, notez les abonnements dont vos consommateurs auront besoin et vérifiez que chacun tient en un seul filtre avec wildcards :
| Consommateur | Besoin | Abonnement |
|---|---|---|
| Tableau de bord de site | Tout un site | acme/dallas/# |
| Superviseur du conditionnement | Tous les états d'une zone | acme/dallas/packaging/+/+/state |
| Analytique de parc | Toutes les remplisseuses, partout | acme/+/+/+/filler/telemetry |
| Service d'alarmes | Toutes les alarmes | acme/+/+/+/+/alarms |
| Historian | Toute la télémétrie | acme/+/+/+/+/telemetry |
Si un périmètre nécessaire exige plusieurs abonnements ou un filtrage côté client, la structure travaille contre vous. Cela signifie généralement qu'un niveau manque, que l'ordre est incohérent ou que le vocabulaire des feuilles est trop lâche.
Publier des topics ISA-95 depuis le code
Générer les topics en un seul endroit évite que des fautes de frappe créent des branches parasites. Ce helper Python construit et valide le chemin, puis publie l'état et la télémétrie retenus. Installez le client avec pip install "paho-mqtt>=2.0". Il utilise le serveur MQTT public gratuit avec un préfixe de démo ; sur un broker privé, définissez PREFIX avec le nom de votre entreprise.
import json
import re
import threading
from dataclasses import dataclass
from datetime import datetime, timezone
import paho.mqtt.client as mqtt
PREFIX = "connectmq/demo/r8t5" # sur votre propre broker : "acme"
SAFE = re.compile(r"^[a-z0-9]+(-[a-z0-9]+)*$")
NAMESPACES = {"telemetry", "state", "alarms", "events", "cmd"}
@dataclass(frozen=True)
class Asset:
site: str
area: str
work_center: str
work_unit: str
def topic(self, namespace: str) -> str:
for level in (self.site, self.area, self.work_center, self.work_unit):
if not SAFE.match(level):
raise ValueError(f"invalid level: {level!r}")
if namespace not in NAMESPACES:
raise ValueError(f"unknown namespace: {namespace!r}")
return f"{PREFIX}/{self.site}/{self.area}/{self.work_center}/{self.work_unit}/{namespace}"
filler = Asset("dallas", "packaging", "line-02", "filler")
connected = threading.Event()
def on_connect(client, userdata, flags, reason_code, properties):
print("connected:", reason_code)
connected.set()
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="isa95-pub-r8t5")
client.on_connect = on_connect
client.connect("broker.connectmq.com", 1883, keepalive=60)
client.loop_start()
connected.wait(timeout=10)
client.publish(filler.topic("state"), "running", qos=1, retain=True).wait_for_publish()
telemetry = {"ts": datetime.now(timezone.utc).isoformat(), "speed_bpm": 118.4, "temp_c": 41.2, "v": 1}
client.publish(filler.topic("telemetry"), json.dumps(telemetry), qos=1, retain=True).wait_for_publish()
print("published to", filler.topic("telemetry"))
client.loop_stop()
client.disconnect()
Vérifiez avec un wildcard correspondant au tableau ci-dessus :
mosquitto_sub -h broker.connectmq.com -p 1883 -i isa95-sub-r8t5 -t "connectmq/demo/r8t5/+/packaging/+/+/state" -v
Le serveur MQTT public gratuit est lisible par tous et autorise 8 niveaux de topic, c'est pourquoi le préfixe de démo remplace ici le niveau entreprise. Réservez les vrais noms d'actifs et les vraies valeurs à un broker privé.
Intégrer Sparkplug B dans l'arborescence ISA-95
Les topics Sparkplug B suivent un format fixe, spBv1.0/<group>/<type>/<edge node>/<device>, avec des payloads Protobuf. Ils ne suivent pas l'ISA-95, et les consommateurs qui ne connaissent pas Sparkplug ne peuvent pas les lire. Plutôt que d'abandonner Sparkplug à l'edge, republiez ses données dans votre arborescence ISA-95.
ConnectMQ le fait automatiquement : il décode les payloads Sparkplug B, Protobuf ou JSON, et les republie dans des topics ISA-95. Par exemple, spBv1.0/factory1/DDATA/line1/cnc1 est republié sous connectmq/factory1/production/line1/cnc1/telemetry. Choisissez des identifiants de groupe et d'edge node Sparkplug qui portent des noms d'usine parlants, afin que les chemins ISA-95 obtenus soient lisibles. Les compromis entre les deux approches sont abordés dans MQTT, Sparkplug B et le Unified Namespace.
Pièges à éviter
- Slash initial.
/acme/dallascrée un premier niveau vide et scinde les abonnements. - Casse mixte. Les topics sont sensibles à la casse ;
Line-02etline-02sont des branches différentes. - Noms de protocole ou de fournisseur dans le chemin.
acme/dallas/opcua/...lie les consommateurs à la technologie d'intégration du moment. - Profondeur variable. Sauter un niveau pour une machine casse les wildcards positionnels. Utilisez plutôt un centre de travail de substitution.
- Horodatages ou identifiants dans les topics. Des topics comme
.../telemetry/20261016créent des branches sans limite et encombrent les retained messages. - Retenir des événements. Retenez l'état, pas les flux d'événements ; un nouvel abonné ne doit pas recevoir un « batch complete » périmé.
- Des commandes sans requête ni réponse. Utilisez les response topics et les correlation data de MQTT 5 pour
cmd, afin que le demandeur sache si la commande a réussi. - Des chemins profonds sur des liaisons contraintes. Les topics longs ajoutent des octets à chaque message. Les topic aliases de MQTT 5 réduisent ce surcoût sans changer la conception.
Documenter la norme avant le déploiement
Une norme de topics qui n'existe que dans la tête d'un ingénieur sera enfreinte dès le deuxième intégrateur. Avant la mise en service de la première passerelle, rédigez une norme d'une page qui couvre :
- L'ordre des niveaux et les caractères autorisés pour chacun.
- La liste des espaces de noms et ce que chacun peut contenir, avec un exemple de payload.
- Les topics retenus et ceux qui ne le sont pas.
- Le format d'horodatage, le fuseau horaire et la convention d'unités.
- Qui approuve les nouveaux sites, zones et centres de travail, et comment les demandes sont faites.
Remettez cette page à chaque intégrateur et à chaque fournisseur de passerelles, avec le helper de topics ci-dessus ou son équivalent dans leur langage. Lorsque la norme et le code concordent, la plupart des erreurs sont détectées avant même d'atteindre le broker.
La sécurité mérite une remarque à part : délivrez un identifiant par passerelle et par application pour que chaque publicateur soit identifiable et révocable, et utilisez TLS sur le port 8883 partout en dehors du labo. Nos bonnes pratiques de sécurité MQTT couvrent le reste.
Pour des arborescences complètes prêtes à copier, consultez la structure des topics UNS. Pour l'architecture qui les entoure, le guide MQTT pour l'IoT industriel est le point de départ.
Exploitez des topics MQTT ISA-95 sur ConnectMQ
ConnectMQ est un broker MQTT managé qui comprend les structures de topics industrielles : il détecte les topics ISA-95 et Unified Namespace, affiche les appareils par niveau ISA-95 dans le portail et republie Sparkplug B dans votre arborescence. Créez un broker avec l'offre gratuite Serverless sur app.connectmq.com, sans carte bancaire, et publiez votre première branche ISA-95 dès aujourd'hui.
Questions fréquentes
Le nom de l'entreprise doit-il être le premier niveau du topic ?
Oui, si le broker est susceptible de servir un jour plus d'une société ou d'une business unit. Pour un broker mono-société, vous pouvez l'omettre, mais l'ajouter plus tard implique de renommer tous les topics.
Comment gérer les machines qui n'appartiennent à aucune ligne ?
Attribuez-leur un centre de travail de substitution comme standalone ou shared pour qu'elles se trouvent à la même profondeur que tout le reste.
Peut-on utiliser des espaces ou des majuscules dans les topics MQTT ISA-95 ?
MQTT les autorise, mais ils provoquent des bugs subtils. Utilisez uniquement des lettres minuscules, des chiffres et des tirets.
Les topics ISA-95 fonctionnent-ils avec MQTT 3.1.1 ?
Oui. La conception des topics est indépendante de la version. MQTT 5 ajoute des compléments utiles comme les topic aliases et les response topics pour les commandes.



