Mostrando entradas con la etiqueta modbus. Mostrar todas las entradas
Mostrando entradas con la etiqueta modbus. Mostrar todas las entradas

domingo, 7 de julio de 2013

Uso de la libreria Modbusserial (2/2)

Después de caer en un vórtice espacio-tiempo (por muchas ocupaciones) vuelvo a escribir en el blog, muchos han preguntado como se usa la librería ModbusSerial

En la siguiente foto se pueden observar los elemento necesarios para hacer la prueba de la librería


El cable usb se conecta al PC en mi caso uso el ModSim para emular una Remota; este cable va conectado el conector RS232 el cual se coloca en el Protoboard para Arduino y con los cables se hace la conección final.

Se debe usar el código fuente base para usar la libreria Modbusserial.

Si no queremos que se muestre por la consola las respuesta de la remota solo se debe comentar la linea 

#define VERBOSE

en la libreria modbusseriallib.h

-------------------------------------------------------------
#include <SoftwareSerial.h>
#include "modbusseriallib.h"

#define RXPIN        4
#define TXPIN        5

SoftwareSerial PLCSerialPort(RXPIN, TXPIN);
ModbusSerial ModbusSerial(&PLCSerialPort);

void setup()
{
  Serial.begin(9600);
  PLCSerialPort.begin(9600);
}

void loop()
{
  // your program //
}
-------------------------------------------------------------

Como verán se puede cambiar el pin RX y TX de acuerdo a sus necesidades
El Objeto instanciado se llama ModbusSerial y se usa de la siguiente manera:

FC1
 
En caso de que deseen llamar la función fc1 de la librería deberán de tener la siguiente  consideración
* Es una función de lectura por lo tanto deben declarar una variable que reciba la información de la remota.
* Si queremos ver la respuesta utilizamos un pequeño ciclo for


Veamos el siguiente ejemplo: Si queremos preguntar por 37 discrete coils desde la direccion 20 inclusive, del Device 17 (ojo viene en formato decimal), codificamos lo siguiente: 

  byte deviceId=17;
  unsigned short start=20;
  int numValues=37;
  unsigned short values[numValues]; // aquí se almacena la respuesta
 
  ModbusSerial.fc1(deviceId,start,numValues,values);
 
  for(int i=0;i<numValues-1;i++)
      {
      Serial.print(values[i]);
      Serial.print(",");
      }
  Serial.println(values[numValues-1]);

Si tomamos en cuenta las respuestas que están en la entrada anterior "http://fernandezajp.blogspot.com/2013/01/libreria-modbus-serial-para-arduino.html", obtendríamos la respuesta de la siguiente manera

MSG:11,1,0,13,0,25,E,10
10 received.
PACKET:11,1,5,CD,6B,B2,E,1B,45,E6
Read successful
1,0,1,1,0,0,1,1,1,1,0,1,0,1,1,0,0,1,0,0,1,1,0,1,0,1,1,1,0,0,0,0,1,1,0,1,1


FC2
El mismo esquema aplicaría para la función fc2, en este caso la dirección de inicio es la 10197 y leeremos solo 22 valores luego lo almacenamos en la variable values.

  byte deviceId=17;
  unsigned short start=10197;
  int numValues=22;
  unsigned short values[numValues]; // aquí se almacena la respuesta
 
  ModbusSerial.fc2(deviceId, start, numValues,values);
 
  for(int i=0;i<numValues-1;i++)
      {
      Serial.print(values[i]);
      Serial.print(",");
      }
  Serial.println(values[numValues-1]);

Respuesta

MSG:11,2,0,C4,0,16,BA,8
8 received.
PACKET:11,2,3,CD,6B,32,45,C4
Read successful
1,0,1,1,0,0,1,1,1,1,0,1,0,1,1,0,0,1,0,0,1,1

FC3
Función fc3 (Read Holding Registers), ahora leeremos 3 valores a partir de la dirección 40108 y lo almacenaremos en la variable values.

  byte deviceId=17;
  unsigned short start=40108;
  int numValues=3;
  unsigned short values[numValues];
 
  ModbusSerial.fc3(deviceId, start, numValues, values);

  for(int i=0;i<numValues-1;i++)
      {
      Serial.print(values[i],HEX);
      Serial.print(",");
      }
  Serial.println(values[numValues-1],HEX);

La respuesta que obtendríamos seria

MSG:11,3,0,6B,0,3,76,11
11 received.
PACKET:11,3,6,AE,41,56,52,43,40,49,AD
Read successful

