Control de dispositivos ModBus RTU

El protocolo de comunicaciones ModBus está situado en el Nivel 2 del modelo de interconexión de sistemas abiertos (ISO/IEC 7498-1), también llamado OSI (Open System Interconnection) y que fue creado por la Organización Internacional para la Estandarización (ISO) en el año 1984. Está basado en la arquitectura maestro/esclavo o cliente/servidor y fue diseñado en 1979 por Modicon para su gama de controladores lógicos programables (PLCs) convirtiéndose algunos años después en uno de los protocolos más usados en el mundo de la automatización de procesos industriales. Su éxito radica en lo sencillo que resulta implementarlo y en que es posible encontrar gran cantidad de documentación donde se tratan todos los aspectos técnicos del mismo.


ModBus permite el control de una red de dispositivos de forma rápida y sencilla sin necesidad de grandes infraestructuras cliente/servidor y que puede estar conformado por elementos de diferente naturaleza: sensores de temperatura, humedad, variadores de frecuencia y un largo etcétera de dispositivos que implementan este protocolo.

Existen dos variantes del mismo protocolo: ModBus RTU, que es una representación binaria de los datos y ModBus ASCII, que es una representación legible en formato ASCII pero menos eficiente. Ambas implementaciones del protocolo son del tipo de comunicación serie.

La variante que veremos en este post es la de ModBus RTU y que, como puede leerse más arriba, empaqueta la información de forma binaria. Dicha información es almacenada en la memoria de cada dispositivo en forma de parámetros ordenados secuencialmente, de forma que desde nuestro programa de control ModBus RTU podemos leer el valor de un parámetro determinado o modificarlo para que el funcionamiento del dispositivo cambie en consecuencia. Existen dos tipos de comandos: de lectura y de escritura, que permiten realizar la acción correspondiente sobre el dispositivo. Inicialmente Modicon, cuando definió el protocolo, creó varios tipos de comandos de lectura y varios de escritura según los siguientes identificadores:

Comando Hex Descripción
01 &H01 Read Discrete Output Coils
05 &H05 Write single Discrete Output Coil
15 &H0F Write multiple Discrete Output Coils
02 &H02 Read Discrete Input Contacts
04 &H04 Read Analog Input Registers
03 &H03 Read Analog Output Holding Registers
06 &H06 Write single Analog Output Holding Register
16 &H10 Write multiple Analog Output Holding Registers

Por otro lado, cada dispositivo presente en la red ModBus está identificado por un número único, de 0 a 255, que le permitirá atender solamente aquellas peticiones de lectura o escritura que circulen por la red y que coincidan con su indentificador. Como ejemplo, he desarrollado una aplicación que puedes descargar en la columna derecha de este post, en Descargas relacionadas:


En primer lugar deberían seleccionarse los parámetros básicos de configuración tales como velocidad en baudios, paridad, bits de datos, etc. en el menú File.

Formando el comando de lectura

Después de haber configurado las comunicaciones en nuestra aplicación de ejemplo es necesario seleccionar el identificador del dispositivo ModBus con el que deseamos comunicar (Terminal ID) y Command a ‘3’ (Read Analog Output Holding Registers) así como el parámetro donde deseamos que comience nuestra lectura (Address) y el número de bytes a leer (Quantity) desde ese parámetro, por defecto un solo byte. Nuestro programa efectúa la construcción del comando ModBus de la siguiente manera:

  1. szCommand = Chr(modbusTerminalID) + Chr(modbusCmd)
  2. szCommand = szCommand + Chr(modbusAdr \ 256) + Chr(modbusAdr Mod 256)
  3. szCommand = szCommand + Chr(modbusQ \ 256) + Chr(modbusQ Mod 256)

  4. commandCRC = modbusCRC(szCommand)
  5. szCommand = szCommand + Chr(commandCRC Mod 256) + Chr(commandCRC \ 256)
  6. MSComm1.Output = szCommand

