google translator

martes, 20 de diciembre de 2011

Creando utilidades para los módulos ADAM6050 de Advantech

1.- LUCES,...

Primero quiero presentar brevemente lo que son estos módulos, para qué se usan y porqué he investigado esta capacidad que el fabricante no me ha sabido|querido|ha pasado de proporcionar.

Los módulos ADAM6050 son módulos controladores que tienen entradas para elementos de detección y salidas para activación de relés. ¿Qué quiere decir esto? Básicamente que puedo incorporarle detectores (De presencia, interruptores,…) y quitar o suministrar electricidad a componentes concretos (Telecontrol de luces, persianas automáticas,…)

Esto, sumado a la API que nos da el fabricante, lo convierten en una solución rápida para temas de inmótica, domótica, etc.

Mi intención es vincular una luz (salida 1) al controlador que se encienda en base a un interruptor (entrada 1) y que, a su vez, pueda telecontrolar desde una aplicación.

Al grano. Cuando programamos uno de estos aparatos, podemos hacerlo de dos maneras:

1.- Utilizar la API para conectarnos mediante TCP/IP al ADAM6050 para preguntar los estados y activar las salidas (Tipo: ¿Cuál es el estado del interruptor de la entrada 1?) o encender/apagar una luz (incorporando relés de 12V, claro),…

2.- Meter en la memoria del ADAM6050 (GCL) un esquema de lo que tiene que hacer automáticamente en función del estado de sus entradas/salidas.

Problemas. Si aplicas GCL a una salida, no puedes implementar un telecontrol desde la API a esa salida a la vez.

Ejemplo. Programamos con GCL lo siguiente: Si te activan la entrada1, activa la salida1 (Básicamente, la entrada1 podría ser un interruptor y la salida1 una luz)

Dada esta afirmación, mediante las APIs no podríamos activar la salida1 (Luz) directamente porque depende exclusivamente de la entrada1 (Interruptor)

2.- CAMARA,...

Bueno, mi objetivo es que, además de que la luz se manipule mediante un interruptor, que yo pueda también telecontrolar esa luz mediante órdenes TCP/IP, sin la necesidad de cambiar el estado del dispositivo de entrada.

¿Cómo puedo hacerlo? Dado que tengo que incorporar el GCL obligatoriamente, me voy a centrar en cambiar el estado de la entrada1.

Realmente no puedo cambiar el estado de la entrada1 porque se activa/desactiva mediante la pulsación del interruptor pero si puedo, mediante el software del fabricante, cambiar su “estado por defecto” (Apagado o encendido por defecto) de manera que cambiaría el estado de la entrada y a la vez, afectaría a la salida. Me voy a focalizar en esa opción "Invert signal":


Me repasé toda la API y me di cuenta que el fabricante no había introducido esta posibilidad en la API.

Entonces realicé la consulta al fabricante…

Sólo decir que si no me hubiese afeitado desde entonces, el día de Reyes iría en camello.

Vaya, ¿El fabricante tiene un programa que lo hace a través de TCP/IP y yo no puedo? Frustrante…

3.- ¡ACCION!

Primero vamos a analizar qué es lo que manda el programa al módulo 6050 cuando le damos a “Invert signal”, que es la característica que queremos desarrollar. Con la aplicación abierta, usamos el wireshark para mirar qué es lo que va pasando (filtrando por la IP del ADAM6050). Vemos 3 cosas:


1.- Vemos que el programa usa el protocolo Modbus, que es un protocolo estándar para la comunicación con controladores (Dicho mal y pronto) Realiza queries de estados y recibe responses con los mismos.

3.- Aquí se puede comprobar cómo está leyendo el estado de referencia 0 (entrada 0) y lee un solo estado (bit count) Se podría pedir con un “bit count” de 5 y nos devolvería los estados de la entrada 0 a la 4.

2.- Bueno, éste es el que nos interesa. Es un intercambio de paquetes UDP que se intercambian justo cuando aplico la opción “Invert signal”… El puerto “blackjack” es el 1025. Entiendo que debo seguir por aquí. Advantech 0 - Trosman 1