AE41,5652,4340

FC4
Función fc4 (Read Input Registers), ahora leeremos 1 valor correspondiente a la dirección 30009 y lo almacenaremos en la variable values.

  byte deviceId=17;
  unsigned short start=30009;
  int numValues=1;
  unsigned short values[numValues];
  
  ModbusSerial.fc4(deviceId, start, numValues, values);

  for(int i=0;i<numValues-1;i++)
      {
      Serial.print(values[i],HEX);
      Serial.print(",");
      }

  Serial.println(values[numValues-1],HEX);

Respuesta:

MSG:11,4,0,8,0,1,B2,7
7 received.
PACKET:11,4,2,0,A,F8,F4
10 Read successful

A

Ahora veamos ahora una de escritura

FC5
Esta función escribe un solo dato en una dirección específica por eso tiene menos parámetros
ej: escribir en la dirección 173 del DeviceId = 17 el valor de True

  byte deviceId=17;
  unsigned short start=173;
 
  ModbusSerial.fc5(deviceId, start, true);

Respuesta

MSG:11,5,0,AC,0,FF,4F,8
8 received.
PACKET:11,5,0,AC,0,FF,4F,3B
Read successful

El mismo caso aplica para los analógicos

FC6

Ej: Escribir en la dirección 40002 el valor de 3

  byte deviceId=17;
  unsigned short start=40002;
  unsigned short value=3;
 
  ModbusSerial.fc6(deviceId, start, value);

Respuesta

MSG:11,6,0,1,0,3,9A,8
8 received.
PACKET:11,6,0,1,0,3,9A,9B
Read successful


Para escribir múltiples valores discretos utilizamos la función 15

FC15

ej: Escribir un arreglo binario de 10 valores binarios en la dirección 20 del device 17.

  byte deviceId=17;
  unsigned short start=20;
  int numValues=10;
  boolean values[numValues];
 
  values[0] = 1;
  values[1] = 1;
  values[2] = 0;
  values[3] = 1;
  values[4] = 0;
  values[5] = 0;
  values[6] = 1;
  values[7] = 1;
  values[8] = 1;
  values[9] = 0;

  ModbusSerial.fc15(deviceId, start, numValues, values);

Respuesta

MSG:11,F,0,13,0,A,2,CB,1,BC,8
8 received.
PACKET:11,F,0,13,0,A,26,99
Read successful


el mismo esquema para los analógicos

FC16

Escribir un arreglo de 2 valores en la dirección 40002 del device 17.

  byte deviceId=17;
  unsigned short start=40002;
  int numValues=2;
  unsigned short values[numValues];
 
  values[0] = 0xA;
  values[1] = 0x102;

  ModbusSerial.fc16(deviceId, start, numValues, values);

Respuesta

MSG:11,10,0,1,0,2,4,0,A,1,2,C6,8
8 received.
PACKET:11,10,0,1,0,2,12,98
Read successful


lunes, 18 de marzo de 2013

PirateModbus

Basados en el desarrollo de la libreria Modbus presentaremos una herramienta basada en Arduino Mega(1) que puede ser util en algunos casos. 

El PirateModbus, que puede colocarse en el medio de las remotas y el SCADA para examinar los paquetes :)







(1):Utilicé el ArduinoMega porque no tenia en que usarlo era muy viejo "Arduino1280", se pudo hacer el diseño con varios attiny85 conectados por puerto I2C o SPI, solo que queria mas largo el programa y mas complicado de armar >:)

miércoles, 30 de enero de 2013

Libreria Modbus Serial para Arduino (1/2)

En vista de que no encontré una implementación completa de la librería modbus para Arduino decidí hacer una librería que posea al menos todas las funciones mínimas necesarias para leer y escribir en una remota.

Ahora disponible en Github:
https://github.com/fernandezajp/ARDUModBus.git  

Todas la pruebas fueron realizadas utilizando el simulador ModSim el cual nos garantiza que funcionaría perfectamente un hardware real.

