IoT industriel

Passerelle Modbus RTU vers MQTT avec RS-485 : câblage, paramètres et code

Reliez des compteurs d'énergie et capteurs Modbus RTU sur RS-485 à une passerelle qui publie leurs mesures en MQTT. Câblage, terminaison, paramètres série, tables de registres et poller Python complet.

Bus RS-485 de compteurs d'énergie câblé à une petite passerelle Linux qui envoie des messages Modbus RTU vers MQTT dans le 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

Une passerelle Modbus RTU vers MQTT permet de récupérer les mesures de compteurs d'énergie, débitmètres, variateurs et capteurs RS-485 et de les publier vers un broker où tableaux de bord et outils d'analytique peuvent les exploiter. Ce tutoriel couvre la couche physique qui cause la plupart des problèmes (câblage, terminaison, paramètres série), la lecture d'une table de registres constructeur et une passerelle Python complète qui interroge plusieurs appareils sur un même bus et publie leurs valeurs en MQTT. Un Raspberry Pi ou n'importe quelle machine Linux avec un adaptateur USB vers RS-485 suffit pour suivre.

Ce qui distingue Modbus RTU de Modbus TCP

Le modèle de données Modbus est le même : coils, discrete inputs, holding registers et input registers. C'est le transport qui change. Modbus RTU fonctionne sur une liaison série, presque toujours RS-485, sous forme de bus multipoint. Un maître interroge jusqu'à 247 esclaves, chacun doté d'une adresse unique. Les trames sont binaires avec un CRC, et leurs limites sont définies par des silences sur la ligne.

Cela a des conséquences pratiques :

  • Un seul maître par bus. Si une GTB ou une IHM interroge déjà les compteurs, votre passerelle ne peut pas simplement se brancher sur les mêmes fils. Il vous faut un bus séparé, un appareil doté d'un second port ou une passerelle placée en amont des deux.
  • Le bus est lent et partagé. À 9600 bauds, chaque requête et réponse prend des dizaines de millisecondes, et les requêtes vers différents appareils sont sérialisées. Le plan d'interrogation compte.
  • Les problèmes électriques ressemblent à des problèmes logiciels. Une terminaison manquante ou une paire inversée produit des timeouts et des erreurs CRC qu'aucun code ne corrigera.

Si vos appareils sont sur Ethernet, utilisez plutôt notre tutoriel Modbus TCP vers MQTT.

Le matériel nécessaire

  1. Un ordinateur passerelle : un Raspberry Pi, un PC industriel ou n'importe quelle machine Linux.
  2. Un adaptateur USB vers RS-485, idéalement isolé, avec résistances de terminaison et de polarisation activables. L'isolation protège la passerelle des différences de potentiel de masse entre armoires.
  3. Du câble paire torsadée pour le bus, de préférence blindé, avec un troisième conducteur pour le commun de signal.
  4. Le manuel Modbus de chaque appareil. Il vous faut sa table de registres et ses paramètres série par défaut.

Un câblage RS-485 dans les règles

RS-485 est robuste lorsqu'il est câblé comme prévu, et frustrant sinon. Suivez ces règles :

  1. En guirlande, pas en étoile. Faites courir le bus d'un appareil à l'autre en ligne. De courtes dérivations sont tolérables ; les longues branches provoquent des réflexions.
  2. Terminer les deux extrémités. Placez une résistance de 120 ohms entre A et B aux deux extrémités physiques du bus, et nulle part ailleurs. De nombreux adaptateurs et compteurs disposent d'un interrupteur ou cavalier de terminaison.
  3. Polariser une seule fois. Les résistances de polarisation (fail-safe) maintiennent la ligne dans un état défini quand personne ne parle. Activez-les à un seul endroit, généralement l'adaptateur maître.
  4. Raccorder le commun. Malgré l'appellation « deux fils », reliez la masse de signal (souvent notée GND, COM ou C) entre les appareils pour que les récepteurs partagent une référence.
  5. Attention au marquage A/B. Les fabricants ne s'accordent pas sur la polarité de A, et certains utilisent D+ et D-. Si rien ne répond, inverser A et B sur l'adaptateur est un premier test légitime.
  6. Éloigner le bus des câbles de puissance. Faites passer le bus séparément des câbles moteurs et variateurs, et mettez le blindage à la terre à une seule extrémité.

