ARduino Modbus sobre RS232. Error de lectura

Hola amigos del foro,

Llevo un par de semanas tratando de resolver un problema de comunicación entre mi Arduino UNO rev3 y un dispositivo que es controlado a través de RS232, usando el protocolo de comunicación Modbus.

Gracias al trabajo increíble de gente altruista dentro del foro, pude encontrar unas librerías que permitían conectar un Arduino UNO (haciendo de máster) con un escalvo usando el protocolo de comunicación Modbus.

Estas librerías se encuentran en el siguiente link:

Previo a la implementación del código de mi Arduino, quise probar la comunicación mediante un emulador de máster y el esclavo en cuestión, un controlador Eurotherm 2406.
El detalle del controlador puede ser visto en la siguiente dirección:

El programa usado fue Qmodbus, un software genial y sencillo de manejar:

Conociendo la dirección del registro en el esclavo que debía consultar, su identificador, la función y la configuración del puerto serie, no tuve ningún problema para extraer dicho dato. El programa hacia su función al 100% y el esclavo respondía a la petición del máster.

En un segundo step, cargué el ejemplo del máster en Arduino UNO, llamada software_serial_simple_master, ya que quería monitorizar el resultado de la adquisición del máster en el PC tras la respuesta del slave y si usaba el ejemplo de simple_master tenía problemas con el USB y los pines de Tx y Rx predeterminados en Arduino UNO. A la par que cargaba el ejemplo, usé el programa de emulación de esclavo modbus, llamado Modbus Slave. Un programa muy sencillo de manejar, también.
El programa lo podéis encontrar aquí:
http://www.modbustools.com/download.html

Una vez cargaba el ejemplo y configuraba el esclavo; función, dirección de registro, número de registros, congifuración del puerto serie, etc..; Arduino era capaz de recibir y plotear los valores de las direcciones del esclavo, previa petición del máster.
A modo de ejemplo, aquí pongo una parte del código que usé:

 case 1: 
    telegram.u8id = 1; // slave address
    telegram.u8fct = 3; // function code (this one is registers read)
    telegram.u16RegAdd = 1; // start address in slave
    telegram.u16CoilsNo = 1; // number of elements (coils or registers) to read
    telegram.au16reg = au16data; // pointer to a memory array in the Arduino

Hasta aquí bien...

  • Conozco las direcciones del esclavo donde debo mirar,

  • Me percato de que el Máster emulador esta leyendo el esclavo real, Eurotherm 2406, y hay comunicación

  • Programo Arduino en versión máster y me lee las direcciones que predetermino en el esclavo emulado

Pero al conectar el máster arduino al esclavo real, el Eurotherm, este no me devuelve nada. O más bien, me devuelve 0, cuando teóricamente debería estar leyendo un parámetro de temperatura del controlador, el cual se encuentra en la dirección 1.

Permitidme dejaros las capturas de pantalla que he ido generando en cada step, así como el código completo que cargo en Arduino Uno.

Breve explicación de las capturas:

  • Lectura Qmodbus Eurotherm 2406: Se puede ver como Eurotherm ha devuelto el dato de temperatura, 259 (25.9ºC)

  • Modificiación dato registro esclavo simulado: En el programa Modbus Slave se pude ver como modifico la dirección de registro 1.
    Modificiación dato registro esclavo simulado.jpg

  • Retorno esclavo simulado hacia Arduino Master: Aquí podemos ver cómo el puerto serie devuelve el valor que tenía la dirección del registro del esclavo simulado

#include <ModbusRtu.h>
#include <SoftwareSerial.h>

// data array for modbus network sharing
uint16_t au16data[16];
uint16_t au16data1[16];
uint8_t u8state;

/**
 *  Modbus object declaration
 *  u8id : node id = 0 for master, = 1..247 for slave
 *  u8serno : serial port (use 0 for Serial)
 *  u8txenpin : 0 for RS-232 and USB-FTDI 
 *               or any pin number > 1 for RS-485
 */
Modbus master(0); // this is master and RS-232 or USB-FTDI via software serial

/**
 * This is an structe which contains a query to an slave device
 */
modbus_t telegram;

unsigned long u32wait;

SoftwareSerial mySerial(3, 5);//Create a SoftwareSerial object so that we can use software serial. Search "software serial" on Arduino.cc to find out more details.

void setup() {
  Serial.begin(9600);//use the hardware serial if you want to connect to your computer via usb cable, etc.
  master.begin( &mySerial, 9600 ); // begin the ModBus object. The first parameter is the address of your SoftwareSerial address. Do not forget the "&". 9600 means baud-rate at 9600
  master.setTimeOut( 2000 ); // if there is no answer in 2000 ms, roll over
  u32wait = millis() + 1000;
  u8state = 0; 
}

