Modbus TCP

Modbus TCP, introduction

Modbus TCP ou Modbus TCP/IP hérite de la simplicité et de la robustesse (en conservant la structure des messages, la communication basée sur les registres, etc.) du protocole Modbus original, en y ajoutant la fiabilité et l’interopérabilité de TCP/IP.

TCP assure une livraison fiable et ordonnée des messages Modbus sur le réseau en fournissant des fonctions telles que la segmentation des données, l’accusé de réception et la retransmission.

La couche IP assure l’adressage, le routage et la livraison des paquets. Il utilise les adresses IP pour identifier les dispositifs source et destination dans un réseau.

L’adressage des appareils dans Modbus TCP/IP est géré au niveau IP du réseau. Contrairement à Modbus RTU ou Modbus ASCII, où chaque appareil se voit attribuer un numéro d’esclave unique compris entre 1 et 247, les appareils d’un réseau Modbus TCP/IP sont adressés à l’aide de leur adresse IP.

Modbus TCP utilise le port : 502 .

Modèle Client - Serveur

Modbus TCP utilise un modèle de communication de type Client - Serveur entre des appareillages (devices) connectés sur un réseau Ethernet. Ce modèle désigne un appareil comme Client qui initie des requêtes, tandis que d’autres appareils agissent comme des Serveurs qui traitent ces requêtes et envoient des réponses.

Le modèle Client - Serveur est basé sur 4 types de messages :

  • Modbus Request : le message est envoyé sur le réseau par le Client pour initier la transaction
  • Modbus Indication : le message est reçu du côté Serveur
  • Modbus Response : la réponse est renvoyée par le Serveur
  • Modbus Confirmation : la réponse est reçue côté Client

Les termes Client et Serveur sont souvent la source de confusions. Un moyen mémotechnique simple est de penser à une commande dans un bar.

En tant que Client, je fais une requête au Serveur (Request) qui m’apporte la boisson demandée (Response).

Si l’on fait l’analogie avec le Protocole Master / Slave de Modbus RTU, le Client correspond au Master tandis que le Serveur correspond au Slave. Dans un modèle Client - Serveur, on peut avoir plusieurs clients.

Architecture d’un réseau Modbus

Sur ce schéma, nous observons :

  • 2 Clients ModBus TCP
  • 2 Serveurs Modbus reliés en Ethernet
  • 2 Serveurs en Modbus série RTU (2 Slaves) reliés vers Modbus TCP avec une Gateway (passerelle) de type Server TCP/IP
  • 1 Client en Modbus série RTU (1 Master) relié vers Modbus TCP avec une Gateway (paserelle) de type Client TCP/IP

Sur une architecture Modbus TCP, il peut y avoir plusieurs clients. Les Gateways permettent de faire la liaison entre les “devices” en Modbus série RTU avec le réseau Modbus TCP.

Trame Modbus TCP

Contrairement au Modbus RTU, Modbus TCP ne nécessite pas de code de correction d’erreur (CRC) car les couches basses Ethernet assurent déjà cette fonction.

La structure de base Modbus Function Code et Data reste identique.

Le Slave Address et le CRC du Modbus RTU disparaissent au profit du MBAP Header, le ModBus Application Protocol. L’ensemble MBPA Header + Function Code + Date forment l’ADU pour Application Data Unit.

Le MBAP Header contient les champs suivants :

Name Length (bytes) Function
Transaction identifier 2 Pour la synchronisation des messages entre le serveur et le client
Protocol identifier 2 0 pour Modbus/TCP
Length field 2 Nombre d’octets contenus dans le message Modbus
Unit identifier 1 Adresse du Serveur (255 if not used), traité comme l’adresse de Slave dans une liaison Modbus série (RTU)

On remarque que le Unit ID remplace le Slave Address du protocole RTU. Le Unit identifier est utilisé dans Modbus TCP dans le cas ou les “devices” sont de type composite, comme une passerelle Modbus/TCP qui communique avec des élements Modbus RTU. La spécification Modbus TCP précise:

Unit ID : Use it only with serial gateway. The Modbus TCP server ignore this value

Exemple Modbus TCP et Modbus Serial

Dans cet exemple, deux centrales de mesure d’énergie Socomec M-50 sont utilisées. Il s’agit de Gateway type Server TCP/IP qui permettent de remonter les données des modules de mesure U-10 et I-35, qui eux, communiquent en Modbus Serial (RTU). Les modules I35 et U10 ont les adresses Modbus @6 et @5 respectivement.

