Ir al contenido

Modbus RTU: escribir un holding register (FC 06) con un PLC M-Duino

Código de función 06 entre dos PLC M-Duino, con código probado, la trama en el bus y los demás códigos de función
19 de octubre de 2021 por
Modbus RTU: escribir un holding register (FC 06) con un PLC M-Duino
Boot & Work Corp. S.L, Fernandez Queralt Martinez

Para escribir un holding register con Modbus RTU, el maestro envía el código de función 06 con la dirección del registro y el nuevo valor de 16 bits, y el esclavo responde con un eco de la misma petición. En un PLC M-Duino con la librería Modbus de Industrial Shields, la llamada es master.writeSingleRegister(slave, address, value), y el eco llega a través de master.available().

En este tutorial un M-Duino trabaja como maestro Modbus RTU y escribe un holding register de un segundo M-Duino, que ejecuta el ejemplo de esclavo de la librería. El registro controla una salida analógica del esclavo, así que puedes medir el resultado con un multímetro. Al final verás cómo probar los otros siete códigos de función con los mismos dos PLC.

¿Qué hace el código de función Modbus 06?

Write Single Register (código de función 06, 0x06) escribe un holding register en un equipo remoto. La petición indica la dirección del registro y el valor. Los registros se direccionan desde cero, así que el registro 1 (40001 en la mayoría de manuales) es la dirección 0. La respuesta normal repite la petición cuando el valor ya se ha escrito.

CampoPeticiónRespuesta
Dirección de esclavo1 byte (1 a 247)Igual
Código de función0x060x06
Dirección del registro2 bytes (0x0000 a 0xFFFF)Igual
Valor del registro2 bytes (0x0000 a 0xFFFF)Igual
CRC2 bytes, byte bajo primero2 bytes

En este ejemplo el maestro escribe 128 (0x0080) en la dirección 0 del esclavo 31 (0x1F). La trama en el bus RS-485 es 1F 06 00 00 00 80 8B D4, y el esclavo responde con los mismos 8 bytes. La guía de la trama y los códigos de función de Modbus RTU explica cada campo y el CRC.

Hardware para una prueba Modbus RTU con dos PLC M-Duino

Conexiones entre los dos PLC M-Duino

  1. Alimenta los dos PLC con 12 a 24 Vdc.
  2. Conecta los bornes RS-485 con un par trenzado: A+ con A+ y B- con B-.
  3. Pon el interruptor de comunicación de los dos PLC en half duplex (HD).
Dos PLC M-Duino conectados por RS-485 con un par trenzado: B con B y A con A

En el banco de pruebas, con un cable corto, el enlace funciona sin terminación. En un bus real, pon una resistencia de 120 Ω entre A+ y B- en los dos extremos de la línea. Los conceptos básicos de RS-485 explican por qué.

Código del maestro: escribir un registro y volver a leerlo

Abre un sketch nuevo en el Arduino IDE y pega este código. Cada segundo alterna dos peticiones: la función 06 escribe 128 en el holding register 0 del esclavo 31, y la función 03 lee el mismo registro para comprobarlo. Compila sin avisos para un M-Duino 21+ con las placas AVR de Industrial Shields 1.2.1 y la librería Modbus 1.1.0.

// Modbus RTU master on an M-Duino PLC: write single register (FC 06), then read it back (FC 03)
// Slave: the ModbusRTUSlave example of the library, address 31, 38400 baud, 8E1
#include <RS485.h>
#include <ModbusRTUMaster.h>

const uint32_t BAUDRATE = 38400UL;
const uint8_t SLAVE_ID = 31;
const uint16_t REG_ADDR = 0;        // holding register 40001 = analog output A0_5 of the slave
const uint16_t VALUE = 128;         // 0 to 255 on an M-Duino analog output: 128 is about 5 V

ModbusRTUMaster master(RS485);
uint32_t lastRequest = 0;
bool writeNext = true;              // alternate: write, then read back