Como puede comprobarse el mensaje para leer un parámetro ModBus está formado por 8 bytes, en la siguiente forma:

Byte_01Terminal ID
Byte_02Command
Byte_03Address MSB
Byte_04Address LSB
Byte_05Quantity MSB
Byte_06Quantity LSB
Byte_07CRC_LSB
Byte_08CRC_MSB

Byte_07 y Byte_08 se refieren a un cálculo de chequeo llamado Comprobación de Redundancia Cíclica (CRC) y que se calcula con los bytes desde Byte_01 a Byte_06 para asegurarse de que toda la información enviada y recibida por los distintos dispositivos de la red ModBus es correcta y coherente. Veremos la función después.

Formando el comando de escritura

El comando de escritura también está formado por 8 bytes, en la siguiente forma:

Byte_01Terminal ID
Byte_02Command
Byte_03Address MSB
Byte_04Address LSB
Byte_05Value MSB
Byte_06Value LSB
Byte_07CRC_LSB
Byte_08CRC_MSB

La única diferencia reseñable es que Byte_05 y Byte_06 contienen el valor que deseamos escribir en el registro Address (Byte_03 y Byte_04) del dispositivo ModBus.

El código fuente de la parte de nuestra aplicación que conforma el mensaje de escritura queda como sigue:

  1. szCommand = Chr(modbusTerminalID) + Chr(modbusCmd)
  2. szCommand = szCommand + Chr(modbusAdr \ 256) + Chr(modbusAdr Mod 256)
  3. szCommand = szCommand + Chr(modbusValue \ 256) + Chr(modbusValue Mod 256)

  4. commandCRC = modbusCRC(szCommand)
  5. szCommand = szCommand + Chr(commandCRC Mod 256) + Chr(commandCRC \ 256)
  6. MSComm1.Output = szCommand


El cálculo de CRC de los comandos

Tanto la formación del comando de lectura como de escritura necesitan un sistema de comprobación de la integridad de la información que contiene (CRC), así que la siguiente función es la encargada de recorrer los primeros 6 bytes que forman el comando y hacer una serie de operaciones que darán como resultado un valor de 16 bits que será añadido al final del comando, antes de ser enviado.

  1. Function modbusCRC(Command As String) As Long

  2. Length = Len(Command)
  3. CRC = 65535
  4. Generator = 40961

  5. For charCnt = 1 To Length
  6. Temp = Asc(Mid(Command, charCnt, 1))
  7. CRC = CRC Xor Temp
  8. For j = 1 To 8
  9. Bit = CRC And 1
  10. CRC = CRC \ 2
  11. If Bit = True Then
  12. CRC = CRC Xor Generator
  13. End If
  14. Next j
  15. Next charCnt
  16. modbusCRC = CRC

  17. End Function


La respuesta de los dispositivos

Como consecuencia de las operaciones de lectura o escritura, cada uno de los dispositivos controlados mediante ModBus retornan una respuesta: la información recuperada del dispositivo, en el caso de envío de un comando de lectura, o una confirmación de que el valor que queríamos escribir ha sido colocado con éxito. Durante mucho tiempo he buscado una aplicación que fuera capaz de recuperar esa respuesta con garantías din éxito ya que esta parte determina el correcto funcionamiento de nuestro sistema de control. Finalmente, he implementado un sistema de recogida de la respuesta usando un evento del propio sistema de comunicaciones serie:

  1. Select Case MSComm1.CommEvent
  2. Case comEvReceive
  3. If (MSComm1.InBufferCount > 0) Then
  4. dataReceived = MSComm1.Input
  5. End If
  6. End Select

El código fuente de esta aplicación además incluye algunas opciones más para garantizar la correcta recepción de la respuesta. Tanto el ejecutable como el código fuente completo está disponible en el margen derecho de esta página, en el apartado Descargas relacionadas.

Comparte este post


Tue, 11 Jun 2013 18:03:17 +0200
Publicado en: Programación

Posts relacionados
conecta conmigo en