Si l’on souhaite récupérer les données du module I-35 du TGBT 1, il faudra :

  • faire une requête Modbus TCP sur l’@IP 192.168.1.4 de la Gateway M-50 sur le port 502
  • mettre 06 dans Unit identifier pour communiquer avec le module I-35

Les trames Modbus TCP

Exemple d’analyse d’une trame Modbus TCP/IP en hexadecimal

La structure de trame Modbus TCP suivante :

Transaction identifier Protocol identifier Length Unit identifier Function code Data
2 bytes 2 bytes 2 bytes 1 byte 1byte n bytes

12 34 00 00 00 06 01 03 00 01 00 02

  • 0x12 et 0x34 : Transaction ID = 0x1234 (2 bytes), c’est un numéro unique pour indentifier la transaction entre le client Modbus TCP et le serveur. L’octet de poids fort (High byte) du transaction ID est 0x12 et l’octet de poids faible (Low byte) est 0x34
  • 0x00 et 0x00 : Protocol identifier, octet de poids fort (high byte) et octet de poids faible (low byte)
  • 0x00 et 0x06 : Longueur (Length high byte and low byte). La longueur du message Modbus est de 6 octets qui incluent: unit identifier (slave address) (1 octet), function code (1 octet), octet de poids fort du registre d’adresse à lire (1 octet), octet de poids faible du registre d’adresse à lire (1 octet) et le nombre de registres de données (2 octets = octets de poids fort (high) et faible (low) du nombre de registres à lire / écrire)
  • 0x01 : Unit identifier = adresse du slave
  • 0x03 : Function code (ici : Read Multiple Holding Registers)
  • 0x00 et 0x01 : high byte et low byte du registre d’adresse à lire. Le registre d’adresse à lire dans notre cas est 0x0001.
  • 0x00 and 0x02 : high byte and low byte du nombre de registres à lire. Le nombre de registres à lire est 0x0002. (càd on lit 2 registres (soit 2 Word))

Encodage des données

Modbus utilise la représentation “Big-Endian” pour les adresses et données. Cela signifie que lorsqu’une quantité numérique plus grande qu’un octet est transmise, l’octet le plus significatif est envoyé en premier. Par exemple :

Register size value
16 bits 0x1234 The first byte sent is 0x12 then 0x34

Analyse de “Function Code” Modbus

On trouveras ci dessous les données (data) associées à quelques Function Code Modbus. L’objectif n’est pas d’être exhaustif mais d’étudier le fonctionnement de quelques fonctions pour être capable de s’adapter facilement aux autres.

FC 03 (0x03) Read Holding Register

La fonction FC 03 permet de lire le contenu de registres. Les registres correspondent à des zones mémoire du Serveur (Slave) dont les données sont représentées sur 16 bits (Word)

Pour une requête (Query ou Request) de type FC 03, 2 octets serviront à définir l’adresse de départ de la zone mémoire à lire et 2 octets servent à indiquer le nombre de registres que l’on souhaite lire à partir de l’adresse de départ. Ces 4 octets sont contenus dans le champs Data.

Dans le champs Data de la réponse (Response), on trouvera un octet qui indiquera le nombre de données en octets contenu dans le message et 2*n octets des valeurs mémoire que l’on souhaitait lire.

Exemple QModMaster pour FC 03

Avec le logiciel QModMaster, je génère une requête Modbus TCP sur le Unit-ID 1 (Slave Addr), avec la Function Code 0x03, à partir de l’adresse de départ 2 et un nombre de registres de 4

Le logiciel QModMaster génère la trame (Tx) : 00 01 00 00 00 06 01 03 00 02 00 04. L’analyse de l’encart ADU de la trame de Requête permet de retrouver les informations.

En réponse (Rx), le logiciel reçoit du device interrogé la trame suivante : 00 01 00 00 00 0B 01 03 08 00 00 00 00 00 00 00 00

  • Length = 000B ce qui donne 11 en décimal. Si l’on compte, on à bien 11 octets qui suivent 01 03 08 00 00 00 00 00 00 00 00
  • Byte Count = 08. Comme on a demandé de lire le contenu de 4 Registres (4 Word), on a bien 8 bytes (octets) en réponse. 00 00 00 00 00 00 00 00 (Les valeurs sont toutes à 0 car la plage mémoire lue était à 0)

FC 15 (0x0F) Write Multiple Coils

La fonction FC 15 (ou 0x0F) permet d’écrire sur de multiples Coils (bit).

Pour une requête (Query) de type FC 15, 2 octets servirons à définir l’adresse de départ de la zone mémoire à lire et 2 octets servent à indiquer le nombre de Coils (bits) que l’on souhaite lire à partir de l’adresse de départ. Ces 4 octets sont contenus dans le champs Data.