void loop() {
  switch( u8state ) {
  case 0: 
    if (millis() > u32wait) u8state++; // wait state
    break;
  case 1: 
    telegram.u8id = 1; // slave address
    telegram.u8fct = 3; // function code (this one is registers read)
    telegram.u16RegAdd = 1; // start address in slave
    telegram.u16CoilsNo = 1; // number of elements (coils or registers) to read
    telegram.au16reg = au16data; // pointer to a memory array in the Arduino
    master.query( telegram ); // send query (only once)
    u8state++;
    break;
  case 2:
    master.poll(); // check incoming messages
    if (master.getState() == COM_IDLE) {
      u8state = 0;
      u32wait = millis() + 2000; 
         Serial.print("Valor de la direccion 1:  ");Serial.println(au16data[0]);//Or do something else!    
          u8state++;
    }
    break;
    }
}

¿A alguna alma caritativa se le ocurre qué puedo estar haciendo mal?

Os doy gracias mil por la ayuda que pueda recibir.

Saludos,

Modificiación dato registro esclavo simulado.jpg

Un error muy común en modbus, es que dependiendo de la implementación de las librerías se incluye el registro "0" otras empiezan con el "1" prueba leer registros aledaños.

Hola PeterKantTropus,

Estuve probando direcciones de registros cercanas. El tema es que cuando simulo el esclavo, no tengo ningún problema con la dirección del registro. Pido leer la dirección 1 y me retorna el valor de esa dirección 1 del esclavo simulado.

En cambio, cuando pido esa dirección en el esclavo real, no me retorna nada. Había comprobado anteriormente con Qmodbus (emulador de maestro modbus), que en la dirección 1 había un registro de temperatura.

De hecho, cuando uso el máster sobre el esclavo real, no me retorna ningún valor de ninguna dirección de registro.

No sé qué puede estar pasando.

Gracias por tu ayuda :wink:

Que adaptador usas entre arduino y el esclavo real?

Para mediar la comunicación uso un cable DB-9 con un adaptador RS232.

https://www.mouser.es/ProductDetail/MikroElektronika/MIKROE-222?qs=sGAEpiMZZMvthb15MeQ6NDspvbA9jMYo

MIKROE-222.jpg

MIKROE-222.jpg

Pudiste comunicar qmodbus con el esclavo simulado?
Otra configuración que no suele traer problemas, pero cuando lo hace es difícil de detectar, es el orden de lectura de bytes. Se establece comunicacion, pero devuelve valores absurdos, generalmente es una tabla con con la forma LH,HL...
En la configuración de Modbus del Eurotherm setea la configuración de Full a integer para forzar a utilizar una dirección modbus

Hola Peter,

No hice ninguna comunicación entre Qmodbus y Modbus Slave (escalvo simulado). Como no hay un COM físico, al conectar ambas aplicaciones debería poder leer de algún puerto.

Ni Qmodbus, ni Modbus Slave me ofrecen la posibilidad de comunicarse. ¿Conoces la manera de poder hacerlo? No se me ocurre cómo podría configurarlos?

En el segundo de los puntos, si me devolviera algo en el Arduino máster, sería todo un avance :wink: .

Creo que debe estar configurado de esta manera el Eurotherm, ya que comunica con protocolo modbus en el momento de comunicarse con Qmodbus (máster). No obstante, intentaré averiguar si está configurado en Integer.

Muchas gracias por el aporte,

Buenas,

Hay algo que me parece muy extraño en la comunicación.

No entiendo cómo puede ser que, pese a que tanto el maestro simulado (usando modbus), como Arduino master , envíen exactamente la misma trama, pero este último no sea capaz de extraer el valor del registro del esclavo

Al menos sé que Arduino está trabajando de manera correcta (o eso creo).

Dejo aquí lo que me aparece en el canal de comunicación :

Descargué este software porque Qmosbus no me permite leer la trama enviada por el máster. Tan sólo me deja leer lo recibido por el slave. Probaré si con este máster puedo leer el controlador de Eurotherm.

Os dejo el link de ModScan32:

A ver si puedo encontrar el punto de bloqueo :wink:

Saludos,

Con QModBus haces una petición del valor de las entradas (función 0x04) y en tu programa estas intentado leer el valor de los registros de mantenimiento holding (función 0x03). Si con el arduino no te da error y estas leyendo 0 eso quiere decir que no está "bien mapeado".

Intenta cambiando la función a 0x04:

telegram.u8fct = 4; // Leer input registers.

Hola a todos,

Finalmente he conseguido establacer la comunicación entre arduino Uno en modo maestro y Eurotherm en modo esclavo.

Gracias por los aportes @PeterKantTropus y @victorjam.

No obstante, comentaros que no era nada de software.

Todo se ha resuelto al estudiar la parte física de la comunicación.

Compré los Max232, una transición de conversión de DB9 macho-macho y el correspondiente cable DB9.

Pues bien, ninguno de las transiciones y el cable DB9 vienen con los pines Tx/Rx intercambiados, y pensaba que la transición sí que venía con este cruce, pero no.

El análisis de todo ha sido de lo más divertido y desesperante, porque estaba demasiado centrado en el SW y no en el HW.

Espero que mis obstáculos y soluciones puedan dar luz a los demás.

Gracias,