· Eduardo Vieira · Protocolos Industriales · 3 min de lectura
Códigos de Función Modbus: Guía Técnica con Ejemplos en C y Python
Domina los códigos 0x01, 0x03, 0x06 y 0x10. Ejemplos prácticos de integración con Libmodbus (C) y PyModbus (Python) para sistemas embebidos e industriales.

Los códigos de función Modbus son el “verbo” en la oración de comunicación industrial. Definen exactamente qué acción debe realizar el esclavo (servidor). En esta guía, no solo veremos qué hacen, sino cómo implementarlos en código real de producción, tanto para Sistemas Embebidos (C) como para Scripting/SCADA (Python).
La Estructura del Byte de Función
El código de función es el segundo byte de la trama Modbus (después de la dirección).
- Rango: 1 a 127 (0x01 - 0x7F).
- Excepción: Si el esclavo responde con un error, enciende el bit más significativo (MSB) del código de función (Ej:
0x03se convierte en0x83).
1. Lectura de Datos (Read)
0x01: Read Coils (Lectura de Bobinas)
- Tipo de dato: Bit (R/W)
- Uso: Leer estado de salidas digitales, relés, habilitaciones.
- Detalle Técnico: Los bits se empaquetan en bytes en la respuesta. Si pides 10 coils, recibes 2 bytes.
Implementación en C (Libmodbus)
uint8_t bits[10];
int rc = modbus_read_bits(ctx, 0, 10, bits);
if (rc == -1) {
fprintf(stderr, "Error de lectura: %s\n", modbus_strerror(errno));
} else {
printf("Coil 0: %d\n", bits[0]);
}0x03: Read Holding Registers (Lectura de Registros de Retención)
- Tipo de dato: Word 16-bit (R/W)
- Uso: La “navaja suiza”. Se usa para leer setpoints, configuraciones y valores analógicos.
- Uso: Para leer flotantes (32-bit), se solicitan 2 registros consecutivos.
Implementación en Python (PyModbus)
# Leer 2 registros empezando en 40100 (Address 99)
rr = client.read_holding_registers(address=99, count=2, slave=1)
if not rr.isError():
# Convertir a float (considerando endianness)
decoder = BinaryPayloadDecoder.fromRegisters(rr.registers, byteorder=Endian.Big, wordorder=Endian.Big)
temp_celsius = decoder.decode_32bit_float()2. Escritura de Datos (Write)
0x05: Write Single Coil (Escribir Bobina Única)
- Uso: Forzar una salida digital (ON/OFF).
- Curiosidad: En el protocolo crudo,
ONes0xFF00yOFFes0x0000. Cualquier otro valor es ilegal, aunque algunas implementaciones laxas toleran0x0001como ON.
Implementación Embebida (Zephyr RTOS - Modbus Client)
/* Escribir TRUE a la bobina 10 */
uint16_t coil_val = 0xFF00; // Constante para ON
int err = modbus_write_coil(client_iface, 1, 10, &coil_val);0x06: Write Single Register (Escribir Registro Único)
- Uso: Cambiar un parámetro simple de configuración (ej: ID de nodo, Baudrate).
- Limitación: Solo escribe 16 bits. NO USAR para escribir floats de 32 bits (causa atomicidad rota: escribes la mitad del float, el proceso lee basura, luego escribes la otra mitad).
0x10: Write Multiple Registers (Escribir Registros Múltiples)
- Uso: La forma segura de escribir datos complejos (Floats, Strings, Long Ints).
- Ventaja: La escritura es atómica dentro del dispositivo (generalmente).
Implementación Robusta en C
uint16_t tab_reg[2];
float setpoint = 23.5f;
// Serializar float a registros (Big Endian)
modbus_set_float_abcd(setpoint, tab_reg);
// Escribir atómicamente
modbus_write_registers(ctx, 4001, 2, tab_reg);3. Diagnóstico y Control
0x08: Diagnostics (Diagnóstico Serial)
Rara vez usado hoy en día, pero vital para debug de RS485 puro. El sub-código 0x0000 (Return Query Data) actúa como un Ping: el esclavo devuelve exactamente lo que recibió. Útil para verificar integridad del cableado.
Tabla de Referencia Rápida
| Código | Nombre | Tipo de Acceso | Área de Memoria |
|---|---|---|---|
| 01 | Read Coils | Lectura | 0xxxx (Salidas) |
| 02 | Read Discrete Inputs | Lectura | 1xxxx (Entradas) |
| 03 | Read Holding Registers | Lectura | 4xxxx (R/W) |
| 04 | Read Input Registers | Lectura | 3xxxx (Solo Lectura) |
| 05 | Write Single Coil | Escritura (1 bit) | 0xxxx |
| 06 | Write Single Register | Escritura (1 word) | 4xxxx |
| 15 | Write Multiple Coils | Escritura (N bits) | 0xxxx |
| 16 | Write Multiple Registers | Escritura (N words) | 4xxxx |
Conclusión Industrial
En planta, la fiabilidad no es negociable.
- Usa siempre 0x10 en lugar de 0x06 si hay la mínima posibilidad de que el dato crezca a 32 bits en el futuro.
- Maneja las excepciones Modbus (
0x83,0x84, etc.) en tu código. Un timeout no es lo mismo que un “Illegal Data Address”. - Valida la atomicidad.
Para implementaciones críticas en tiempo real, revisa mis guías sobre optimización de latencia en RS485.