Paramètres série : débit, parité et bits de stop

Tous les appareils d'un bus doivent utiliser les mêmes paramètres, et chacun doit avoir une adresse unique.

Paramètre Valeurs courantes Remarques
Débit (bauds) 9600, 19200, 38400 9600 et 19200 sont les valeurs par défaut les plus courantes ; les câbles longs favorisent les débits faibles
Bits de données 8 Modbus RTU utilise toujours 8 bits de données
Parité Paire, aucune, impaire La spécification Modbus prévoit la parité paire par défaut, mais de nombreux appareils sont livrés sans parité
Bits de stop 1 ou 2 Sans parité, la spécification exige 2 bits de stop, bien que de nombreux appareils acceptent 1
Adresse 1 à 247 Réglée en façade de l'appareil ou par logiciel ; doit être unique sur le bus

Notez les paramètres sous forme de code court comme 9600 8E1 ou 19200 8N2, et réglez chaque appareil en conséquence avant de brancher quoi que ce soit.

Lire une table de registres

Le manuel d'un appareil liste les registres dans un tableau avec adresse, description, type de données, unité et parfois facteur d'échelle. L'exemple ci-dessous est un compteur d'énergie triphasé hypothétique utilisé pour ce tutoriel ; les vrais compteurs diffèrent, prenez donc toujours les adresses dans votre propre manuel.

Description Fonction Adresse (base 0) Type Unité
Tension L1-N 04 input 0 float32 V
Courant L1 04 input 6 float32 A
Puissance active totale 04 input 52 float32 W
Fréquence 04 input 70 float32 Hz
Énergie active importée 04 input 72 float32 kWh

Trois points à vérifier dans chaque manuel : si les adresses commencent à zéro ou sont écrites sous la forme 30001 (soustrayez le décalage), si les valeurs 32 bits utilisent l'ordre mot de poids fort en premier ou mot de poids faible en premier, et si les registres entiers nécessitent un facteur d'échelle.

Prérequis sur la passerelle

sudo usermod -aG dialout $USER     # autoriser l'accès au port série, puis se déconnecter et se reconnecter
ls -l /dev/serial/by-id/           # chemin stable de votre adaptateur
python3 -m venv ~/rtu-gw && source ~/rtu-gw/bin/activate
pip install "pymodbus[serial]>=3.8,<3.9" "paho-mqtt>=2.0"

Utilisez le chemin /dev/serial/by-id/... plutôt que /dev/ttyUSB0, car les numéros des périphériques USB peuvent changer après un redémarrage.

Le code de la passerelle Modbus RTU vers MQTT

La passerelle interroge chaque compteur à tour de rôle, lit chaque bloc de registres en une seule requête lorsque c'est possible et publie un message JSON par compteur et par cycle. Regrouper les valeurs d'un compteur dans un seul message maintient un faible débit de messages, ce que vous recherchez sur un bus lent et sur une liaison montante facturée au volume.

# rtu_mqtt_gateway.py - interroge des compteurs Modbus RTU sur RS-485, publie en MQTT
import json, logging, struct, time, uuid
import paho.mqtt.client as mqtt
from pymodbus.client import ModbusSerialClient

logging.basicConfig(level=logging.INFO)
log = logging.getLogger("rtu-gw")

# En production, remplacez port par votre chemin /dev/serial/by-id/...
SERIAL = dict(port="/dev/ttyUSB0", baudrate=9600, parity="E", stopbits=1, bytesize=8, timeout=0.5)
POLL_SECONDS = 10
TOPIC_ROOT = "connectmq/demo/e5a1/plant1/energy"     # utilisez votre propre id aléatoire
MQTT_HOST, MQTT_PORT = "broker.connectmq.com", 1883   # production: 8883 with TLS
MQTT_USER, MQTT_PASS = None, None                     # production: "YOUR_USERNAME", "YOUR_PASSWORD"
CLIENT_ID = f"rtu-gw-{uuid.uuid4().hex[:8]}"          # production: "YOUR_CLIENT_ID"

