# Modbus TCP

> Registros y bobinas Modbus sobre TCP/IP, con direccionamiento tipado, control del orden de bytes y una exploración de red acotada.

El controlador Modbus TCP lee y escribe registros y bobinas Modbus sobre TCP/IP: autómatas, medidores, pasarelas y cualquier servidor que hable Modbus/TCP. El direccionamiento es del todo tipado: se elige una clase de registro y un número de registro que empieza en 0, y la aplicación compone la petición del bus; no hay ninguna dirección de texto libre en la que equivocarse.

## Dispositivo {#device}

| Campo | Significado | Por defecto |
| --- | --- | --- |
| **Host o IP** | Nombre DNS, dirección IPv4 o IPv6 del servidor o de la pasarela Modbus. | 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 se llama al dispositivo 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 una pasarela usa para encaminar hacia el dispositivo que hay detrás. De 0 a 255. | 1 |
| **Formato de trama** | Cómo viaja cada petición por la conexión: **MBAP**, la cabecera de Modbus TCP delante de cada petición, o **RTU**, la trama RTU con su CRC enviada tal cual, para un convertidor serie-Ethernet en modo transparente (véase [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 tras completarse una transacción antes de que empiece la siguiente, de 0 a 1000. Cero no añade retardo artificial. | 0 |
| **Intervalo de sondeo (ms)** | Ritmo de sondeo por defecto para los tags que no lo sustituyen. | 1000 |

El URI base que se muestra en la cabecera del panel es `modbus+tcp://host:port/unitID`, más `?minimumTransactionIntervalMs=n` cuando el intervalo está fijado.

Los dispositivos que comparten host y puerto comparten también una única puerta de transacciones: las peticiones al mismo endpoint nunca se solapan, y el intervalo mínimo entre transacciones **más grande** de entre los dispositivos habilitados de ese endpoint marca la cadencia de todos ellos. El panel avisa cuando otro dispositivo eleva el intervalo efectivo por encima del que se ha configurado aquí. El intervalo gobierna solo el tráfico que emite esta aplicación: es una ayuda de compatibilidad para un servidor o una pasarela lentos, no un remedio para las averías de red.

Conectar espera como mucho 5 segundos; una respuesta se espera como mucho 10 segundos; no hay reintentos a nivel de protocolo. El bucle de reintento en segundo plano es el dueño 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 vuelve. 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 cabecera MBAP, y solo da por buena una respuesta cuyo CRC cuadra. 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 cada vez, que es además todo lo que lleva un bus serie. Si el convertidor necesita una pausa entre tramas, suba 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 agotara el tiempo nunca se toma por la de la siguiente.

## Direccionamiento del tag {#tag-addressing}

| Campo | Significado | Por defecto |
| --- | --- | --- |
| **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 Boolean reciben una entrada discreta (solo lectura) o una bobina (con escritura); los demás tipos reciben un registro de entrada (solo lectura) o un registro de retención (con escritura). |
| **Número de registro (desde 0)** | La dirección de protocolo en bruto. Es la dirección que empieza en 0 sobre 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 por encima 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 admite de 0 a 65535 y rechaza cualquier cosa más ancha allí donde se escribe. | 0 |
| **Cantidad (registros, en blanco = automático)** | Cuántos registros de 16 bits ocupa el valor. La casilla se dibuja solo en los tags String: los demás tipos tienen un ancho fijo, así que no hay nada que sustituir. | 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 por el bus; las palabras entre paréntesis son las que imprimen los manuales. En tags numéricos y en un tag Boolean guardado en un registro de retención o de entrada; un Boolean en una bobina o una entrada discreta es un solo bit sin bytes que ordenar, así que ahí la casilla no se dibuja. | encendido (lo que pide la especificación Modbus) |
| **Palabra más significativa primero (palabra alta primero)** | Qué registro de un valor de dos registros lleva la mitad alta. Solo en tags Int32 y Float: un valor de un solo registro (Boolean, Int16, UInt16) no tiene segundo registro, así que la casilla no se dibuja ahí. Bajo las dos casillas, **Orden de los bytes del valor** nombra la disposición que forman con las letras de un manual ([Orden de byte y de palabra](#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 por encima 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.

### Tipos de valor y 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 | En una clase de registro: cualquier registro distinto de cero se lee como verdadero, y una escritura envía 1 o 0. En `Coil`/`DiscreteInput`: el propio bit. |
| Int16 | 1 | Entero con signo de 16 bits en un único registro, de -32768 a 32767. |
| UInt16 | 1 | Entero sin signo de 16 bits en un único registro, de 0 a 65535. El mismo registro se lee como 65535 en UInt16 y como -1 en Int16: el signo lo pone el tipo de dato elegido, no el registro. |
| Int32 | 2 | Entero con signo de 32 bits repartido en una pareja de registros. |
| Float | 2 | Precisión simple IEEE 754 repartida en una pareja de registros. |
| String | 1 o más (4 por defecto) | Bytes UTF-8 empaquetados de dos en dos por registro. Una lectura se detiene en el primer byte NUL; una escritura tiene que 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 que el dispositivo guarda los bytes, y una escritura envía siempre UTF-8. Los bytes que no son UTF-8 válido, que es el aspecto que tiene un rango que el dispositivo nunca inicializó o un texto escrito en otra codificación, se leen como el carácter Unicode de sustitución en lugar de fallar: el tag muestra algo visiblemente mal, 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 casilla no se dibuja en esos puntos, ni en el panel del tag ni en la rejilla de tags; la sustitución existe para dimensionar los tags String.

### Orden de byte y de palabra {#byte-and-word-order}

La casilla del orden de byte se dibuja en los tags numéricos y en un Boolean guardado en un registro, y la del orden de palabra solo en los de dos registros (Int32, Float). Son preguntas independientes: qué byte va delante dentro de cada registro, y qué registro va delante en una pareja. 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 que el dispositivo los guarda. Para el valor `0x01020304`, siendo `A` 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 por defecto de la especificación) |
| encendido | apagado | `CD AB` (el intercambio de palabras clásico) |
| apagado | encendido | `BA DC` |
| apagado | apagado | `DC BA` |

Las lecturas y las escrituras aplican las dos mismas respuestas en el mismo orden, así que una escritura deposita exactamente los bytes que una lectura habría recogido. **Orden de los bytes del valor**, bajo las casillas, 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 byte se le aplica: para `0x1234`, con el byte más significativo primero se envía `12 34` y con la casilla apagada, `34 12`. No hay un segundo registro que colocar, así que la casilla del orden de palabra no se dibuja para él.

### Códigos de función {#function-codes}

| Clase de registro | Lectura | Escritura |
| --- | --- | --- |
| Registro de retención (4xxxx) | 3 (Read Holding Registers) | 6 (registro único) 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 únicamente entonces.

## Descubrimiento {#discovery}

La exploración es explícita y acotada: la aplicación nunca deduce ni barre la subred local. El formulario del alcance toma:

| Campo | Significado | Por defecto |
| --- | --- | --- |
| **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 va a sondear. | 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 explórelo en RTU. | MBAP |
| **Intervalo mínimo entre transacciones (ms)** | Cadencia entre sondeos completados en la misma dirección, de 0 a 1000. | 0 |

La exploración envía cada sondeo en el **Formato de trama** elegido. En **RTU** cada sondeo es la trama RTU desnuda, y una respuesta solo cuenta cuando su ID de unidad coincide y su CRC cuadra, 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 de la exploración al añadirla.

Una sola exploración puede enviar como mucho **256 sondeos** (direcciones × ID de unidad); un plan mayor se rechaza antes de que se abra ningún zócalo. 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 del todo válida demuestra que hay una unidad presente sin declarar su identidad; la alcanzabilidad TCP, los tiempos de espera agotados y las tramas mal formadas no lo demuestran. Se exploran hasta cuatro direcciones en paralelo, de forma secuencial dentro de cada una, con tiempos de espera de 750 ms para conectar y para sondear.

Los resultados llevan el endpoint completo y un nombre sugerido, montado con el fabricante y el código de producto cuando el dispositivo los informa. Los candidatos tienen cero tags: los registros se configuran después de **Añadir**. 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 codificador del bus va y vuelve con exactitud solo en los seis tipos de arriba.
- Hay una petición pendiente por endpoint a la vez; muchos dispositivos detrás de una misma pasarela 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.