En el wireshark, señalo los paquetes UDP -> Follow UDP Stream… -> Convierto esta conversación con la opción “Hex dump”, de manera que ya tengo todo en binario:


Bueno, el siguiente paso lógico sería desactivar “Invert signal” y ver cómo cambia esta traza UDP. Incluso también realizarlo con otra entrada y revisar la traza para ver los cambios.

Como suele decir un compañero mío, “a veces la informática es una ciencia infusa” y, bueno, este es el mejor ejemplo, ya que la traza resultante es igual a la anterior. Advantech 1 - Trosman 1.

Bueno, voy a intentar copiar esa comunicación y hacer pruebas. Para ello, primero me voy a crear una herramienta que me genere esa trama de comunicación UDP.

Entiendo que, como es UDP, no voy a necesitar establecer la conexión y se la voy a poder “meter doblá”. Para ello, voy a utilizar C#, la clase UDPClient en concreto. ¿Por qué? Porque es lo que tengo más a mano.

Después de un rato columpiándome con la conversión de bytes, termino el programita (todo en el main, para que sea cortito):


--------

const string IP = "192.168.1.40";

const int PORT = 1025;

// Genero los objetos de conexión

IPAddress ip = IPAddress.Parse(IP);

IPEndPoint endPoint = new IPEndPoint(ip, PORT);

UdpClient cliente = new UdpClient();

// Genero los bytes de los paquetes

byte[] paquete1 = { 36, 48, 49, 67, 56,

48, 56, 48, 48, 48, 48, 48, 48, 48, 48,

48, 48, 48, 48, 48, 48, 48, 48, 48, 48,

48, 48, 48, 48, 48, 48, 48, 48, 48, 48,

48, 48, 48, 48, 48, 13

};

byte[] paquete2 = { 36, 48, 49, 67, 13 };

byte[] paquete3 = { 36, 48, 49, 70, 13 };

byte[] paquete4 = { 36, 48, 49, 48, 13 };

// Comienzo la conversación

cliente.Connect(endPoint);

cliente.Send(paquete1, paquete1.Length);

cliente.Receive(ref endPoint);

cliente.Send(paquete2, paquete2.Length);

cliente.Receive(ref endPoint);

cliente.Send(paquete3, paquete3.Length);

cliente.Receive(ref endPoint);

cliente.Send(paquete4, paquete4.Length);

cliente.Receive(ref endPoint);

cliente.Close(); // Cerramos la conexión

// Avisamos

Console.WriteLine("Trama enviada!");

Console.ReadKey();

-------

Allá vamos. Comienzo enviando esta conversación al Adam y veo… que algo he hecho, pues he activado el “Invert signal” de la entrada0. Empecé a fijarme en el byte marcado en rojo en el código y me di cuenta que es… un flag! 56 ON – 48 OFF.

Exactamente, teníamos el primer invertsignal! El orden de bytes del primer mensaje funcionaría así:

[0] [1] [2] [3] [Invert0] [Reset0] [Invert1] [Reset1] [Invert2] [Reset2] [Invert3] [Reset3]

Los reset servirían para resetear el modo de las entradas digitales.

Después de crear un GCL y realizar la prueba, fue bastante satisfactoria. Pude encender y apagar las luces con el GCL en marcha.

Con esta información y revisando algo más por si cambia algo en la trama entra diferentes dispositivos, se puede crear la librería de rigor. Esto a gusto de consumidor.

Comentario sobre seguridad: Estos aparatos pueden modificar su comportamiento con paquetes UDP, esto es tremendamente peligroso por lo que aconsejo mantener como mínimo en subredes diferentes (Y si puede ser en otra VLAN o en otro switch a parte del de producción, mucho mejor)

Recordad que si tardas unos meses en desarrollar un SCADA con este tipo de elementos, cualquier investigador de seguridad podría sacar fallos muy gordos en poco tiempo… Por eso conviene, desde mi punto de vista, reforzar el acceso desde la red.

Espero que os haya gustado, si tenéis cualquier comentario o pregunta, dejadme un comentario. (Críticas no por favor, ando bajo de autoestima XDDDD)