# Modbus TCP

> Registros y bobinas Modbus sobre TCP/IP, con direccionamiento tipado, control del orden de bytes y un escaneo de red acotado.

El driver Modbus TCP lee y escribe registros y bobinas Modbus sobre TCP/IP: PLC,
medidores, puertas de enlace y cualquier servidor que hable Modbus/TCP. El
direccionamiento es completamente tipado: usted elige una clase de registro y un
número de registro desde 0, y la aplicación arma la petición del bus; no hay ninguna
dirección de texto libre en la que equivocarse.

## El dispositivo {#device}

| Campo | Significado | Valor inicial |
| --- | --- | --- |
| **Host o IP** | Nombre DNS, dirección IPv4 o IPv6 del servidor Modbus o de la puerta de enlace. | vacío: el dispositivo no se conecta hasta que se indica uno |
| **Puerto** | Puerto TCP, de 1 a 65535. Un campo vacío muestra 502 en gris y al dispositivo se le marca por él, que es también como se lee un dispositivo guardado con 0. | 502 |
| **ID de unidad** | El id de unidad (esclavo) Modbus que lleva cada petición, lo que usa una puerta de enlace para enrutar al dispositivo que hay detrás. De 0 a 255. | 1 |
| **Formato de trama** | Cómo viaja cada petición por la conexión: **MBAP**, el encabezado de Modbus TCP antes de cada petición, o **RTU**, la trama RTU con su CRC enviada tal cual, para un convertidor serie-Ethernet en modo transparente (consulta [RTU sobre TCP](#rtu-over-tcp)). El host, el puerto, el ID de unidad y los tags son los mismos en los dos casos. | MBAP |
| **Intervalo mínimo entre transacciones (ms)** | Cadencia de compatibilidad: el silencio mínimo después de que se completa una transacción y antes de que empiece la siguiente, de 0 a 1000. Cero no agrega retardo artificial. | 0 |
| **Intervalo de sondeo (ms)** | El ritmo de sondeo predeterminado para los tags que no lo invaliden. | 1000 |

El URI base que se muestra en el encabezado del panel es
`modbus+tcp://host:puerto/idDeUnidad`, más `?minimumTransactionIntervalMs=n` cuando
el intervalo está puesto.

Los dispositivos que comparten host y puerto comparten también una sola compuerta de
transacciones: las peticiones al mismo endpoint nunca se traslapan, y el
intervalo mínimo entre transacciones **más grande** de entre los dispositivos
activados de ese endpoint los cadencia a todos. El panel dice cuándo otro
dispositivo eleva el intervalo efectivo por encima del que se configuró aquí. El
intervalo gobierna solo el tráfico que emite esta aplicación: es una ayuda de
compatibilidad para un servidor o una puerta de enlace lentos, no un arreglo de las
fallas de la red.

Conectarse espera 5 segundos como máximo; una respuesta se espera 10 segundos como
máximo; no hay reintentos a nivel de protocolo. El lazo de reintentos en segundo
plano se encarga de la reconexión, así que un dispositivo apagado no cuesta más que
una lectura de mala calidad.

## RTU sobre TCP {#rtu-over-tcp}

Un convertidor serie-Ethernet en modo transparente no habla Modbus TCP por su cuenta: lo que llega a su socket sale tal cual por su bus serie, y lo que el bus responde regresa. Un dispositivo detrás de uno es un dispositivo Modbus TCP con el **Formato de trama** en **RTU**: la estación envía la trama RTU (el ID de unidad, la petición y su CRC) sin encabezado MBAP, y solo da por buena una respuesta cuyo CRC coincide. El URI base lleva entonces `framing=rtu` en su consulta, por ejemplo `modbus+tcp://192.168.0.20:502/1?framing=rtu`; un dispositivo en **MBAP** conserva la dirección que siempre tuvo. Todo lo demás de esta página sigue igual: las clases de registro, los códigos de función, la cola de escrituras, los tiempos de espera y el intervalo entre transacciones.

- El **ID de unidad** es la dirección del dispositivo en el bus serie detrás del convertidor, de 1 a 247.
- Los dispositivos detrás de un convertidor comparten su host y su puerto, y con ellos su compuerta de transacciones: una petición a la vez, que es además todo lo que lleva un bus serie. Si el convertidor necesita una pausa entre tramas, sube el **Intervalo mínimo entre transacciones (ms)**.
- Los ajustes serie del propio convertidor (velocidad en baudios, formato) se configuran en el convertidor; la estación nunca los ve.
- Una trama RTU no lleva número de transacción, así que antes de cada petición la estación descarta lo que el convertidor ya haya enviado: una respuesta que llega después de que su petición agotó el tiempo nunca se toma por la de la siguiente.

## El direccionamiento del tag {#tag-addressing}

| Campo | Significado | Valor inicial |
| --- | --- | --- |
| **Clase de registro** | Qué entidad Modbus direcciona el tag: Bobina (0xxxx), Entrada discreta (1xxxx), Registro de entrada (3xxxx) o Registro de retención (4xxxx). Cada una lleva el primer dígito que la dirección del manual tiene para ella, así que un manual que imprime solo 30001 apunta a los registros de entrada. | Se deriva del tipo y del acceso: los tags booleanos reciben una entrada discreta (solo lectura) o una bobina (escribible); todos los demás tipos reciben un registro de entrada (solo lectura) o un registro de retención (escribible). |
| **Número de registro (desde 0)** | La dirección cruda del protocolo. Es la dirección desde 0 tal como va en el bus, no la notación del manual: el registro de retención 40001 es el número de registro 0. Escribir 30001 aquí no elige la clase, porque hay equipos con registros arriba de 30000 de verdad: la clase se elige arriba. El registro viaja por el bus como un número de 16 bits, así que el campo toma de 0 a 65535 y rechaza cualquier cosa más ancha ahí mismo donde se escribe. | 0 |
| **Cantidad (registros, en blanco = automático)** | Cuántos registros de 16 bits ocupa el valor. La caja se dibuja solo en los tags String: todos los demás tipos tienen un ancho fijo, así que no hay nada que invalidar. | Por tipo: Boolean, Int16 y UInt16 1, Int32 y Float 2, String 4 |
| **Byte más significativo primero (MSB first, big-endian)** | Qué byte de cada registro de 16 bits va primero en el bus; las palabras entre paréntesis son las que imprimen los manuales. Tags numéricos y un tag booleano guardado en un registro de retención o de entrada; un booleano en una bobina o una entrada discreta es un solo bit sin bytes que ordenar, así que ahí la caja no se dibuja. | encendido (lo que pide la especificación de Modbus) |
| **Palabra más significativa primero (palabra alta primero)** | Qué registro de un valor de dos registros lleva la mitad alta. Solo tags Int32 y Float: un valor de un solo registro (Boolean, Int16, UInt16) no tiene segundo registro, así que la caja no se dibuja ahí. Bajo las dos cajas, **Orden de los bytes del valor** nombra la disposición que forman con las letras de un manual ([El orden de bytes y de palabras](#byte-and-word-order)). | encendido |

La tarjeta Fuente termina con **Dirección en el manual**, dónde está el tag en la notación
que imprime el manual del equipo: el primer dígito de la clase y después el registro
contado desde 1. El registro de entrada 0 se lee 30001, un Float en el registro de retención
100 se lee de 40101 a 40102, y un registro arriba de 9998 pasa a seis dígitos (el registro
de retención 9999 se lee 410000), así que el panel se lee contra el manual línea por línea.

### Los tipos de valor y las cantidades {#value-types-and-quantities}

Se ofrecen seis tipos de valor, y el ancho de protocolo de cada uno es fijo:

| Tipo de dato | Registros | Forma en el bus |
| --- | --- | --- |
| Boolean | 1 | Sobre una clase de registro: cualquier registro distinto de cero se lee como verdadero, y una escritura envía 1 o 0. Sobre `Coil`/`DiscreteInput`: el bit mismo. |
| Int16 | 1 | Entero con signo de 16 bits en un solo registro, de -32768 a 32767. |
| UInt16 | 1 | Entero sin signo de 16 bits en un solo registro, de 0 a 65535. El mismo registro se lee como 65535 en UInt16 y como -1 en Int16: el signo lo da el tipo de dato elegido, no el registro. |
| Int32 | 2 | Entero con signo de 32 bits a lo largo de un par de registros. |
| Float | 2 | IEEE 754 de precisión simple a lo largo de un par de registros. |
| String | 1 o más (4 al inicio) | Bytes UTF-8 empacados de dos por registro. Una lectura se detiene en el primer byte NUL; una escritura debe caber en la cantidad declarada (2 bytes por registro) y se rellena con ceros. |

Un tag String se lee y se escribe en el orden en el que el dispositivo guarda los
bytes, y una escritura siempre envía UTF-8. Los bytes que no son UTF-8 válido, que es
lo que parece un rango que el dispositivo nunca inicializó o un texto escrito en otra
codificación, se leen como el carácter Unicode de reemplazo en lugar de fallar: el
tag muestra algo visiblemente equivocado, y todos los demás tags de ese dispositivo
siguen leyendo.

La **Cantidad (registros, en blanco = automático)** se pide solo en los tags String.
Los anchos de Boolean, Int16, UInt16, Int32 y Float son fijos, así que la caja no se
dibuja en esos puntos, ni en el panel del tag ni en la cuadrícula de tags; la invalidación existe
para dimensionar los tags String.

### El orden de bytes y de palabras {#byte-and-word-order}

La caja del orden de bytes se dibuja en los tags numéricos y en un booleano guardado en un registro, y la del orden de palabras solo en los de dos registros (Int32, Float). Son preguntas independientes:
qué byte encabeza dentro de cada registro, y qué registro encabeza a lo largo de un
par. Modbus no define ninguna de las dos para el texto, así que a un tag String no se
le ofrecen: sus bytes viajan en el orden en el que el dispositivo los guarda. Para el
valor `0x01020304`, con `A` como el byte más significativo, las cuatro disposiciones
son:

| Byte más significativo primero | Palabra más significativa primero | Bytes en el bus |
| --- | --- | --- |
| encendido | encendido | `AB CD` (el valor predeterminado de la especificación) |
| encendido | apagado | `CD AB` (el intercambio clásico de palabras) |
| apagado | encendido | `BA DC` |
| apagado | apagado | `DC BA` |

Las lecturas y las escrituras aplican las mismas dos respuestas en el mismo orden, así
que una escritura deja exactamente los bytes que una lectura habría recuperado. **Orden de
los bytes del valor**, bajo las cajas, dice la disposición en una palabra (ABCD, CDAB, BADC
o DCBA), como la nombra un manual.

Un valor Int16 o UInt16 es un solo registro, así que solo el orden de bytes aplica a
él: para `0x1234`, con el byte más significativo primero se envía `12 34` y con la caja
apagada, `34 12`. No hay un segundo registro que acomodar, así que la caja del orden de
palabras no se dibuja para él.

### Los códigos de función {#function-codes}

| Clase de registro | Lectura | Escritura |
| --- | --- | --- |
| Registro de retención (4xxxx) | 3 (Read Holding Registers) | 6 (un solo registro) o 16 (varios registros) |
| Registro de entrada (3xxxx) | 4 (Read Input Registers) | solo lectura; una escritura se rechaza |
| Bobina (0xxxx) | 1 (Read Coils) | 5 (Force Single Coil), una sola bobina |
| Entrada discreta (1xxxx) | 2 (Read Discrete Inputs) | solo lectura; una escritura se rechaza |

Una escritura devuelve éxito solo cuando la respuesta de protocolo del servidor
confirma que la aceptó; el valor en caché avanza solo entonces.

## El descubrimiento {#discovery}

El escaneo es explícito y acotado: la aplicación nunca deduce ni barre su subred
local. El formulario de alcance toma:

| Campo | Significado | Valor inicial |
| --- | --- | --- |
| **Host IPv4 o CIDR** | Una dirección (`192.168.1.10`) o una subred no más ancha que /24 (`192.168.1.0/24`). Las direcciones de red y de difusión se saltan. | obligatorio |
| **Puerto** | El puerto que se sondea. | 502 |
| **ID de unidad** | Valores, listas separadas por comas o rangos ascendentes: `1`, `1,5,9`, `1, 3, 8-16` (de 0 a 255). | 1 |
| **Formato de trama** | La trama en que se envía cada sondeo. Un convertidor en modo transparente solo responde a tramas RTU, así que escanéelo en RTU. | MBAP |
| **Intervalo mínimo entre transacciones (ms)** | La cadencia entre sondeos completados en el mismo host, de 0 a 1000. | 0 |

El escaneo envía cada sondeo en el **Formato de trama** elegido. En **RTU** cada sondeo es la trama RTU sola, y una respuesta solo cuenta cuando su ID de unidad coincide y su CRC es válido, así que las unidades detrás de un convertidor en modo transparente se encuentran como cualquier otra. Una unidad encontrada conserva el formato de trama del escaneo al agregarla.

Un solo escaneo puede enviar como máximo **256 sondeos** (hosts × ID de unidad); un
plan más grande se rechaza antes de que se abra ningún socket. La única petición que
se envía es **Read Basic Device Identification** (función 43/14): el descubrimiento
nunca lee registros ni adivina un mapa de registros. Una respuesta de excepción
Modbus completamente válida prueba que hay una unidad presente sin declarar su
identidad; la accesibilidad TCP, los tiempos de espera agotados y las tramas mal
formadas no lo prueban. Se escanean hasta cuatro hosts en paralelo, y de forma
secuencial dentro de cada host, con tiempos de espera de 750 ms para conectar y para
sondear.

Los resultados llevan el endpoint completo y un nombre sugerido, armado con
el fabricante y el código de producto cuando el dispositivo los reporta. Los
candidatos tienen cero tags: los registros se configuran después de **Agregar**. Una
unidad que ya está configurada se marca como tal y ofrece **Abrir** en lugar de crear
un duplicado.

## Límites que conviene conocer {#limits-worth-knowing}

- Los demás anchos de entero (SByte, Byte, UInt32, Int64, UInt64) y Double no se
  ofrecen: el códec del bus va y viene exactamente con los seis tipos de arriba.
- Hay una petición pendiente por endpoint a la vez; muchos dispositivos
  detrás de una misma puerta de enlace comparten esa serialización por diseño.
- Las escrituras direccionan una bobina o una tirada de registros a la vez; no hay
  escritura de varias bobinas.
