Search

Adquiriendo datos con Python y dispositivos ModBus

Vamos a empezar el año cambiando de tercio. Hoy vamos a ver cómo acceder a las lecturas de un dispositivo Modbus utilizando Python y su librería pymodbus.

En las comunicaciones industriales se puede hablar de una estructura con tres niveles:

  • En el nivel más bajo estaría el bus de campo. Es el nivel más cercano al proceso que se quiere controlar y/o monitorizar. Y es aquí donde PLCs (controladores lógicos programables), equipos de medida, y otros pequeños automatismos se integran para comunicarse entre sí con el fin de supervisar un determinado proceso.  A todo este conjunto de aparatitos se le llama célula de fabricación.
  • Por encima del nivel anterior estaría el nivel de LAN. Es en éste nivel dónde se comunican entre sí varias células de fabricación.
  • Por último tendríamos el nivel LAN/WAN. Sería el nivel más próximo al área de gestión. Este nivel se encarga de integrar los niveles anteriores en una estructura de planta industrial o incluso de múltiples plantas en diferentes emplazamientos. Aquí es donde se centralizaría el control y supervisión de todos los procesos a través de, por ejemplo, sistemas SCADA, y se incorporarían bases de datos.

Estos tres niveles se pueden representar en lo que se llama pirámide CIM (Computer Integrated Manufacturing).

Nosotros nos vamos a quedar en el nivel más bajo, en el bus de campo. Como he dicho antes es el nivel más sencillo y cercano al proceso a controlar. Un bus de campo es una red de comunicación entre los diferentes dispositivos que están implicados en el control y supervisión de un proceso, y que permite intercambiar órdenes y datos entre dichos dispositivos. Dentro de esta red, los dispositivos podrán ser todos pertenecientes a un mismo fabricante o a distintos, pero lo importante es que se puedan comunicar entre ellos a través de un protocolo reconocido por todos ellos, independientemente del fabricante del aparato.

Uno de los buses de campo más extendidos actualmente es MODBUS. Éste fue diseñado por la empresa Modicon en 1979 para implementarlo en sus PLCs, y está basado en una arquitectura maestro/esclavo (RTU) o cliente/servidor (TCP/IP). Realmente la denominación como bus de campo a MODBUS no es correcta, ya que MODBUS es un protocolo. Sin embargo, en la industria se suele hablar de MODBUS como un estándar de bus de campo. Voy a hacer un resumen rápido de sus características:

  • Es público y gratuito
  • El medio físico de conexión puede ser un bus RS-485 o RS-422.
  • Las velocidades de transmisión van desde los 75 a los 19200 baudios.
  • La distancia máxima entre dispositivos puede alcanzar hasta los 1200 metros sin necesidad de usar repetidores.
  • La estructura lógica es del tipo maestro-esclavo.
  • El número máximo de dispositivos es de 63 esclavos más un dispositivo maestro.
  • La codificación de datos dentro de la trama puede hacerse en modo ASCII o en RTU (Remote Transmission Unit).
  • Los campos de la trama del mensaje son los siguientes:
    • Número de dispositivo (1 byte): del 0 a 255. La dirección 0 es para mensajes broadcast.
    • Código de función (1 byte): Dependiendo de la función podemos transmitir datos u órdenes al esclavo.
    • Campo de subfunciones/datos (n bytes): Aquí van los parámetros necesarios para ejecutar la función anterior. Pueden palabras a leer o escribir, número de bits, etc…
    • Palabra de control de errores (2 bytes): En código ASCII es el CRC. Para el caso de codificación RTU el CRC se calcula mediante una fórmula.

MODBUS: Funciones básicas y códigos de operación

Para nuestro ejemplo vamos a usar una sonda de temperatura y humedad (data sheet de la sonda).

Para poder conectarme a ella y leer las temperaturas, voy a utilizar,  además de la propia sonda, un conversor de USB a RS-485, un pc con Ubuntu como SO, python y la librería pymodbus.

Lo primero que debemos hacer al conectar el conversor al puerto USB es verificar si se ha detectado. Para ello podemos listar los puertos serie ejecutando el siguiente comando en la consola:

Así podemos ver que el puerto que está utilizando el conversor es el ttyUSB0.

El nombre completo de este puerto es /dev/ttyUSB0, que corresponde al Conversor USB-serie 1. Es posible que tengas que cambiar los permisos mediante estos comandos:

Vale. Vamos a empezar a escribir nuestro código.