# Compteurs sur le bus : adresse Modbus -> nom de topic
METERS = [
    {"unit": 1, "name": "mdb1_incomer"},
    {"unit": 2, "name": "mdb1_compressors"},
]


def f32(regs, offset, big_endian_words=True):
    words = regs[offset:offset + 2] if big_endian_words else regs[offset:offset + 2][::-1]
    return struct.unpack(">f", struct.pack(">HH", *words))[0]


def read_meter(client, unit):
    """Lit les input registers float32 du compteur hypothétique en deux requêtes de bloc."""
    block_a = client.read_input_registers(0, count=8, slave=unit)        # tension, courant
    block_b = client.read_input_registers(52, count=24, slave=unit)      # puissance .. énergie
    for rr in (block_a, block_b):
        if rr.isError():
            raise IOError(str(rr))
    a, b = block_a.registers, block_b.registers
    return {
        "voltage_l1": round(f32(a, 0), 1),
        "current_l1": round(f32(a, 6), 2),
        "power_total": round(f32(b, 0), 0),
        "frequency": round(f32(b, 18), 2),
        "energy_import": round(f32(b, 20), 1),
    }


def main():
    mq = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=CLIENT_ID)
    if MQTT_USER:
        mq.username_pw_set(MQTT_USER, MQTT_PASS)
        mq.tls_set()
    mq.will_set(f"{TOPIC_ROOT}/gateway", "offline", qos=1, retain=True)
    mq.on_connect = lambda c, u, f, rc, p: c.publish(f"{TOPIC_ROOT}/gateway", "online", qos=1, retain=True)
    mq.connect(MQTT_HOST, MQTT_PORT, keepalive=60)
    mq.loop_start()

    bus = ModbusSerialClient(**SERIAL)
    errors = {m["unit"]: 0 for m in METERS}

    while True:
        cycle_start = time.monotonic()
        if not bus.connected and not bus.connect():
            log.error("cannot open serial port %s", SERIAL["port"])
            time.sleep(5)
            continue
        for meter in METERS:
            topic = f"{TOPIC_ROOT}/{meter['name']}"
            try:
                values = read_meter(bus, meter["unit"])
                errors[meter["unit"]] = 0
                payload = {**values, "quality": "good",
                           "ts": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())}
                mq.publish(topic, json.dumps(payload), qos=1, retain=True)
                log.info("%s %s", meter["name"], values)
            except Exception as exc:
                errors[meter["unit"]] += 1
                log.warning("unit %s: %s", meter["unit"], exc)
                if errors[meter["unit"]] == 3:   # publier une qualité "bad" une fois, après 3 échecs
                    mq.publish(topic, json.dumps({"quality": "bad"}), qos=1, retain=True)
            time.sleep(0.05)                     # petit intervalle entre appareils sur le bus
        time.sleep(max(0, POLL_SECONDS - (time.monotonic() - cycle_start)))


if __name__ == "__main__":
    main()

Notez les décalages dans read_meter : le bloc B commence au registre 52, donc la fréquence au registre 70 correspond au décalage 18 et l'énergie importée au registre 72 au décalage 20. Se tromper sur ces décalages est le bug le plus courant quand on regroupe les lectures : affichez une fois les registres bruts pendant la mise en service.

Exécuter et vérifier

Lancez la passerelle, puis observez la sortie depuis n'importe quelle machine :

python rtu_mqtt_gateway.py
mosquitto_sub -h broker.connectmq.com -p 1883 -i rtu-viewer-e5a1 -t "connectmq/demo/e5a1/#" -v
connectmq/demo/e5a1/plant1/energy/mdb1_incomer {"voltage_l1": 231.4, "current_l1": 18.72, "power_total": 12890.0, "frequency": 50.01, "energy_import": 48213.6, "quality": "good", "ts": "2026-10-16T10:05:00Z"}