void setup() {
  Serial.begin(9600UL);
  RS485.begin(BAUDRATE, HALFDUPLEX, SERIAL_8E1);
  master.begin(BAUDRATE);
}

void loop() {
  if (!master.isWaitingResponse() && millis() - lastRequest >= 1000UL) {
    bool sent = writeNext ? master.writeSingleRegister(SLAVE_ID, REG_ADDR, VALUE)
                          : master.readHoldingRegisters(SLAVE_ID, REG_ADDR, 1);
    if (!sent) {
      Serial.println("Request not sent");
    }
    lastRequest = millis();
  }

  if (master.isWaitingResponse()) {
    ModbusResponse response = master.available();
    if (master.hasException()) {
      Serial.print("No valid answer: ");
      Serial.println(master.getExceptionMessage());
      master.clearException();
    }
    if (response) {
      if (response.hasError()) {
        Serial.print("Slave exception: ");
        Serial.println(response.getErrorMessage());
      } else if (response.getFC() == 6) {
        Serial.println("Register written");          // the slave echoed the request
      } else {
        Serial.print("Register value: ");
        Serial.println(response.getRegister(0));
      }
      writeNext = !writeNext;
    }
  }
}
  1. Selecciona Herramientas > Placa > Industrial Shields AVR Boards > M-Duino family.
  2. Selecciona el modelo en Herramientas > Modelo. En nuestro caso, M-Duino 21+.
  3. Selecciona el puerto en Herramientas > Puerto.
  4. Carga el sketch con el botón de la flecha o con Programa > Subir.

El monitor serie (9600 baudios) muestra "Register written" y "Register value: 128" cada segundo. "No valid answer: timeout" significa que el esclavo no responde: comprueba la dirección del esclavo, la velocidad, el interruptor HD y A+ y B-.

Código del esclavo: el ejemplo ModbusRTUSlave

Con el maestro ya programado, programa el segundo M-Duino con el ejemplo de esclavo de la librería:

  1. Conecta el cable USB al otro PLC M-Duino.
  2. Abre Archivo > Ejemplos > Modbus > ModbusRTUSlave. Usa la dirección 31, 38400 baudios y 8E1, igual que el maestro.
  3. Selecciona Herramientas > Placa > Industrial Shields AVR Boards > M-Duino family y el modelo, en nuestro caso M-Duino 21+.
  4. Selecciona el puerto en Herramientas > Puerto.
  5. Carga el sketch con Programa > Subir.

El ejemplo asigna las entradas y salidas de la primera zona del PLC a las cuatro tablas Modbus. En un M-Duino 21+ el mapa es:

Tabla ModbusDireccionesPines del M-Duino 21+Valores
Coils0 a 4Q0_0 a Q0_40 o 1
Discrete inputs0 a 6I0_0 a I0_60 o 1
Holding registers0 a 2A0_5, A0_6, A0_7 (salidas analógicas)0 a 255 (0 a 10 V)
Input registers0 a 5I0_7 a I0_12 (entradas analógicas)0 a 1023 (0 a 10 V)

Con el maestro en marcha, mide A0_5 del esclavo con un multímetro: marca unos 5 V. Cambia VALUE a 255 y sube a unos 10 V.

¿Por qué 128 y no 1000? Un holding register acepta de 0 a 65535, pero el ejemplo de esclavo lo envía a analogWrite(), y las salidas analógicas del M-Duino tienen 8 bits de resolución (0 a 255). Una versión anterior de este tutorial escribía 1000: el registro PWM solo guarda los 8 bits bajos, así que la salida iba a 232 (unos 9 V) en lugar de dar error. Escala el valor en el maestro, o en el esclavo antes de escribir la salida.

Prueba los demás códigos de función Modbus con los mismos PLC

Mantén la conexión y el esclavo. En el maestro, sustituye la petición por una de estas llamadas. La respuesta se lee igual, con master.available().