Las funciones marcadas en azul corresponden a las implementadas en esta librería


   - Función 1 Read Coil Status
   - Función 2 Read Input Status
   - Función 3 Read Holding Registers
   - Función 4 Read Input Registers
   - Función 5 Force Single Coil
   - Función 6 Preset Single Register
   - Función 7 Read Exception Status
   - Función 8 Diagnostics
   - Función 9 Program 484
   - Función 10 Poll 484
   - Función 11 Fetch Communication Event Counter
   - Función 12 Fetch Communication Event Log
   - Función 13 Program Controller
   - Función 14 Poll Controller
   - Función 15 Force Multiple Coils
   - Función 16 Preset Multiple Registers
   - Función 17 Report Slave ID
   - Función 18 Program 884/M84
   - Función 19 Reset Comm. Link
   - Función 20 Read General Reference
   - Función 21 Write General Reference
   - Función 22 Mask Write 4X Register
   - Función 23 Read/Write 4X Registers
   - Función 24 Read FIFO Queue

A continuación detallamos las tramas de cada función con un ejemplo



Funcion: Read Coil Status (FC=01)


Request: Esta trama realiza la petición del estado de los (discrete coils) del 20 al 56 del dispositivo esclavo con dirección 17.
11 01 0013 0025 0E10
11: Dirección del Esclavo (17 = 11 hex.)
01: Código de la Función (read Coil Status)
0013: Dirección del dato del primer coil para leer. (Coil 20 - 1 = 19 = 13 hex)
0025: Número de coils para leer. (del 20 al 56 =  37 = 25 hex) 
0E10: El CRC (cyclic redundancy check) para manejo de errores.
Response
11 01 05 CD6BB20E1B 45E6 
11: Dirección del esclavo (17 = 11 hex)
01: Código de la función (read Coil Status)
05: Número de data bytes (37 Coils / 8 bits por byte = 5 bytes)
CD: Coils 27 - 20 (1100 1101)
6B: Coils 35 - 28 (0110 1011)
B2: Coils 43 - 36 (1011 0010)
0E: Coils 51 - 44 (0000 1110)
1B: 3 bits no usados & Coils 56 - 52 (0001 1011)
45E6: El CRC (cyclic redundancy check).


Función: Input Status (FC=02)




Request: Esta trama realiza la petición del estado de los (discrete inputs) del 10197 al 10218 del dispositivo esclavo con dirección 17.
11 02 00C4 0016 BA08
11: Dirección del Esclavo (17 = 11 hex.)
02: Código de la Función (read Input Status).
00C4: Dirección de la primera entrada. (10197 - 10001 = 196 = C4 hex.)
0016: Número total de coils de la petición. (197 al 218 =  22 = 16 hex.)
BA08: El CRC (cyclic redundancy check) para manejo de errores.
Response
11 02 03 CD6B32 45C4
11: Dirección del Esclavo (17 = 11 hex.)
02: Código de la función (read Input Status)
03: El numero total de bytes (22 Inputs / 8 bits per byte = 3 bytes)
CD: Entradas Discretas 10204 -10197 (1010 1100)
6B: Entradas Discretas 10212 - 10205 (1101 1011)
32: 2 espacios vacíos & Entradas Discretas 10218 - 10213 (0011 0101)
45C4: El código CRC (cyclic redundancy check).



Función: Read Holding Registers (fc3)




Request: Esta trama realiza la petición del estado de la salida analógica holding registers desde 40108 a 40110 del dispositivo esclavo con dirección 17.
11 03 006B 0003 7611
11: Dirección del Esclavo (17 = 11 hex.)
03: Código de la función (read Analog Output Holding Registers)
006B: Dirección del Dato del primer registro requerido. (40108-40001 = 107 = 6B hex.)
0003: Número total de registros requeridos. (3 registros desde 40108 al 40110) 
7611: El CRC (cyclic redundancy check) manejo de errores.
Response
11 03 06 AE41 5652 4340 49AD
11: Dirección del Esclavo (17 = 11 hex)
03: Código de la función (read Analog Output Holding Registers)
06: Número de bytes (3 registros x 2 bytes por cada uno = 6 bytes)
AE41: Contenido del registro 40108
5652: Contenido del registro 40109
4340: Contenido del registro 40110
49AD: El CRC (cyclic redundancy check).



Función: Read Input Register (fc4)




Request: Esta trama realiza la petición del estado de la entrada analógica input registers #30009 del dispositivo esclavo con dirección 17.
11 04 0008 0001 B207
11: Dirección del Esclavo (17 = 11 hex.)
04: Código de la Función (read Analog Input Registers)
0008: Dirección de dato del primer registro requerido. (30009-30001 = 8)
0001: El numero de registros para leer. (leer 1 registro) 
B207: The CRC (cyclic redundancy check) for error checking.
Response
11 04 02 000A F8F4
11: Dirección del Esclavo (17 = 11 hex)
04: El Código de Función (read Analog Input Registers)
02: Numero de bytes de datos (1 registro x 2 bytes por registro = 2 bytes)
000A: Contenido del registro 30009
F8F4: El código CRC (cyclic redundancy check).