Le serveur MQTT public gratuit est lisible par n'importe qui : utilisez-le uniquement pour des données de test. Pour un vrai site, créez un broker privé et passez sur le port 8883 avec vos identifiants.

Dépannage Modbus RTU

Symptôme Cause probable À essayer
Aucun appareil ne répond A/B inversés, mauvais débit ou parité, mauvais port Inverser A/B, confirmer 8E1 ou 8N1, vérifier /dev/serial/by-id
Certains appareils répondent, d'autres non Adresse en double ou incorrecte, coupure de câblage Déconnecter tous les appareils sauf un et tester chacun
Erreurs CRC intermittentes Terminaison manquante, topologie en étoile, bruit des variateurs Terminer les deux extrémités, refaire le cheminement du câble, utiliser une paire torsadée blindée
Fonctionne à 9600, échoue à des débits plus élevés Câble long ou câblage médiocre Rester au débit le plus faible ou corriger le câblage
Code d'exception 2 (adresse de données illégale) Adresse hors de la table de l'appareil ou bloc couvrant un trou Découper le bloc en lectures plus petites
Timeouts aléatoires quand un autre système tourne Deux maîtres sur le même bus Donner à la passerelle son propre bus ou port

Passer à l'échelle

Un seul port RS-485 suffit pour quelques compteurs. Quand vous en avez des dizaines, envisagez un adaptateur par tableau électrique ou des convertisseurs Modbus RTU vers TCP qui permettent à une seule passerelle d'atteindre plusieurs bus via Ethernet, puis réutilisez le pont TCP. Publier sur changement plutôt qu'à chaque cycle, comme décrit dans notre guide du report by exception, garde aussi la liaison montante silencieuse. Pour les sites distants en données mobiles, consultez MQTT sur réseau cellulaire.

Un broker MQTT managé comme ConnectMQ évite d'avoir à exploiter un serveur pour ces passerelles. L'offre Serverless gratuite prend en charge jusqu'à 25 connexions, assez pour un pilote avec plusieurs passerelles, et l'offre Serverless payante monte jusqu'à 1 000. Les règles d'alerte à seuil du portail peuvent surveiller des valeurs comme power_total et journaliser une alerte lorsqu'une limite est franchie. Pour l'architecture qui sous-tend l'ensemble, lisez le guide de l'IoT industriel avec MQTT.

Démarrer avec ConnectMQ

Mettez vos compteurs RS-485 en ligne dès aujourd'hui. Inscrivez-vous sur app.connectmq.com, choisissez l'offre Serverless gratuite (sans carte bancaire), créez un broker et copiez le nom d'utilisateur, le mot de passe et le client ID générés dans le script de la passerelle. Les mesures de vos compteurs apparaîtront dans l'explorateur de topics en temps réel de ConnectMQ dès la fin de la première interrogation.

Questions fréquentes

Combien d'appareils Modbus RTU un bus RS-485 peut-il accueillir ?

Les adresses Modbus autorisent jusqu'à 247 appareils, mais la limite électrique dépend de la charge des transceivers ; le RS-485 standard supporte 32 charges unitaires, et de nombreux transceivers modernes présentent des fractions de charge. En pratique, c'est généralement le temps d'interrogation qui devient la limite en premier.

Puis-je lire des appareils Modbus RTU déjà interrogés par une GTB ?

Pas sur le même bus, car Modbus RTU n'autorise qu'un seul maître. Utilisez un second port sur l'appareil, une sortie de compteur séparée, ou demandez à l'intégrateur de la GTB d'exposer les données autrement.

Quel débit utiliser pour Modbus RTU ?

Celui que tous les appareils du bus prennent en charge, généralement 9600 ou 19200. Les débits faibles tolèrent mieux les câbles longs et les environnements bruyants.

Une passerelle Modbus RTU doit-elle publier à chaque interrogation ?

Pas nécessairement. Regrouper les valeurs d'un appareil dans un message par cycle, ou ne publier que sur changement significatif, limite le volume de messages sans perte d'information.

Articles liés

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.