Exemple QModMaster pour FC 15 (0x0F)

Avec le logiciel QModMaster, je génère une requête Modbus TCP sur le Unit-ID 1 (Slave Addr), avec la Function Code 0x0F, à partir de l’adresse de départ 6 et un nombre de Coils de 12. J’ai affecté aux 12 Coils la valeur binaire suivante : 0101 1111 0001

En analysant la contenu de l’ADU de la trame générée par QModMaster, on remarque plusieurs choses :

  • Quantity of register : 000C -> bug du logiciel, il devrait y avoir écrit Quantity of Coils
  • Byte Count : 02, pour les 12 Coils sont codés sur 2 octets (Byte). Si l’on avait voulu écrire 18 Coils, on aurait eu besoin de 3 octets, etc.

Plus étrange est la Valeur de Output Values :

  • Output Values = FA 08

alors qu’on aurait pu s’attendre à 5F 10 pour qu’on soit sur les 2 octets !

Explications : Pour chaque octet, on envoie d’abord le bit de poids le plus faible. Si l’on reprend les valeurs des 12 coils à envoyer 0101 1111 0001

  • la première étape est déjà de coder les valeurs sur 2 octets : 0101 1111 0001 0000
  • ensuite, on sépare les 2 octets : 01011111 00010000
  • on envoie les bits du LSB au MSB pour chaque octet ce qui donne 11111010 00001000
  • si l’on traduit en hexa FA 08: ce qui correspond au contenu de l’encart ADU.

La trame de réponse est la suivante : 00 01 00 00 00 06 01 0F 00 06 00 0C

Rien de particulier à décoder, on retrouve :

  • Start Address = 00 06
  • Length = 00 0C soit les 12 Coils

Analyse Réseau Modbus

Encapsulation d’une trame Modbus TCP dans une trame Ethernet TCP / IP

La trame Modbus est ensuite encapsulée dans une trame Ethernet. Des champs supplémentaires s’ajoutent, le Ethernet Header qui va contenir les adresses MAC, le IP Header qui va contenir les adresses IP de source et de destination ainsi que les numéros de port, le TCP Header qui contient les paramètres permettant la transmission en mode connecté et le Ehernet CRC qui permet de vérifier la transmission sans erreur de la trame Ethernet.

Etablissement de la connexion Modbus

La messagerie Modbus doit fournir une “socket” d’écoute sur le port 502, qui permet d’accepter une nouvelle connexion et d’échanger des données avec d’autres appareils.

Lorsque le service de messagerie doit échanger des données avec un serveur distant, il doit ouvrir une nouvelle connexion client avec un Port 502 distant afin d’échanger des données avec celui-ci. Le port local doit être supérieur à 1024 et différent pour chaque connexion client.

Capture Wireshark et analyse

Pour analyser les trames Ethernet, j’utilise l’analyserur de paquets Wireshark. J’utilise QModMaster comme Client Modbus TCP sur un PC portable et un automate Wago 750-880 comme serveur Modbus. Cet automate est associé à une carte à 4 entrées TOR 750-423 et une carte à 4 sorties TOR 750-504. Le montage est le suivant :

L’idée est de générer les requêtes avec QModMaster pour lire les entrées TOR et écrire sur les sorties TOR et d’analyser les trames Ethernet associées au protocole Modbus TCP avec Wireshark.

Configuration de QModMaster

Configuration de l’adresse IP Configuration du Base Address
On configure ici l’adresse IP du Server avec lequel on souhaite communiquer, ici le PLC Wago 750-880 qui à l’@IP 192.168.1.128 Ici on configure l’adresse mémoire de départ du Serveur à 0. Certains modules commencent leurs plages mémoire à l’adresse 1, il faut donc modifier la valeur ici
⚠️
Vous remarquerez que l’adresse IP est écrite 192.168.001.128 et non 192.168.0.128 C’est une contrainte du logiciel QModMaster, il faut penser à l’écrire de la sorte sinon cela ne fonctionne pas.

La carte Wago à 4 entrées digitales a été adressée à %IW0. La documentation nous indique que nous pouvons lire la valeur en Modbus à l’adresse de registre 0 avec un Read Holding Register (0x03).

La carte Wago à 4 sorties digitales a été adressée à %QW0. La documentation nous indique que nous pouvons lire la valeur en Modbus à l’adresse de registre 0 avec un Write Single Register (0x06).

Read Holding Register FC 0x03

Sur l’automate Wago 750-880, j’ai placé les 4 Digital-Input à 1. J’ai lancé une requête avec QModMaster