Lo primero que vamos a hacer es escribir una función que nos va a devolver el mapa de memoria de la sonda. ¿Qué es el mapa de memoria?. Es una tabla de direcciones en las que se puede acceder a los diferentes datos de la sonda. En un dispositivo básicamente podemos encontrar estos 4 bloques:

  • Salidas digitales (coils).
  • Entradas digitales (inputs).
  • Salidas analógicas (holding registers).
  • Entradas analógicas (input registers).

En el caso de nuestra sonda de temperatura y humedad, vamos a acceder a las direcciones de memoria de las salidas analógicas (holding registers).

Mapa de memoria Modbus

Según esto nos vamos a crear una función que devuelva un diccionario con tres claves-valor. Una para las direcciones de las salidas tipo integer, correspondientes a la configuración Modbus de la sonda. Otra para las direcciones de las salidas tipo float, correspondientes a las medidas de la sonda. Y una última para los valores por defecto de la configuración Modbus de la sonda. Nos quedaría algo tal que así:

Una vez que tenemos los distintos diccionarios definidos, vamos a crearnos un script que se conecte a la sonda, lea los valores y los guarde en una base de datos. Yo voy a usar MySQL.

Lo primero que vamos a hacer es una función para realizar la conexión a la base de datos y la inserción de los valores leídos de la sonda.

Para ello crearemos el archivo mysqlConnect.py y dentro escribiremos la siguiente función:

Ésta función recibe como argumento una lista de valores, y lo que hace es conectarse a la base de datos e insertar en una tabla esa lista de valores, creando un nuevo registro.

Bien. Vamos ahora a escribir el script principal, que será el que realiza la conexión con la sonda, la lectura de los valores, y llamará a la función insertQuery para guardarlos datos obtenidos en la base de datos MySQL.

Empezamos importando las librerías necesarias, y creando una serie de variables necesarias para realizar la conexión con la sonda.

Para la lectura de los datos, utilizaremos la función read_holding_registers.

Ésta admite como parámetros, la dirección desde la que se empieza a leer, el número de registros a leer, y el número de periférico del dispositivo al que se le pregunta, en este caso, nuestra sonda, que lleva el número de periférico 1.

Empezaremos leyendo los valores de los registros correspondientes al diccionario memo_integers, que hemos creado anteriormente, y que contiene los datos de configuración de la sonda. En este caso, solo leeremos el primer registro.

A continuación, leeremos los valores de las medidas, cuyas direcciones cogemos del diccionario memo_floats. En este caso, al ser un tipo de dato float, tenemos que leer, por un lado el registro 1 y por otro el registro 2. Necesitaremos ambos para obtener el valor . Para ello añadimos el siguiente bloque de código:

Si hacemos un print del resultado de esas lecturas, que hemos llamado rr1 y rr2, podemos ver sus valores. Por ejemplo nos vamos a fijar en la humedad absoluta:

El valor de rr1 es 23040 y el de rr2 es 16808. A partir de dichos valores, queremos obtener otro valor en formato coma flotante simple (32 bits) (IEEE-754 Floating Point).
Para obtener el valor de la humedad en dicho formato, tenemos que realizar los siguientes pasos:
1.- Construir, a partir de los dos números decimales obtenidos, un número binario de 32 bits. Para ello pasamos cada número decimal obtenido a su valor binario, y le añadimos un 0 al inicio, para tener 8 bits en cada parte. Este paso lo realizamos con las líneas de código:

El resultado es el siguiente string que representa un número binario de 32 bits:
01000001101010000101101000000000

2. Lo siguiente es pasar el string a un entero, pasándole como argumento la base que queremos utilizar, en este caso, base 2.

3. Utilizar la librería struck de python para poder poder convertir los tipos de datos obtenidos en el formato requerido:

Con lo que obtenemos un valor para la temperatura de 21.04 ºC

En el este conversor online podemos comprobar que el resultado es correcto:

FloatConverter/IEEE754.html

Los datos se van añadiendo en una lista, la cual que se pasa como argumento al final del script a la función insertQuery, que se encarga de insertar dichos valores en una tabla de la base de datos mySQL.

Este script podemos automatizarlo utilizando cron, de manera que se ejecute, por ejemplo cada 15 minutos.

Para finalizar, podemos hacernos una función sencilla para la lectura de algunos datos de temperatura guardados en mySQL, y representarlos en un gráfico:



Puedes descargarte el código de mi 

3 Comments

Deja un comentario

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.