Función: Force Single Coil (fc5)




Request: Este comando escribe el registro del discrete coil # 173 a ON en el dispositivo esclavo con dirección 17.
11 05 00AC 00FF 4F08
11: Dirección del Esclavo (17 = 11 hex)
05: Código de la Función (Force Single Coil)
00AC: Dirección de Dato del coil. (# 173 - 1 = 172 = AC hex)
00FF: Estado a escribir ( 00FF = ON,  0000 = OFF ) 
4F08: El Código CRC (cyclic redundancy check) para manejo de errores.
Response
11 05 00AC 00FF 4F3B
11: Dirección del Esclavo (17 = 11 hex)
05: Código de la Función (Force Single Coil)
00AC: Dirección de Dato del coil. (coil# 173 - 1 = 172 = AC hex)
00FF: Estado actualizado ( 00FF = ON,  0000 = OFF ) 
4F3B: El Código CRC (cyclic redundancy check) para manejo de errores.



Función: Preset Single Register (fc6)




Request: Este comando escribe el contenido del analog output holding register # 40002
al dispositivo esclavo con direccion 17.
11 06 0001 0003 9A08
11: Dirección del Esclavo (17 = 11 hex)
06: Código de la Función (Preset Single Register)
0001: Dirección del registro de Dato. (# 40002 - 40001 = 1 )
0003: Valor a escribir.
9A08: El código CRC (cyclic redundancy check) para manejo de errores.
Response: La respuesta normal es un echo del request.
11 06 0001 0003 9A9B
11: Dirección del Esclavo (17 = 11 hex)
06: Código de la Función (Preset Single Register)
0001: Dirección del registro de Dato. (# 40002 - 40001 = 1 )
0003: Valor escrito.
9A9B: El código CRC (cyclic redundancy check) para manejo de errores.



Función: Force Multiple Coils (FC=15)




Request: Este comando escribe el contenido discreto de una serie de 10 coils desde  #20 al #29 en el esclavo con direccion 17.
11 0F 0013 000A 02 CB01 BF08
11: Dirección del Esclavo (17 = 11 hex.)
0F: Código de la Función (Force Multiple Coil, 15 = 0F hex.)
0013: Dirección del primer coil. (# 20 - 1 = 19 = 13 hex.)
000A: Número de coils para ser escrito (10 = 0A hex)
02: Numero de bytes de datos (10 Coils / 8 bits por byte = 2 bytes)
CB: Coils 27 - 20 (1100 1011
01: 6 espacios vacios & Coils 29 - 28 (0000 0001
BF08: El Código CRC (cyclic redundancy check) para manejo de errores.

Response
11 0F 0013 000A 2699
11: Dirección del Esclavo (17 = 11 hex)
0F: Código de la Función (Force Multiple Coil, 15 = 0F hex)
0013: Dirección del primer coil. (coil# 20 - 1 = 19 = 13 hex)
000A: Número de coils para ser escrito (10 = 0A hex)
2699: El Código CRC (cyclic redundancy check) para manejo de errores.



Función: Preset Multiple Registers (FC=16)




Request: Este comando escribe el contenido de 2 analog output holding registers # 40002 & 40003 en el esclavo con dirección 17.
11 10 0001 0002 04 000A 0102 C608
11: Dirección del Esclavo (17 = 11 hex)
10: Código de la función (Preset Multiple Registers 16 = 10 hex)
0001: Dirección del primer registro. (# 40002 - 40001 = 1 )
0002: Numero de registros para escribir.
04: Numero de bytes (2 registros x 2 bytes por cada registro = 4 bytes)
000A: Valor para escribir para el registro 40002
0102: Valor para escribir para el registro 40003
C608: El código CRC (cyclic redundancy check) para el manejo de errores.
Response
11 10 0001 0002 1298
11: Dirección del Esclavo (17 = 11 hex)
10: Código de la función (Preset Multiple Registers 16 = 10 hex)
0001: Dirección del primer registro. (# 40002 - 40001 = 1 )
0002: Número de bytes escritos.
1298: El Código CRC (cyclic redundancy check) para manejo de errores


Ver Parte 2 En la siguiente entrada esta un pequeño ejemplo de como se usa

http://fernandezajp.blogspot.com/2013/07/uso-de-la-libreria-modbusserial.html