QModMaster nous retourne la valeur 15 en décimal, ce qui est correct car cela correspond aux 4 bits placés à 1 1111. L’analyse au moniteur de QModMaster nous indique dans Register Value : 00 0F ce qui correspond à la valeur 15 codée sur un Word

Analyse de la requête avec Wireshark

La capture avec Wireshark donne :

Pour la trame de requête (Query) avec le décodage de la trame suivant :

Frame 943: 66 bytes on wire (528 bits), 66 bytes captured (528 bits) on interface \Device\NPF_{D296DD04-C873-4280-A194-BD27AE8C4C2C}, id 0
Ethernet II, Src: 80:6d:97:4b:a4:7a (80:6d:97:4b:a4:7a), Dst: WAGOKontaktt_08:57:c9 (00:30:de:08:57:c9)
    Destination: WAGOKontaktt_08:57:c9 (00:30:de:08:57:c9)
    Source: 80:6d:97:4b:a4:7a (80:6d:97:4b:a4:7a)
    Type: IPv4 (0x0800)
Internet Protocol Version 4, Src: 192.168.1.123, Dst: 192.168.1.128
Transmission Control Protocol, Src Port: 50518, Dst Port: 502, Seq: 1, Ack: 1, Len: 12
Modbus/TCP
Modbus

La trame en format hexa et Ascii Dump est la suivante :

0000   00 30 de 08 57 c9 80 6d 97 4b a4 7a 08 00 45 00   .0..W..m.K.z..E.
0010   00 34 7d 85 40 00 80 06 f8 f2 c0 a8 01 7b c0 a8   .4}.@........{..
0020   01 80 c5 56 01 f6 1e f5 91 b3 09 aa 49 55 50 18   ...V........IUP.
0030   fa f0 64 84 00 00 00 01 00 00 00 06 01 03 00 00   ..d.............
0040   00 01                                             ..

Si l’on analyse cette trame, on observe que :

  • les 6 premiers octets correspondent à l’adresse MAC de la destination, càd, celle de l’automate Wago : 00 30 de 08 57 c9
  • les 6 octets suivants correspondent à l’adresse MAC de la source (le PC) : 80 6d 97 4b a4 7a
  • 08 00 indique qu’il s’agit d’une adresse IPv4

on saute quelques octets pour arriver à c0 a8 01 7b

  • c0 a8 01 7b correspond à l’@IP de la source (le PC). Il suffit de convertir chaque octet en binaire pour retrouver l’adresse IP en décimal pointé : 192.168.1.123
  • les 4 octets suivants correspondent à l’adresse IP de la destination (l’automate Wago). c0 a8 01 80 qui donnent 192.168.1.128
  • les deux octets suivants correspondent au numéro du port de la source (PC) : c5 56 soit en décimal 50518
  • les deux octets suivants correspondent au numéro du port de la destination (Wago): 01 f6 ce qui donne 502 soit le port d’écoute du protocole Modbus TCP ;)

on saute quelques octets pour arriver à 00 01 00 00 00 06 01 03 00 00 00 01 qui correspond à la trame Modbus TCP que dont on a déjà décoder un exemple plus haut.

  • 00 01 Transaction ID
  • 00 00 Protocol ID -> Modbus TCP
  • 00 06 Length
  • 01 Unit ID
  • 03 Function ID -> Read Holding Register ;)
  • 00 00 Adresse de départ -> 0
  • 00 01 Nombre de registres à lire -> 1

Analyse de la réponse avec Wireshark

Cette fois-ci c’est la trame correspond à la réponse (Response) du Server (l’automate Wago)

Pour la trame de réponse (Response) avec le décodage de la trame suivant :

Frame 944: 65 bytes on wire (520 bits), 65 bytes captured (520 bits) on interface \Device\NPF_{D296DD04-C873-4280-A194-BD27AE8C4C2C}, id 0
Ethernet II, Src: WAGOKontaktt_08:57:c9 (00:30:de:08:57:c9), Dst: 80:6d:97:4b:a4:7a (80:6d:97:4b:a4:7a)
Internet Protocol Version 4, Src: 192.168.1.128, Dst: 192.168.1.123
Transmission Control Protocol, Src Port: 502, Dst Port: 50518, Seq: 1, Ack: 13, Len: 11
Modbus/TCP
Modbus

La trame en format hexa et Ascii Dump est la suivante :

