-
Modbus: guía completa del protocolo industrial, registros, direccionamiento, RTU, ASCII, TCP y ciberseguridad
Modbus es uno de los protocolos de comunicación más extendidos en automatización industrial, supervisión de instalaciones y sistemas de control. Aunque fue diseñado hace décadas, continúa presente en controladores lógicos programables (PLC), sistemas SCADA, interfaces HMI, analizadores de redes eléctricas, contadores de energía, variadores de frecuencia, sensores, sistemas de climatización, dispositivos de entrada/salida y pasarelas industriales.
Su permanencia se explica por un diseño sencillo: un equipo solicita una operación, otro interpreta la petición y devuelve los datos o el resultado. Sin embargo, esa aparente simplicidad oculta varias dificultades prácticas. Para trabajar correctamente con Modbus necesitamos entender qué es un registro, cómo se diferencia su número de su dirección, qué códigos de función existen, cómo se representa un valor de 32 bits, cómo se delimitan las tramas serie, qué contiene la cabecera MBAP de Modbus TCP y qué problemas de seguridad aparecen al conectar dispositivos industriales a una red IP.
En esta guía desarrollamos el funcionamiento del protocolo desde sus fundamentos hasta la lectura real de registros en Debian, el análisis de capturas con Wireshark y las medidas de protección que debemos aplicar en entornos OT. Tomamos como punto de partida el tutorial Modbus 101 de Control Solutions y contrastamos la información con las especificaciones oficiales de Modbus Organization.
Qué es Modbus y cómo funciona
Modbus apareció en 1979 de la mano de Modicon, empresa posteriormente integrada en Schneider Electric. Se creó para permitir que distintos equipos de automatización intercambiaran información de forma predecible. Con el tiempo, el protocolo pasó a utilizarse sobre enlaces serie y redes Ethernet, con una especificación publicada abiertamente y sin necesidad de pagar regalías por su utilización.
Modbus define fundamentalmente un protocolo de aplicación basado en solicitudes y respuestas. El dispositivo que necesita información envía una petición con un código de función, una dirección y los parámetros correspondientes. El equipo destinatario procesa esa operación y responde con los datos pedidos, con una confirmación de escritura o con una excepción que identifica un error.
Podemos imaginar un contador eléctrico que expone la tensión medida en un registro y la corriente en otro. Un sistema de supervisión consulta periódicamente esos registros, interpreta sus valores y los incorpora a una gráfica. De forma similar, un sistema de control puede enviar una orden para modificar una consigna de temperatura, aunque las operaciones de escritura requieren precauciones adicionales porque pueden actuar sobre procesos físicos.
La denominación histórica de los participantes es master/slave (maestro/esclavo), especialmente en manuales y configuraciones antiguas. En la documentación moderna se utilizan los términos cliente y servidor. En ambos casos, el principio fundamental es el mismo: el cliente inicia la transacción y el servidor proporciona la respuesta. En Modbus clásico no existe, de manera general, un mecanismo de publicación espontánea de valores equivalente al de un sistema de mensajería por eventos.
CLIENTE / SISTEMA SCADA SERVIDOR / DISPOSITIVO | | |---- Leer 2 registros desde 0 ------>| | | |<--- Valores: 100 y 200 --------------| | | |---- Leer 1 registro desde 10 ------>| | | |<--- Excepción: dirección inválida ---| | |La comunicación es determinista en su estructura, pero no garantiza por sí sola tiempos de respuesta fijos. Los tiempos dependen del enlace, la velocidad serie, el número de dispositivos, la carga del servidor, los tiempos de espera, las retransmisiones y, en Modbus TCP, de las condiciones de la red y de las conexiones disponibles.
Arquitectura de una red Modbus y funciones de cada dispositivo
En una instalación típica distinguimos varios componentes. El cliente puede ser un PLC, un SCADA, un programa de adquisición de datos o un gateway. El servidor suele ser un equipo que dispone de variables accesibles mediante Modbus. Las pasarelas convierten entre medios físicos o protocolos, mientras que los sistemas de supervisión agregan información y pueden generar históricos, alarmas o informes.
En un bus serie Modbus RTU tradicional basado en RS-485 trabajamos con un único cliente que organiza las transacciones sobre ese segmento. Podemos conectar varios servidores con direcciones diferentes, pero no debemos conectar dos clientes que transmitan independientemente sobre el mismo bus sin un mecanismo externo de arbitraje. El protocolo serie habitual no resuelve las colisiones entre dos clientes.
En Modbus TCP, en cambio, un servidor puede aceptar conexiones de varios clientes mediante TCP/IP. Esto no significa que todos los equipos soporten cualquier número de conexiones ni que acepten operaciones concurrentes sin limitación: el número de clientes simultáneos, las peticiones pendientes y el tratamiento de escrituras concurrentes dependen de cada implementación.
SCADA / CLIENTE MODBUS TCP | | Ethernet / TCP 502 | [Gateway TCP - RTU] | | Bus RS-485 +------ Dispositivo RTU 1 +------ Dispositivo RTU 2 +------ Dispositivo RTU 3 Otro cliente TCP ----> Servidor Modbus TCP nativo IP: 192.168.1.50 Puerto: 502En un segmento Modbus serie, las direcciones unicast normalizadas de los servidores van de 1 a 247. La dirección 0 se reserva para difusión o broadcast; algunas órdenes de escritura pueden dirigirse simultáneamente a todos los dispositivos compatibles, y en ese caso los servidores no responden. Las direcciones 248 a 255 están reservadas para otros usos y no forman parte del rango normalizado de servidores serie, aunque algunas herramientas permitan introducirlas.
Debemos diferenciar la cantidad de direcciones lógicas disponibles del número de equipos que admite un segmento RS-485. El límite eléctrico clásico de 32 cargas unitarias no equivale necesariamente a 32 dispositivos en todas las instalaciones. El uso de transceptores de 1/2, 1/4 o 1/8 de carga, repetidores y la topología elegida modifica la cantidad real de nodos admisible.
Modbus RTU, Modbus ASCII y Modbus TCP: diferencias y compatibilidad
Modbus no es sinónimo de RS-485 ni de Ethernet. El protocolo de aplicación puede transportarse mediante varias tecnologías y cada modalidad introduce reglas propias de encapsulación y delimitación de mensajes. Las implementaciones principales que nos encontraremos son Modbus RTU, Modbus ASCII y Modbus TCP.
Característica Modbus RTU Modbus ASCII Modbus TCP Medio habitual RS-485 o RS-232 RS-485 o RS-232 Ethernet / IP Codificación Binaria Caracteres ASCII hexadecimales Binaria sobre TCP Delimitación Intervalos de silencio Dos puntos iniciales y CR/LF final Longitud declarada en MBAP Comprobación propia CRC-16 LRC Sin CRC/LRC Modbus adicional Direccionamiento Dirección serie Dirección serie IP y Unit Identifier Puerto TCP habitual No aplica No aplica 502 Uso práctico Muy extendido en campo Principalmente equipos heredados Redes industriales IP Modbus RTU transmite octetos binarios y es eficiente para enlaces serie de ancho de banda limitado. Debemos establecer los mismos parámetros de comunicación en todos los participantes: velocidad, paridad, bits de datos y bits de parada.
Modbus ASCII transforma cada octeto de datos en dos caracteres hexadecimales legibles. Esta modalidad consume más tiempo de transmisión, pero delimita sus tramas mediante caracteres explícitos y tolera ciertos escenarios serie donde el cumplimiento de los temporizadores RTU resulta difícil. No debemos confundir su menor utilización actual con su inexistencia normativa: el modo ASCII continúa descrito como opcional en la guía oficial de Modbus sobre línea serie.
Modbus TCP utiliza una cabecera específica denominada MBAP, seguida de la unidad de datos del protocolo Modbus. No transmite una trama RTU completa dentro de TCP: no conserva el campo CRC de RTU, y la dirección del dispositivo se representa de otro modo. Esta distinción es importante al analizar capturas o programar clientes.
También podemos encontrar productos anunciados como RTU over TCP o pasarelas serie por IP. Estos mecanismos pueden transportar bytes RTU completos a través de una conexión TCP, pero no son equivalentes automáticamente a Modbus TCP normalizado. Debemos verificar qué encapsulación espera cada extremo.
Modelo de datos: coils, discrete inputs, input registers y holding registers
Un dispositivo Modbus presenta un modelo lógico de cuatro clases de datos. Podemos entenderlo como cuatro espacios independientes de elementos direccionables. No son necesariamente cuatro bloques físicos de memoria; cada fabricante decide cómo representar internamente las variables, y un mismo dato puede exponerse en varios espacios o funciones.
Objeto Tamaño Acceso lógico Función de lectura Referencia clásica Coil / salida discreta 1 bit Lectura y escritura 01 0xxxx Discrete Input / entrada discreta 1 bit Solo lectura 02 1xxxx Input Register / registro de entrada 16 bits Solo lectura 04 3xxxx Holding Register / registro de retención 16 bits Lectura y escritura en el modelo lógico 03 4xxxx Un coil representa un estado binario, habitualmente relacionado con una salida, un relé, una habilitación o una bandera de control. Un discrete input representa también un bit, pero su acceso Modbus está definido como lectura. Los input registers contienen palabras de 16 bits utilizadas frecuentemente para mediciones. Los holding registers representan palabras de 16 bits que pueden contener consignas, contadores, configuraciones, estados y otros valores.
La clasificación describe las operaciones disponibles en el modelo del protocolo, no necesariamente los permisos de escritura de todos los registros reales. Un fabricante puede exponer valores de solo lectura dentro del espacio de holding registers. Por eso, no debemos asumir que un registro se puede modificar solo porque lo leamos con la función 03.
Asimismo, una entrada física puede aparecer duplicada como input register y como holding register. Esto facilita la interoperabilidad con clientes que solamente implementan determinadas funciones, pero puede generar confusión cuando las dos referencias tienen escalas o semánticas distintas.
Direccionamiento Modbus: por qué 40001, 1 y 0 pueden referirse al mismo registro
Uno de los errores más habituales al integrar equipos industriales consiste en confundir la referencia documental del registro con la dirección que se transmite en la trama. El protocolo utiliza un campo de dirección de 16 bits, mientras que muchos manuales conservan una convención histórica de números precedidos por un dígito que indica el tipo de objeto.
Notación tradicional Objeto Primera referencia Primer offset transmitido 0xxxx Coils 00001 0 1xxxx Discrete inputs 10001 0 3xxxx Input registers 30001 0 4xxxx Holding registers 40001 0 En la notación clásica, 40001 identifica el primer holding register. Para leerlo enviamos la función 03 y la dirección hexadecimal 0x0000. El número 40001 no se transmite como un entero literal, ni tampoco se envía el dígito inicial 4: el tipo de objeto queda determinado por el código de función.
Referencia documental: 40001 Tipo de objeto: Holding Register Función Modbus: 03 (Read Holding Registers) Número humano: 1 Dirección en la PDU: 0 (0x0000) Referencia documental: 40123 Tipo de objeto: Holding Register Función Modbus: 03 Número humano: 123 Dirección en la PDU: 122 (0x007A) Referencia documental: 30017 Tipo de objeto: Input Register Función Modbus: 04 Número humano: 17 Dirección en la PDU: 16 (0x0010)
La convención también dispone de versiones extendidas de seis dígitos, como 400001 para el primer holding register. Las formas de cinco y seis dígitos pertenecen a sistemas de referencia utilizados por fabricantes y programas; no son un campo adicional de la PDU. Una dirección Modbus de 16 bits admite offsets de 0 a 65535 en cada espacio lógico, aunque un dispositivo real solo implementará los rangos indicados en su manual.
Hay tres situaciones que debemos identificar antes de configurar cualquier programa: el manual puede enumerar registros comenzando por 1; puede mostrar offsets comenzando por 0; o puede usar referencias heredadas como 30001 y 40001. Algunas herramientas esperan referencias humanas y realizan internamente la resta de uno, mientras otras exigen directamente offsets de protocolo.
Como regla práctica, debemos confirmar siempre tipo de objeto, función de lectura, base de numeración, dirección inicial, longitud, tipo de dato y escala. Una lectura que comienza un registro antes o después del esperado puede devolver números aparentemente válidos, lo que convierte los errores de desplazamiento en fallos especialmente difíciles de detectar.
El mapa de registros no es universal. Modbus estandariza cómo acceder a los datos, pero no establece que una temperatura, una tensión o un número de serie deban encontrarse en una dirección concreta. En general, necesitamos el mapa de registros proporcionado por el fabricante. Algunas implementaciones admiten identificación del dispositivo mediante funciones específicas, pero eso no sustituye el mapa completo ni garantiza un autodescubrimiento de todas las variables disponibles.
Códigos de función Modbus y operaciones que permiten
El código de función ocupa un octeto dentro de la PDU e indica la operación solicitada. Los códigos públicos más habituales para lecturas y escrituras básicas son los siguientes:
Decimal Hexadecimal Operación Datos afectados Máximo por petición estándar 01 0x01 Read Coils Leer coils 2000 bits 02 0x02 Read Discrete Inputs Leer entradas discretas 2000 bits 03 0x03 Read Holding Registers Leer holding registers 125 registros 04 0x04 Read Input Registers Leer input registers 125 registros 05 0x05 Write Single Coil Escribir un coil 1 bit 06 0x06 Write Single Register Escribir un holding register 1 registro 15 0x0F Write Multiple Coils Escribir varios coils 1968 bits 16 0x10 Write Multiple Registers Escribir varios holding registers 123 registros Las funciones 01 y 02 devuelven bits empaquetados: en el primer octeto de datos, el bit menos significativo corresponde al primer punto solicitado. Las funciones 03 y 04 devuelven palabras de 16 bits, dos octetos por registro. Las funciones 05 y 06 modifican un único elemento, mientras que 15 y 16 permiten enviar grupos de valores consecutivos.
Al utilizar la función 05, el estado activado se representa normalmente mediante el valor hexadecimal 0xFF00, y el desactivado mediante 0x0000. En la respuesta correcta se repiten la dirección y el valor solicitado. Para la función 06, el servidor también confirma la escritura repitiendo los campos de la solicitud. En las escrituras múltiples se devuelve normalmente la dirección inicial y el número de elementos escritos, pero no todos los valores enviados.
Existen códigos adicionales contemplados por la especificación: 07 para leer estado de excepción en determinados equipos serie; 08 para diagnósticos serie; 11 y 12 para información de eventos de comunicación; 17 para identificación básica del servidor serie; 20 y 21 para registros de archivo; 22 para escritura con máscara; 23 para lectura y escritura combinadas de registros; 24 para colas FIFO; y 43/14 (0x2B/0x0E) para leer información de identificación del dispositivo.
Las funciones menos comunes no están necesariamente implementadas en todos los productos. Debemos distinguir entre una función definida por el estándar y una función realmente compatible con un modelo concreto. Tampoco debemos probar funciones de escritura para averiguar si están soportadas en equipos de producción.
Estructura de las tramas y análisis de una transacción real
La parte común del protocolo se denomina PDU (Protocol Data Unit) y está formada por el código de función y sus datos. Al transportarla, cada modalidad añade los campos que necesita y produce una ADU (Application Data Unit). Esta separación permite entender por qué una misma operación se representa con bytes parcialmente diferentes en RTU y en TCP.
Cómo se representa una petición Modbus RTU
Una ADU RTU contiene dirección de servidor (1 byte), función (1 byte), datos (hasta 252 bytes) y CRC (2 bytes). Su tamaño máximo normalizado es de 256 bytes. Para leer dos holding registers desde el offset cero del servidor 1, la trama completa es:
01 03 00 00 00 02 C4 0B | | |_____| |_____| |___| | | | | | | | | | +-- CRC-16: C4 0B (byte bajo primero) | | | +--------- Cantidad: 2 registros | | +----------------- Dirección inicial: 0 | +----------------------- Función: 03 +-------------------------- Servidor: 1
Si los dos registros contienen los valores decimales 100 y 200, la respuesta RTU tendrá esta forma:
01 03 04 00 64 00 C8 BA 7A | | | |____| |____| |___| | | | | | | | | | | | +-- CRC-16 | | | | +-------- Registro 2: 0x00C8 = 200 | | | +--------------- Registro 1: 0x0064 = 100 | | +--------------------- Cantidad de bytes de datos: 4 | +------------------------ Función: 03 +--------------------------- Servidor: 1
Es importante observar que el segundo octeto de la respuesta es un código de función y el siguiente indica el número de bytes de datos, no el número de registros. Por ello, una lectura de dos registros produce cuatro bytes de valores.
Cómo se representa la misma petición en Modbus TCP
Modbus TCP incorpora una cabecera MBAP de 7 bytes seguida de la PDU. Sus campos son Transaction Identifier (2 bytes), Protocol Identifier (2 bytes), Length (2 bytes) y Unit Identifier (1 byte). El identificador de protocolo vale 0 para Modbus estándar; el campo longitud cuenta los bytes situados después de sí mismo, incluido el Unit Identifier.
Petición TCP (12 bytes): 00 01 00 00 00 06 01 03 00 00 00 02 |___| |___| |___| | | |_____| |_____| | | | | | | | | | | | | | +-- 2 registros | | | | | +---------- Offset 0 | | | | +---------------- Función 03 | | | +------------------- Unit ID 1 | | +----------------------- Longitud 6 | +----------------------------- Protocol ID 0 +----------------------------------- Transaction ID 1 Respuesta TCP (13 bytes): 00 01 00 00 00 07 01 03 04 00 64 00 C8 |___| |___| |___| | | | |____| |____| | | | | | | | | | | | | | | | +-- Valor 200 | | | | | | +--------- Valor 100 | | | | | +--------------- 4 bytes de datos | | | | +------------------ Función 03 | | | +--------------------- Unit ID 1 | | +------------------------- Longitud 7 | +------------------------------- Protocol ID 0 +------------------------------------- Transaction ID 1
El servidor utiliza el Transaction Identifier para devolver una respuesta asociable a la solicitud, especialmente cuando existen varias transacciones pendientes. No podemos asumir, sin embargo, que cualquier servidor admita múltiples solicitudes simultáneas. Una conexión TCP también puede segmentar una ADU en varios segmentos: un paquete capturado no equivale necesariamente a una trama Modbus completa. El receptor debe reconstruir los datos siguiendo la longitud MBAP.
El Unit Identifier tiene especial relevancia en gateways TCP/RTU, porque permite seleccionar el dispositivo serie que se encuentra detrás de una única dirección IP. En un servidor Modbus TCP nativo, la especificación recomienda usar 0xFF como identificador no significativo, pero en la práctica hay equipos que aceptan, ignoran o exigen otros valores, como 1. Debemos verificar la implementación concreta y no confundir Unit ID con la dirección IP.
Cómo se representa la petición en Modbus ASCII
En Modbus ASCII las tramas comienzan con el carácter :, continúan con parejas de caracteres hexadecimales y terminan con retorno de carro y salto de línea (CR/LF). La comprobación de integridad se realiza con un LRC. La petición equivalente a la lectura anterior sería:
:010300000002FA\r\n : Inicio de trama 01 Dirección del servidor 03 Código de función 0000 Offset inicial 0002 Cantidad de registros FA LRC \r\n Fin de trama (CR/LF)
El valor LRC se obtiene sumando los octetos representados por el mensaje, sin incluir los delimitadores, y calculando el complemento a dos de los ocho bits menos significativos del resultado. Una trama ASCII ocupa más caracteres que su equivalente RTU, aunque puede facilitar el diagnóstico en ciertos enlaces.
Modbus RTU en profundidad: RS-485, temporización y CRC
Cuando trabajamos con Modbus RTU debemos distinguir tres conceptos: el protocolo Modbus, que define operaciones y datos; el formato serie, que establece la transmisión de bits y la delimitación; y la capa eléctrica, que puede utilizar RS-485 o RS-232, entre otros medios.
RS-485 es especialmente habitual porque admite comunicaciones diferenciales y múltiples dispositivos en un mismo bus. En instalaciones de dos hilos suele emplearse una configuración half-duplex: los participantes comparten el par de comunicación y transmiten de forma alterna. RS-232 suele conectar dos extremos y no ofrece la capacidad multidrop propia de RS-485.
Los parámetros serie deben coincidir en ambos extremos. Una configuración frecuente es 9600 baudios, 8 bits de datos, paridad par y 1 bit de parada (8E1). También encontraremos 19200, 38400, 57600 o 115200 baudios, así como 8N1 y 8N2 según fabricante. La especificación de línea serie utiliza paridad par como opción habitual y prevé dos bits de parada cuando no se emplea paridad para conservar la longitud de carácter. No conviene sustituir a ciegas los parámetros documentados por los que consideremos más comunes.
Una característica fundamental de RTU es que no utiliza caracteres especiales para marcar el comienzo y el final: reconoce los límites de cada trama mediante intervalos de silencio. La guía oficial establece un espacio mínimo de 3,5 tiempos de carácter entre tramas. Un intervalo superior a 1,5 tiempos de carácter dentro de una trama provoca que esta se considere incompleta y se descarte. Para velocidades superiores a 19200 bit/s, la guía recomienda temporizadores fijos de 750 microsegundos y 1,750 milisegundos, respectivamente, para esos dos umbrales.
Por ejemplo, una comunicación 9600 8E1 transmite 11 bits por carácter: un bit de inicio, ocho de datos, uno de paridad y uno de parada. Cada carácter dura aproximadamente 1,146 milisegundos y una separación de 3,5 caracteres representa alrededor de 4,01 milisegundos. Esto ayuda a explicar por qué un adaptador serie, una pasarela mal configurada o la fragmentación temporal de una transmisión pueden causar errores incluso cuando los bytes observados parecen correctos.
El CRC-16/Modbus se calcula sobre el campo dirección, el código de función y los datos, y no sobre los bits de inicio, parada o paridad del UART. El algoritmo parte de 0xFFFF y utiliza el polinomio reflejado 0xA001. En la trama se transmite primero el byte menos significativo del CRC y después el más significativo. Para la petición 01 03 00 00 00 02 obtenemos C4 0B.
Podemos comprobarlo de forma independiente mediante este pequeño script de Python, sin bibliotecas adicionales:
#!/usr/bin/env python3 import sys def fCalcularCRC(pDatos): vCRC = 0xFFFF for vOcteto in pDatos: vCRC ^= vOcteto for vIndice in range(8): if vCRC & 1: vCRC = (vCRC >> 1) ^ 0xA001 else: vCRC >>= 1 return bytes((vCRC & 0xFF, (vCRC >> 8) & 0xFF)) def fPrincipal(): aOctetos = [int(vArgumento, 16) for vArgumento in sys.argv[1:]] vDatos = bytes(aOctetos) print(fCalcularCRC(vDatos).hex(" ").upper()) if __name__ == "__main__": fPrincipal()python3 crc_modbus.py 01 03 00 00 00 02 # Resultado esperado: C4 0B
En cuanto al cableado, debemos utilizar la topología de bus recomendada por los fabricantes, evitar derivaciones largas y configuraciones en estrella, instalar terminaciones adecuadas en los dos extremos físicos del segmento cuando corresponda y disponer de polarización de reposo (biasing) donde sea necesaria. Una terminación habitual se aproxima a 120 ohmios, pero debe ajustarse a la impedancia característica del cable y al diseño del sistema.
La identificación de los conductores A/B, D+/D− o +/− no siempre es uniforme entre fabricantes. Una inversión de polaridad puede impedir la comunicación, por lo que debemos basarnos en la documentación de los dos equipos y no solamente en el color o la letra de un borne. También debemos atender las recomendaciones de referencia común, blindaje, puesta a tierra, aislamiento galvánico y protección frente a sobretensiones.
Interpretación de los valores: enteros, signos, bits, escalas y números de 32 o 64 bits
Una palabra Modbus contiene 16 bits, pero eso no determina por sí solo el significado de sus datos. El mismo valor binario puede representar una cantidad positiva, un entero con signo, una escala decimal, un conjunto de indicadores o una parte de un número mayor. La interpretación correcta depende del mapa de registros del dispositivo.
Un registro de 16 bits leído como entero sin signo (uint16) admite valores de 0 a 65535. Si el fabricante lo define como entero con signo (int16) en complemento a dos, su rango es de −32768 a 32767. Así, 0xFFFF equivale a 65535 sin signo, pero a −1 con signo. No hay ningún indicador interno de Modbus que permita adivinar cuál de las dos interpretaciones corresponde.
Es frecuente que los fabricantes utilicen factores de escala para representar decimales. Si un registro contiene 253 y su documentación indica una resolución de 0,1 °C por unidad, interpretamos 25,3 °C. Otros equipos emplean 0,01 A, 0,001 kWh o multiplicadores configurables. También debemos identificar unidades, signos, valores especiales de error y límites de validez.
Cuando necesitamos 32 bits, lo habitual es combinar dos registros consecutivos de 16 bits. Para 64 bits normalmente se utilizan cuatro. Esto permite transportar enteros grandes, marcas temporales o valores de coma flotante IEEE 754. Modbus establece el orden big-endian de los dos bytes dentro de un registro, pero el orden de las palabras que forman un dato más ancho no es universal entre fabricantes.
Orden habitual Secuencia de bytes para ABCD Descripción ABCD A B C D Palabra alta primero; bytes en orden normal CDAB C D A B Palabra baja primero; intercambio de registros BADC B A D C Intercambio de bytes dentro de cada palabra DCBA D C B A Inversión de palabras y bytes Las representaciones BADC y DCBA no siguen el orden canónico de bytes dentro de cada palabra Modbus, pero aparecen en algunas documentaciones o herramientas de conversión. Para un valor de coma flotante de 25,0, la representación IEEE 754 de 32 bits es 0x41C80000. Si el dispositivo entrega los registros 0x41C8 y 0x0000, podemos interpretar el valor con el orden ABCD; si entrega 0x0000 y 0x41C8, tendremos que intercambiar las palabras.
También podemos interpretar valores de 8 bits empaquetados dentro de una palabra. La máscara 0xFF00 permite seleccionar el byte alto y 0x00FF el bajo. Las máscaras de bits individuales, por su parte, se construyen desplazando un 1 a la posición requerida.
Bit 0 = 0x0001 Bit 8 = 0x0100 Bit 1 = 0x0002 Bit 9 = 0x0200 Bit 2 = 0x0004 Bit 10 = 0x0400 Bit 3 = 0x0008 Bit 11 = 0x0800 Bit 4 = 0x0010 Bit 12 = 0x1000 Bit 5 = 0x0020 Bit 13 = 0x2000 Bit 6 = 0x0040 Bit 14 = 0x4000 Bit 7 = 0x0080 Bit 15 = 0x8000 Byte alto: 0xFF00 Byte bajo: 0x00FF Máscara general para el bit n: 1 << n
Cuando un fabricante utiliza una referencia como 40001:7, normalmente está señalando el bit 7 del holding register 40001. Para conocer su estado debemos leer el registro completo con la función 03 y extraer después ese bit. No existe una función estándar que lea directamente un único bit interno de un holding register como si fuera un coil; son espacios lógicos diferentes.
#!/usr/bin/env python3 import struct def fInterpretarRegistro(pValor): vSinSigno = pValor vConSigno = pValor if pValor < 0x8000 else pValor - 0x10000 vBit7 = (pValor >> 7) & 1 vByteAlto = (pValor >> 8) & 0xFF vByteBajo = pValor & 0xFF print("Sin signo:", vSinSigno) print("Con signo:", vConSigno) print("Bit 7:", vBit7) print("Byte alto:", vByteAlto) print("Byte bajo:", vByteBajo) def fInterpretarFloat32(pRegistroAlto, pRegistroBajo): vDatos = struct.pack(">HH", pRegistroAlto, pRegistroBajo) return struct.unpack(">f", vDatos)[0] def fPrincipal(): fInterpretarRegistro(0x0180) print("Float 32 bits:", fInterpretarFloat32(0x41C8, 0x0000)) if __name__ == "__main__": fPrincipal()El código anterior interpreta valores que ya hemos obtenido de un dispositivo y no realiza ninguna comunicación. Si el fabricante utiliza orden de palabras CDAB, tendremos que invertir el orden de los dos registros al llamar a la función de conversión. Para datos de 64 bits aplicamos el mismo principio utilizando cuatro palabras, pero verificando además su orden y representación numérica.
Respuestas de excepción, timeouts y diagnóstico de errores
No todos los errores Modbus se manifiestan de la misma manera. Cuando el servidor recibe una petición válida a nivel de comunicación, pero no puede ejecutarla, puede devolver una respuesta de excepción. Cuando no recibe la petición, la descarta por un error de integridad o no puede responder, el cliente generalmente acabará alcanzando su tiempo de espera.
Las respuestas de excepción activan el bit más significativo del código de función: código de respuesta = código solicitado + 0x80. Así, si solicitamos la función 03 y el dispositivo informa de una dirección ilegal, la respuesta contendrá 0x83 y después el código de excepción 0x02.
Petición RTU: 01 03 00 00 00 02 C4 0B Respuesta de excepción RTU: 01 83 02 C0 F1 | | | |___| | | | +-- CRC-16 | | +------- Excepción 02: Illegal Data Address | +---------- 0x83 = 0x03 + 0x80 +------------- Servidor 1
Código Nombre estándar Interpretación práctica 01 Illegal Function Función no admitida por ese servidor 02 Illegal Data Address Dirección o rango no disponible para esa operación 03 Illegal Data Value Valor o estructura de un campo de petición no aceptable a nivel de protocolo 04 Server Device Failure Fallo interno durante la ejecución 05 Acknowledge Operación especial aceptada, todavía en procesamiento 06 Server Device Busy El servidor está ocupado 08 Memory Parity Error Error de consistencia en áreas de archivo especiales 0A Gateway Path Unavailable La pasarela no pudo establecer la ruta interna 0B Gateway Target Device Failed to Respond El dispositivo detrás del gateway no respondió La excepción 03 merece una precisión: no significa necesariamente que una temperatura o consigna esté fuera de su límite operativo, porque el protocolo no conoce por sí mismo la semántica de los registros. En el estándar se refiere a valores no aceptables dentro de los campos de la solicitud, por ejemplo una longitud o cantidad incorrecta. Los límites funcionales de un equipo pueden validarse mediante mecanismos adicionales del fabricante.
También debemos distinguir entre la excepción 0B enviada por una pasarela y un timeout del propio cliente. En el primer caso existe una respuesta Modbus que informa de un fallo en el destino interno; en el segundo simplemente no hemos recibido una respuesta aceptable antes de que expire el temporizador.
Para diagnosticar errores RTU, podemos consultar, si están soportados, los contadores y subfunciones de diagnóstico serie, revisar CRC, errores de paridad, colisiones, dirección del dispositivo y tiempo entre tramas. Para Modbus TCP, comprobamos conectividad IP, puerto, cabecera MBAP, Unit ID, códigos de función, tamaño de la respuesta y posibles cierres de conexión.
Instalar herramientas Modbus en Debian y leer registros reales
En Debian disponemos de herramientas para trabajar con Modbus sin instalar plataformas de automatización completas. Una de las opciones más prácticas es mbpoll, disponible en los repositorios de Debian estable. Permite consultar equipos Modbus TCP y RTU, seleccionar el tipo de dato y controlar los parámetros de comunicación. Para capturas podemos utilizar tcpdump y Wireshark o tshark.
sudo apt update sudo apt install mbpoll tcpdump tshark # Comprobar las opciones disponibles mbpoll -h
Supongamos que tenemos un servidor Modbus TCP de laboratorio en 192.168.1.50, puerto 502, con Unit ID 1. Para leer dos holding registers desde la primera referencia humana, ejecutamos:
mbpoll -m tcp -a 1 -t 4 -r 1 -c 2 -1 192.168.1.50
En este comando, -m tcp selecciona el transporte; -a 1 fija el identificador de unidad; -t 4 indica el espacio de holding registers en la nomenclatura propia de mbpoll; -r 1 selecciona la primera referencia; -c 2 pide dos valores; y -1 realiza una sola consulta en lugar de repetirla indefinidamente.
Para leer los primeros input registers utilizamos el tipo -t 3. Debemos tener cuidado: esos valores de la opción -t identifican categorías de datos de mbpoll y no coinciden directamente con los códigos de función Modbus. Por ejemplo, -t 4 utiliza la función Modbus 03, mientras que -t 3 utiliza la función Modbus 04.
# Input registers: función Modbus 04 mbpoll -m tcp -a 1 -t 3 -r 1 -c 4 -1 192.168.1.50 # Holding registers en hexadecimal mbpoll -m tcp -a 1 -t 4:hex -r 1 -c 4 -1 192.168.1.50 # Offset real 0, indicando explícitamente numeración base cero mbpoll -m tcp -a 1 -t 4 -0 -r 0 -c 2 -1 192.168.1.50
Si necesitamos trabajar sobre un enlace RS-485, conectamos el adaptador serie correspondiente y comprobamos el nombre del dispositivo, por ejemplo /dev/ttyUSB0. La siguiente orden consulta un servidor RTU con dirección 1 a 9600 baudios, paridad par y un bit de parada:
# Identificar el adaptador serie ls -l /dev/serial/by-id/ # Lectura puntual de dos holding registers mediante Modbus RTU mbpoll -m rtu -a 1 -b 9600 -P even -s 1 -t 4 -r 1 -c 2 -1 /dev/ttyUSB0
Si recibimos un error de acceso al puerto serie, debemos revisar los permisos del dispositivo y la pertenencia del usuario al grupo correspondiente, generalmente dialout. Si se abre el puerto pero no llega respuesta, verificamos los parámetros 8E1/8N1/8N2, la dirección del servidor, el par diferencial, la terminación y la polarización del bus.
Las órdenes anteriores son de lectura. Las escrituras pueden cambiar estados físicos, consignas o parámetros de funcionamiento, por lo que debemos limitarlas a equipos de laboratorio o a procedimientos autorizados y documentados. Leer tampoco es completamente inocuo en todos los sistemas: algunos equipos heredados pueden saturarse por exceso de consultas, y ciertas funciones avanzadas tienen efectos particulares.
Programar un cliente Modbus TCP en Python
Para integrar lecturas en scripts podemos utilizar PyModbus, una biblioteca de Python que implementa clientes y servidores Modbus. En Debian podemos instalarla con Python y emplearla para obtener valores, procesar respuestas y exportar mediciones. Conviene revisar la versión: en las versiones recientes de PyModbus 3.x, el parámetro que identifica al dispositivo es device_id, mientras que en ejemplos de versiones anteriores aparece como slave.
python3 -m pip install pymodbus --break-system-packages
El siguiente ejemplo realiza una única lectura de holding registers, admite parámetros desde la línea de comandos, controla los errores de comunicación y cierra la conexión incluso si se produce una excepción:
#!/usr/bin/env python3 import argparse from pymodbus.client import ModbusTcpClient def fLeerRegistros(pHost, pPuerto, pUnidad, pDireccion, pCantidad): vCliente = ModbusTcpClient(host=pHost, port=pPuerto, timeout=3) if not vCliente.connect(): raise ConnectionError(f"No se pudo conectar con {pHost}:{pPuerto}") try: vRespuesta = vCliente.read_holding_registers( address=pDireccion, count=pCantidad, device_id=pUnidad ) if vRespuesta.isError(): raise RuntimeError(f"Respuesta Modbus de error: {vRespuesta}") for vIndice, vValor in enumerate(vRespuesta.registers): print(f"Offset {pDireccion + vIndice}: {vValor} (0x{vValor:04X})") finally: vCliente.close() def fPrincipal(): vAnalizador = argparse.ArgumentParser(description="Lectura Modbus TCP") vAnalizador.add_argument("host") vAnalizador.add_argument("--puerto", type=int, default=502) vAnalizador.add_argument("--unidad", type=int, default=1) vAnalizador.add_argument("--direccion", type=int, default=0) vAnalizador.add_argument("--cantidad", type=int, default=2) vArgumentos = vAnalizador.parse_args() fLeerRegistros( vArgumentos.host, vArgumentos.puerto, vArgumentos.unidad, vArgumentos.direccion, vArgumentos.cantidad ) if __name__ == "__main__": fPrincipal()python3 lector_modbus_tcp.py 192.168.1.50 --unidad 1 --direccion 0 --cantidad 2
Este ejemplo utiliza offsets base cero, por lo que la dirección 0 corresponde a la primera referencia del tipo de registro solicitado. Si cambiamos de PyModbus 3.11 a otra rama de la biblioteca, debemos comprobar la documentación de esa versión antes de modificar nombres de argumentos. También podemos construir clientes serie con ModbusSerialClient e indicar expresamente la interfaz y los parámetros RTU.
En un sistema de adquisición permanente podemos ampliar esta base con temporizadores de sondeo, reconexión controlada, un inventario de mapas de registros, almacenamiento en una base de datos de series temporales y validación de rangos. Para evitar sobrecargas, resulta preferible agrupar registros consecutivos compatibles en una misma lectura y respetar los límites de los dispositivos, en lugar de realizar cientos de peticiones aisladas sin planificación.
Capturar y analizar tráfico Modbus con tcpdump, tshark y Wireshark
Una captura de paquetes nos permite confirmar las direcciones consultadas, los códigos de función utilizados, los tiempos de respuesta, las conexiones inesperadas y las operaciones de escritura. Modbus TCP sin cifrar es especialmente sencillo de interpretar porque sus campos de aplicación viajan visibles en la conexión TCP.
En un entorno autorizado, podemos capturar el tráfico de Modbus TCP de una interfaz y guardarlo para inspección posterior:
sudo tcpdump -i enp3s0 -nn -s 0 -w modbus.pcap 'tcp port 502' # Mostrar paquetes Modbus TCP de la captura tshark -r modbus.pcap -Y 'mbtcp' # Mostrar únicamente operaciones de lectura de holding registers tshark -r modbus.pcap -Y 'modbus.func_code == 3' # Mostrar operaciones de escritura habituales tshark -r modbus.pcap -Y 'modbus.func_code == 5 || modbus.func_code == 6 || modbus.func_code == 15 || modbus.func_code == 16'
En Wireshark podemos utilizar los filtros mbtcp, modbus.func_code, modbus.reference_num, modbus.exception_code y mbtcp.trans_id para localizar transacciones. La denominación exacta de algunos campos puede variar entre versiones del analizador.
Al revisar una conversación conviene comprobar si el cliente está leyendo el offset correcto, si la respuesta incluye la cantidad de bytes esperada, si aparecen excepciones y si la duración de las transacciones aumenta con el tiempo. Cuando un servicio Modbus utiliza un puerto no estándar, puede que tengamos que indicar manualmente al analizador que interprete esa conversación como Modbus TCP.
Para observar Modbus RTU necesitamos acceso al enlace serie mediante un analizador, un capturador o una herramienta capaz de registrar los octetos y sus marcas temporales. Una captura de bytes sin información temporal puede ser insuficiente para diagnosticar errores relacionados con las separaciones de 1,5 y 3,5 caracteres.
Gateways, registro histórico de datos, alarmas e integración con otros protocolos
Una pasarela Modbus puede realizar tareas muy distintas. Un gateway Modbus TCP/RTU adapta las solicitudes y respuestas entre Ethernet y un bus serie, utilizando el Unit Identifier para seleccionar el destino cuando corresponde. Otro tipo de dispositivo puede convertir objetos de Modbus a BACnet, KNX, LonWorks o SNMP, o exponer mediante Modbus variables obtenidas de equipos que utilizan protocolos diferentes.
No todas las pasarelas son simples convertidores de tramas. Algunas incorporan tablas de mapeo, escalado, intercambio de palabras, cachés, agregación de lecturas y traducción de tipos de datos. Otras emulan la disposición de registros de un equipo antiguo para permitir su sustitución sin tener que reprogramar todos los clientes existentes.
Debemos entender las limitaciones de estos diseños. Una lectura TCP puede tardar más de lo esperado si se traduce a varias consultas RTU secuenciales. Si varios clientes TCP consultan el mismo bus serie, la pasarela tendrá que organizar las solicitudes. Un error de conexión TCP, un timeout del dispositivo serie y una excepción 0B pueden indicar fallos diferentes, aunque a nivel de supervisión se perciban como la misma pérdida de dato.
Cuando necesitamos almacenar históricos, una aplicación puede consultar periódicamente los registros y registrar valor, marca temporal, unidad, calidad de lectura e identificador del dispositivo. Los datos pueden exportarse a CSV, almacenarse en una base de datos o integrarse en sistemas de visualización. Conviene conservar también los fallos de consulta, ya que interpolar silenciosamente una medición ausente puede ocultar un problema de comunicación.
Para generar alarmas podemos establecer umbrales, reglas de persistencia, histéresis y estados de fallo. No debemos confundir una alarma creada por el sistema de supervisión con una notificación enviada espontáneamente por Modbus: en el modelo clásico, la supervisión detecta el cambio después de consultar los datos. Esta diferencia condiciona la frecuencia de sondeo y el tiempo máximo de detección.
Ciberseguridad de Modbus: riesgos reales en redes industriales
La seguridad es una de las principales limitaciones de Modbus clásico. Modbus TCP tradicional no incorpora autenticación de clientes, autorización criptográfica por operación ni cifrado de la carga útil. Que un dispositivo responda por TCP en el puerto 502 no significa que verifique quién está solicitando información o modificando registros. Las protecciones disponibles dependen de la implementación y de los controles de red y sistema situados a su alrededor.
Este diseño responde al contexto histórico de las redes industriales, que originalmente solían encontrarse físicamente aisladas y priorizaban simplicidad e interoperabilidad. Actualmente, las pasarelas IP, el acceso remoto, las integraciones IT/OT y las plataformas de monitorización hacen que esa confianza implícita resulte insuficiente.
Entre los riesgos que debemos contemplar se encuentran la lectura no autorizada de variables de proceso, la manipulación de consignas y salidas, la suplantación de respuestas, la alteración del tráfico en tránsito, la pérdida de disponibilidad por exceso de conexiones o consultas y las configuraciones inseguras de los gateways. En un entorno OT, una escritura aparentemente pequeña puede modificar el comportamiento físico de máquinas, instalaciones eléctricas o sistemas de climatización.
Para reducir la exposición debemos combinar controles técnicos y operativos:
- Inventariar clientes, servidores, direcciones IP, Unit IDs, pasarelas, mapas de registros y funciones realmente necesarias.
- Segmentar los sistemas OT en redes o zonas separadas, controlando los flujos entre zonas mediante firewalls y reglas explícitas.
- Restringir TCP/502 a los clientes y servidores autorizados; evitar publicar dispositivos Modbus directamente en Internet.
- Separar la supervisión del control cuando sea viable, permitiendo solo las operaciones de lectura necesarias y aplicando control granular sobre escrituras mediante pasarelas o firewalls industriales compatibles.
- Proteger el acceso remoto mediante soluciones con autenticación robusta, registro de sesiones y mecanismos criptográficos apropiados, sin asumir que una conexión VPN elimina todos los riesgos internos.
- Monitorizar el tráfico para detectar clientes desconocidos, códigos de función de escritura inesperados, cambios de frecuencia y errores anómalos.
- Aplicar gestión de cambios, copias de configuración, procedimientos de recuperación y pruebas de compatibilidad antes de modificar sistemas de producción.
- Priorizar la disponibilidad y la seguridad física al programar auditorías: el sondeo intensivo y las pruebas activas deben validarse según los requisitos del proceso industrial.
La guía NIST SP 800-82 Rev. 3 desarrolla principios de defensa en profundidad, segmentación, supervisión y gestión de riesgos específicos de tecnologías operacionales. Estas medidas tienen que adaptarse a las necesidades de disponibilidad y seguridad del proceso, no aplicarse mecánicamente como si los PLC fueran simples servidores de oficina.
Para nuevos equipos y arquitecturas compatibles, existe Modbus Security, una variante normalizada que transporta Modbus mediante TLS, incorpora autenticación mutua basada en certificados X.509v3 y permite transportar información de roles para mecanismos de autorización. Su puerto registrado es TCP/802, distinto del TCP/502 tradicional. Sin embargo, que exista el estándar no implica que todos los dispositivos Modbus lo implementen. Debemos verificar compatibilidad, versiones de TLS, certificados, políticas de acceso y requisitos del fabricante.
La segmentación, los controles de acceso y la monitorización siguen siendo necesarios incluso cuando la comunicación está cifrada. Un canal autenticado puede proteger frente a determinadas manipulaciones de red, pero no evita por sí mismo que un cliente legítimo, comprometido o mal configurado envíe órdenes de control incorrectas.
Problemas frecuentes al implementar Modbus y cómo identificarlos
Síntoma Posibles causas Comprobación recomendada No recibimos respuesta RTU Velocidad, paridad, dirección, polaridad A/B o cableado incorrectos Revisar configuración serie, adaptador y bus CRC inválido o tramas intermitentes Ruido, terminación, polarización, colisiones o temporización Inspeccionar señal y marcas temporales TCP conecta pero Modbus no responde Unit ID no aceptado, recurso agotado, gateway o función incorrecta Revisar cabecera MBAP, límites y mapa del equipo Excepción 01 Función no implementada Consultar lista de funciones admitidas Excepción 02 Offset inválido, tipo de objeto incorrecto o lectura fuera de rango Revisar base cero/uno y rango solicitado Recibimos valores absurdos Escala, signo, orden de palabras o tipo numérico incorrectos Revisar unidades, mapa y endianness Varias lecturas funcionan por separado, pero no agrupadas Huecos no implementados o límites internos del dispositivo Reducir tamaño y agrupar solo rangos compatibles El gateway informa de excepción 0B Servidor serie de destino sin respuesta Verificar enlace y dirección detrás de la pasarela Los datos históricos tienen huecos Time out, saturación, reinicios o periodos de muestreo demasiado agresivos Registrar errores, tiempos y calidad del dato Dos clientes RTU interrumpen las comunicaciones Acceso simultáneo sin arbitraje al mismo bus Consolidar el sondeo mediante un cliente o gateway La primera decisión ante un fallo debe ser identificar en qué capa aparece: enlace eléctrico, configuración serie, comunicación TCP, encapsulación Modbus, direccionamiento, función o interpretación de valores. Cambiar simultáneamente varios parámetros suele impedir que sepamos cuál de ellos solucionó el problema y puede introducir errores nuevos.
Como ejemplo, si conseguimos leer un registro pero recibimos una excepción al pedir cinco consecutivos, no deberíamos asumir automáticamente que la red falla. Es posible que uno de esos offsets no exista. En cambio, si todas las peticiones finalizan por timeout y estamos utilizando RS-485, conviene revisar primero la interfaz física y los parámetros de comunicación, antes de modificar el tipo de datos.
Documentación técnica y especificaciones de referencia
Para desarrollar clientes, integrar equipos o contrastar diferencias entre fabricantes, podemos consultar los siguientes documentos y proyectos. La especificación de aplicación define funciones, excepciones y modelo de datos; la guía serie define RTU, ASCII y comportamiento de la comunicación serie; y la guía TCP describe la cabecera MBAP, el transporte y la interacción entre clientes, servidores y pasarelas.
- Control Solutions: Modbus 101. Material introductorio utilizado como punto de partida.
- Modbus Application Protocol Specification V1.1b3. Modelo de datos, códigos de función, formatos de mensajes y excepciones.
- Modbus over Serial Line Specification and Implementation Guide V1.02. Definición de RTU, ASCII, direccionamiento serie, CRC, LRC y temporizadores.
- Modbus Messaging on TCP/IP Implementation Guide V1.0b. Cabecera MBAP, transacciones, Unit Identifier y gateways.
- Modbus Security Protocol. Protección con TLS, certificados y control de acceso.
- PyModbus: documentación del cliente 3.11.1. API para implementar clientes TCP y serie en Python.
- Debian: paquete mbpoll. Herramienta de lectura y escritura para Modbus TCP y RTU.
- Wireshark: referencia de filtros Modbus. Campos de análisis del protocolo.
- NIST SP 800-82 Rev. 3. Recomendaciones de ciberseguridad para tecnología operacional.
2026/10/13