FCLlamada del maestroQué pasa en el esclavoRespuesta con
01master.readCoils(31, 0, 5)Devuelve el estado de Q0_0 a Q0_4response.isCoilSet(i)
02master.readDiscreteInputs(31, 0, 7)Devuelve I0_0 a I0_6response.isDiscreteInputSet(i)
03master.readHoldingRegisters(31, 0, 3)Devuelve los valores de A0_5 a A0_7response.getRegister(i)
04master.readInputRegisters(31, 0, 6)Devuelve las entradas analógicas I0_7 a I0_12response.getRegister(i)
05master.writeSingleCoil(31, 0, true)Activa Q0_0response.hasError()
06master.writeSingleRegister(31, 0, 128)Pone A0_5 a unos 5 Vresponse.hasError()
15master.writeMultipleCoils(31, 0, coils, 5)Fija Q0_0 a Q0_4 desde un array bool coils[5]response.hasError()
16master.writeMultipleRegisters(31, 0, values, 3)Fija A0_5 a A0_7 desde un array uint16_t values[3]response.hasError()

Cada llamada devuelve false si el maestro aún espera la respuesta anterior, así que envía una petición cada vez. La librería también tiene un ejemplo de maestro para cada código de función en Archivo > Ejemplos > Modbus. La referencia completa, la conexión para Ardbox y ESP32 PLC y una tabla de errores están en Modbus RTU con PLC basados en Arduino y ESP32.

Prueba un bus con más esclavos. En el simulador de red Modbus, un M-Duino 21 interroga a un transmisor de temperatura, un transmisor de presión, un caudalímetro y un variador de frecuencia. Cambia un registro, un tipo de dato o el orden de las palabras y mira la respuesta del esclavo, trama a trama.

Abrir el simulador de red ModbusEscribir la velocidad de un variador por Modbus RTU

Preguntas frecuentes sobre la escritura de registros Modbus

¿Qué diferencia hay entre la función 06 y la función 16?

La función 06 escribe un registro. La función 16 (0x10) escribe de 1 a 123 registros consecutivos en una sola petición. Usa la 16 para valores de 32 bits, que ocupan dos registros. Algunos equipos solo aceptan una de las dos, así que consulta el manual.

¿Por qué el esclavo responde pero el valor no cambia?

Comprueba la dirección: el registro 40001 es la dirección 0, así que escribir en la dirección 1 cambia el registro 40002. Comprueba también el rango y la escala del equipo: muchos guardan 23,5 como 235 (valor x 10), y algunos rechazan los valores fuera de rango con la excepción 03.

¿Cómo escribo un valor negativo?

Escribe su complemento a dos de 16 bits. Por ejemplo, -1 es 65535 (0xFFFF). En C, haz un cast: (uint16_t)(int16_t)-1. El esclavo debe tratar ese registro como entero con signo.

¿Puedo escribir el mismo registro en todos los esclavos a la vez?

Modbus RTU tiene una dirección de broadcast (0), y los esclavos nunca responden a un broadcast. La librería de maestro de Industrial Shields rechaza la dirección 0, así que con esta librería escribe en cada esclavo por turnos.

¿Se conserva el valor tras un corte de alimentación?

Depende del esclavo. En el ejemplo ModbusRTUSlave los holding registers son variables en RAM, así que vuelven a 0 tras un reset. Los equipos industriales suelen indicar en su manual qué registros se guardan en memoria no volátil.

Buscar en nuestro blog

Modbus RTU: escribir un holding register (FC 06) con un PLC M-Duino
Boot & Work Corp. S.L, Fernandez Queralt Martinez 19 de octubre de 2021
Compartir esta publicación

¿Estás buscando tu Controlador Lógico Programable ideal?

Echa un vistazo a esta comparativa de producto de varios controladores industriales basados en Arduino.

Comparamos entradas, salidas, comunicaciones y otras especificaciones con las de los equipos de otras marcas destacadas.

Comparativa de PLCs