0000   80 6d 97 4b a4 7a 00 30 de 08 57 c9 08 00 45 00   .m.K.z.0..W...E.
0010   00 33 00 e3 40 00 40 06 b5 96 c0 a8 01 80 c0 a8   .3..@.@.........
0020   01 7b 01 f6 c5 56 09 aa 49 55 1e f5 91 bf 50 18   .{...V..IU....P.
0030   3e 80 0f ec 00 00 00 01 00 00 00 05 01 03 02 00   >...............
0040   0f                                                .

Si l’on analyse cette trame, on observe que :

  • les 6 premiers octets correspondent à l’adresse MAC de la destination, càd, celle de du PC maintenant ! 80 6d 97 4b a4 7a
  • les 6 octets suivants correspondent à l’adresse MAC de la source, càd l’automate maintenant : 00 30 de 08 57 c9

Pour les adresses IP, le principe restera identique et les adresses IP de source et de destination seront inversées.

De même pour les numéros de ports, le port de destination côté PC sera également le 502

Pour le plaisir, nous allons encore analyser la trame Modbus TCP 00 01 00 00 00 05 01 03 02 00 0F

  • 00 01 Transaction ID
  • 00 00 Protocol ID -> Modbus TCP
  • 00 05 Length
  • 01 Unit ID
  • 03 Function ID -> Read Holding Register ;)
  • 02 Size of data in byte (2 byte = 1 Word)
  • 00 0F -> valeur contenu dans le Word, ce qui donne 15 en décimal, càd, ce qu’il fallait trouver !

Remarques sur les captures Wireshark

Faire des analyses Wireshark ne constituent pas le quotidien de l’automaticien. Il est cependant intéressant de comprendre la structure d’une trame et des informations qui y sont codées, notamment quand on se retrouve face à des problématiques de communication pour voir si les trames sont générées correctement ou si c’est le Serveur Modbus qui est défectueux.

Conclusion :

Avantages de Modbus

ℹ️
  • Bien que le protocole soit ancien, de nouveaux produits continuent de l’intégrer car c’est efficace et facile à intégrer.
  • De très nombreux fabricants l’intègrent dans leurs gammes produits. On retrouve également beaucoup ModBus dans la gestion technique du bâtiment.
  • De nombreux logiciels permettent de communiquer en ModBus: Node-Red, LabView, Matlab, tous les SCADA, … ce qui permet une excellente inter-opérabilité.
  • Il n’y a pas de drivers spécifiques à installer, pas de logiciel propriétaire qui est associé, pas de versions de firmware non-compatibles. On est toujours capable de piloter une machine Modbus de 30 ans aujourd’hui avec des logiciels à jour !

Remarque Perso : C’est un protocole que j’apprécie beaucoup. C’est du Low-Tech fiable dont on maîtrise toutes les couches logicielles et dont je peux maîtriser la perennité pour de nombreuses années.

Les limitations de Modbus

  • Modbus est un protocole de la fin des années 1970 pour communiquer avec des PLC/ Le nombre de types de données est réduit (coil, register) et les structures ou grands objets binaires ne sont pas supportés.
  • Il n’y a pas de standard pour connaître la description d’un objet. Par exemple savoir qu’une valeur de registre représente une donnée entre de température et 0 et 150°C.
  • Modbus est basé sur le mode Client/Server (Master/Slave) avec un accès en Polling, il n’y a pas de mécanisme pour que le Slave (Server) envoie des données au Master (Client) suite à un évenement ou par publication/abonnement comme le MQTT. Cela consomme de la bande passante inutilement.
  • Modbus a été conçu à une époque où la cybersécurité n’était pas encore une préoccupation majeure, et il ne dispose pas de fonctions de sécurité intégrées. Le protocole ne prend pas en charge le cryptage ou l’authentification, ce qui signifie que les données transmises via Modbus TCP/IP peuvent être facilement interceptées et modifiées. Il est également facile pour des appareils non autorisés de rejoindre un réseau Modbus et de commencer à envoyer des commandes.
  • Modbus TCP/IP ne prend pas en charge certaines fonctions modernes de mise en réseau. Par exemple, il ne prend pas en charge la découverte automatique des appareils, ce qui signifie que lorsqu’un nouvel appareil est ajouté à un réseau, son adresse et d’autres détails doivent être configurés manuellement.
  • Comme Modbus TCP est basé sur la couche TCP, cela génère des latences et de la gigue incompatible avec le contrôle de processus temps réel (Motion Control par exemple)

Bilan

Comme pour tous les choix techniques, la clé est de comprendre les forces, les faiblesses et l’adéquation du Modbus TCP/IP aux besoins spécifiques de l’application envisagée.

Pour de la surveillance de paramètres du pilotage basique de machine, Modbus TCP reste pertinent aujourd’hui (et demain). Si l’on souhaite synchroniser des mouvements de machines et réaliser du Motion Control, il faut considérer d’autres bus de terrain comme EtherCAT ou Profinet IRT.