You are on page 1of 10

Adquisicin de datos en Linux

Por: Jess Castrejn Figueroa


Facultad de Ciencias, UNAM

Introduccin
Muchas veces, nos vemos necesitados de conectar algn dispositivo a nuestra computadora por
medio del puerto serie, con distintos fines, como para la adquisicin de datos por ejemplo. Cuando
trabajamos en windows la tarea no resulta tan compleja, debido a que la mayora de los aparatos
cuentan con software incluido, pero qu pasa cuando lo queremos usar bajo
GNU-Linux?. Este es el motivo de este texto.
1 Adquisicin de datos
Un dispositivo de adquisicin de datos es un medio para convertir variables fsicas seales elctricas,
y mediante un convertidor anlogo-digital se convierte dicha seal en pulsos elctricos bien definidos
en intensidad y longitud que son reconocibles para la computadora, por ejemplo un pulso de 5 volts
representa un uno, mientras que un pulso de 1 volt representa un cero, definiendo as el lenguaje
binario usado por la computadora.
Por medio de algn protocolo de comunicacin previamente definido y conocido universalmente (El
protocolo ModBus por ejemplo) se logra establecer una comunicacin entre el dispositivo de
adquisicin y nuestra computadora, dando un significado a las cadenas enviadas y recibidas.
Para una informacin completa de la adquisicin de datos y la conversin anloga-digital, ver aqui.

2 Puertos
Un puerto de E/S (Entrada/salida) es un modo de conseguir que los datos entren y salgan de la
computadora. Existen muchos tipos de puertos de E/S, como los puertos serie, puertos paralelos,
controladores de disqueteras, tarjetas Ethernet, etc. Trataremos con puertos serie ya que las
terminales (Nuestro dispositivo de adquisicin por ejemplo) son dispositivos serie. Cada puerto serie
debe tener una direccin de E/S.
Tabla1.- Direcciones en Linux de los dispositivos serie
Direccin del dispositivo

Descripcin

/dev/ttyS0

Primer puerto serie nativo. (Equivalente a COM1 en Windows)

/dev/ttySn

n-simo puerto serie nativo

/dev/ttyUSB0

convertidor USB-serie 1

/dev/ttyUSBn

n-simo convertidor USB-serie

En el caso de que nuestra computadora no cuente con una entrada para puerto serie, podemos usar
un convertidor USB-Serie, en tal caso aparecer no como un dispositivo serie nativo /dev/ttySn sino
como /dev/ttyUSBn.

Fig 1.- Convertidor USB-Serie(RS-232)

Hay veces en las cuales Linux no asigna una direccin a nuestro dispositivo serie, si ese es el caso,
ver este Tutorial.

2.1 Leer el puerto directamente


Hay situaciones en las que nuestro dispositivo trabaja sin protocolo de comunicacin, ModBus es el
protocolo de comunicacin mas usual en dispositivos comerciales, y es el el del cual hacemos uso
mas adelante en el texto, si este es el caso, puede que el dispositivo este mandando seales
continuamente a la computadora, por lo cual podramos ser capaces de leer directamente la direccin
E/S, cat /dev/ttySn
O con pequeo script de bash, en este caso para ttyS0:
#!/bin/bash
while true
do
read LINE < /dev/ttyS0
echo $LINE
done

Para mas informacin ver el manual de read.


Pero como esto cumple apenas un caso muy particular, lo mejor es tener una herramienta mas
general que funcione para cualquier tipo de dispositivo serie.

3 Programando el puerto Serie


A continuacin se presentan las funciones bsicas para poder acceder, dar lectura o escritura al
puerto serie, mediante las funciones open(), read() y write(), hechas en C, y probadas bajo Ubuntu
9.10 con el compilador gcc 4.4.1.
A diferencia de los archivos normales, antes de usar el puerto serie es necesario configurarlo,
estableciendo la velocidad de trabajo y su modo de funcionamiento.
La cabecera de un cdigo fuente en C, para estos propsitos, debe incluir al menos, las siguientes
bibliotecas:

#include
#include
#include
#include

<termios.h>
<stdio.h>
<stdlib.h>
<string.h>

Para mas informacin ver el manual de cada biblioteca.


A continuacin se muestran algunos ejemplos de como han de ser las funciones para el manejo del
puerto

3.1 Serial_open()
int serial_open(char *serial_name, speed_t baud)
{
struct termios newtermios;
int fd;
fd = open(serial_name,O_RDWR | O_NOCTTY);
newtermios.c_cflag= CBAUD | CS8 | CLOCAL | CREAD;
newtermios.c_iflag=IGNPAR;
newtermios.c_oflag=0;
newtermios.c_lflag=0;
newtermios.c_cc[VMIN]=1;
newtermios.c_cc[VTIME]=0;
cfsetospeed(&newtermios,baud);
cfsetispeed(&newtermios,baud);
if (tcflush(fd,TCIFLUSH)==-1) return -1;
if (tcflush(fd,TCOFLUSH)==-1) return -1;
if (tcsetattr(fd,TCSANOW,&newtermios)==-1) return -1;
return fd;
}

La funcin serial_open() abre el puerto serie para lectura/escritura y en modo crudo (raw). En este
modo no se procesan ciertos caracteres especiales de control. Se usa el puerto serie slo para
enviar/recibir bytes sin interpretarlos.
Se configura el modo de funcionamiento serie a 8N1 (8 bits de datos, 1 bit de stop y sin paridad). La
velocidad es la indicada por el usuario en el parmetro baud. Es una constante que puede valer:
B9600, B19200, etc. Se puede encontrar ms informacin sobre todos los valores posibles en la
pgina de manual de termios (man termios)
Esta funcin devuelve el descriptor del puerto serie (o -1 si ha ocurrido un error), que ser necesario
para realizar las lecturas y escrituras.

3.2 Serial_send()
La funcin serial_send() enva una cadena de bytes por el puerto serie. Lo nico que hace es invocar
la llamada al sistema write(). Como parmetros se pasa el descriptor serie del puerto serie (serial_fd),
devuelto por la funcin serial_open(), el arreglo con los datos a enviar (data) y su tamao (size).
void serial_send(int serial_fd, char *data, int size)
{
write(serial_fd, data, size);
}

3.3 Serial_read
La funcin serial_read() se usa para leer datos del puerto serie. Se le pasan como parmetros el
descriptor del puerto serie (serial_fd), el donde almacenar los datos recibidos (data), el tamao
mximo de bytes a recibir (size, para no desbordar el ) y el tiempo mximo para recibir los datos
(timeout_usec). Si transcurre un tiempo igual a timeout_usec y no se han recibido datos, la funcin
retornar y devolver el control a la que la invoc.
Para realizar las lecturas no bloqueantes, se usa la llamada al sistema select(). En este caso
devuelve 1 si hay datos esperando a ser ledos y 0 si ha ocurrido un timeout. A continuacin se invoca
a read() para leer estos datos. El proceso se repite para garantizar que es posible recibir datos de
tamao size. Al utilizar convertidores USB-serie, los datos llegan en diferentes grupos siendo
necesario realizar ms de una llamada a read().
Se devuelve el nmero de bytes ledos (o 0 si ha ocurrido un timeout).
int serial_read(int serial_fd, char *data, int size, int timeout_usec)
{
fd_set fds;
struct timeval timeout;
int count=0;
int ret;
int n;
do {
FD_ZERO(&fds);
FD_SET (serial_fd, &fds);
timeout.tv_sec = 0;
timeout.tv_usec = timeout_usec;
ret=select (FD_SETSIZE,&fds, NULL, NULL,&timeout);
if (ret==1) {
n=read (serial_fd, &data[count], size-count);
count+=n;
data[count]=0;
}
} while (count<size && ret==1);
return count;
}

Se adjunta un ejemplo de un programa, enviar_recibir.c que enva una cadena de prueba al


dispositivo, y recibe la respuesta de ste, de lo contrario espera por respuesta determinado tiempo
(timeout) si no la recibe, imprime timeout.
Para una informacin completa acerca de la programacin del puerto serie con C en Linux, ver el

Manual para programacion del puerto serie para sistemas operativos POSIX, de Michael R. Sweet; y
el Serial Howto de Greg Hankins.
Para distintos tipos de programacin del puerto, ver el Como programar el puerto serie, de Peter
Baumann.

4 Protocolo de comunicacin ModBus


ModBus es un protocolo de comunicacin basado en la arquitectura maestro/esclavo, o
cliente/servidor, diseado en 1979 por Modicon para su gama de controladores lgicos programables
(PLCs). Convertido en un protocolo de comunicaciones estndar de facto en la industria es el que
goza de mayor disponibilidad para la conexin de dispositivos electrnicos industriales. Una de las
razones por las cuales el uso de ModBus es superior a otros protocolos de comunicaciones es porque
es publico y su implementacin requiere poco desarrollo.
Existen dos tipos de variaciones, RTU con una representacin compacta binaria de los datos y ASCII
con una representacin legible del protocolo pero menos eficiente. Adems existe La versin
ModBus/TCP es muy semejante al formato RTU, pero estableciendo la transmisin mediante
paquetes TCP/IP.
Con el protocolo ModBus, el maestro mediante la direccin del dispositivo esclavo, pide una peticin
mediante una funcin (lectura, escritura, etc), el esclavo procesa la validez de la peticin, si es valida
realiza la accin solicitada y regresa la respuesta al maestro, en caso contrario, devuelve un aviso de
error.
Tabla 2.- Funciones del protocolo ModBus.*

*Nota: La lista se encuentra en ingls ya que las bibliotecas que hemos de usar se encuentran programadas en ingls, por lo que no hay
mucho caso con traducir cada una de las funciones, para que despus vuelvan al ingls.

Para una descripcin detallada de cada una de las funciones ver el manual de ModBus oficial,
descargarle de la pagina de ModBus. Ahora nos centraremos en la utilizacin de la funcin 03
(read_holding_registers).

4.1 Libmodbus
Libmodbus, es una biblioteca escrita en C, para Linux (OSX) para el manejo del protocolo ModBus,
soporta RTU y TPC. La biblioteca esta bajo la licencia GPL.
Nosotros hacemos
uso de versin libmodbus-2.03, pero se pueden descargar otras versiones descargarles de la pagina
oficial.
Para instalar la versin 2.03:
wget http://launchpad.net/libmodbus/trunk/2.0.3/+download/libmodbus-2.0.3.tar.gz
tar zxvf libmodbus-2.0.3.tar.gz
cd libmodbus-2.0.3
./configure
make
sudo make install
sudo ln -s /usr/local/lib/libmodbus.so.2.0.0 /usr/lib/libmodbus.so
sudo ln -s /usr/local/lib/libmodbus.so.2.0.0 /usr/lib/libmodbus.so.2
exit

Para poder hacer uso de ellas, basta agregar en la cabeza del codigo fuente include
<modbus/modbus.h> y al momento de compilar, es necesario agregarle -lmodbus alf inal de la
instruccion:
gcc ejemplo.c -o ejecutable -lmodbus

ver manual de gcc.

4.2 Configuracin de un dispositivo


Cuando vamos a hacer uso de un dispositivo, y obtener datos de ste por medio de nuestra
computadora, lo primero que debemos de hacer, es configurarlo y despus ajustar los parmetros de
comunicacin que acabamos de configurar a nuestro programa. Los parmetros a configurar, o que
debemos de conocer para este fin son los siguientes:
1.-Modo de comunicacin RTU
2.-Direccin hardware del dispositivo
Asignada por Linux, al conectar el puerto serie presentadas las posibles en la tabla 1 (/dev/ttyUSB0)
3.-Direccin esclavo del dispositivo
Es la direccin lgica (en hexadecimal si esta configurado el dispositivo en RTU) a la cual el maestro enviara la
peticin de la accin a realizar, puede tomar valores de 1~125
3.- Tasa de transmisin, o velocidad de transmisin (Baud rate)
ModBus soporta 2,400, 4,800, 9,600, 19,200, 38,400 bps ; 9600 bps por default.

4.- Longitud de los datos a enviar (en bits)


5.-Paridad
Normalmente se usa ninguna (none) ya que esta no forma parte del protocolo, por por cuestiones tcnicas a
veces es necesario usarla (odd, even), leer el manual de modbus para mas informacin.
6.-Bits de paro
El numero de bits para hacer de separacin entre los paquetes de datos a enviar por el esclavo

4.3 Uso de funciones y direcciones de registros


Las direcciones de registro, es la asignacin lgica de un numero a un bloque de informacin del
esclavo, es decir, la contrasea que la funcin dada por el maestro debe dar para poder obtener una
accin del esclavo, por ejemplo, un termmetro, guarda el valor de la temperatura actual mostrada en
pantalla en la direccin 0x0001, y guarda la informacin de las unidades usadas en 0x0002, entonces
al enviar la peticin de funcin 03 (read holding registers) para leer el valor de la temperatura actual,
se tendr que aadir el parmetro 0x0001, y si queremos cambiar la unidades usadas, enviaremos la
peticin de la funcin 06 (write single register) supongamos que 0,1 es para C y F respectivamente,
entonces si escribimos 0 con la funcin 06 tendremos la temperatura en C.
A continuacin se muestra en una tabla las caractersticas que han de seguir para una peticin a la
funcin read holding registers (03)
Solicitud
Cdigo de funcin
Direccin de inicio
Cantidad de Registros
Respuesta
Cdigo de funcin
Contador de Bytes
Registro del valor
*N= Cantidad de registros

1 Byte
2 Byte
2 Byte

0x03
0x0000 a 0xFFFF
1 ~ 125

1 Byte
1 Byte
2xN Bytes

0x03
2xN*

1 Byte
1 Byte

0x83
01, 02, 03, 04

Error
Cdigo de error
Cdigo de excepcin

Aqu un ejemplo de solicitud a leer los registros de 108 110:


Solicitud
Funcin
Direccin de inicio alta
direccin de inicio baja
Numero de registros alta
Numero de registros baja

3
0
6B
0
3

Repuesta
Funcin
Contador de Bytes
Valor del registro alto (108)
Valor del registro bajo (108)
Valor del registro alto (109)
Valor del registro bajo (109)
Valor del registro alto (110)
Valor del registro bajo (110)

3
6
2
2B
0
0
0
64

El contenido del registro 108 se muestran como los dos valores de bytes de 02 2B hexadecimal o
decimal 555. El contenido de los registros son 109-110 00 00 y 00 64 hexadecimal o decimal 0 y 100,
respectivamente.

4.4 Implementacin al Cdigo


A continuacin se presenta el cdigo para leer el PV (process value, temperatura en este caso) de un
convertidor de temperaturas DELTA DTB (Serie B) que trabaja con un protocolo de comunicacin
Modbus RTU, utilizando un convertidor Serie USB/RS-485. Ver el manual del convertidor, para tener
la informacin de las direcciones que necesitamos para hacer este programa.
/**************** delta.c *****************/
#include <stdio.h>
#include <stdlib.h>
#include <modbus/modbus.h>
#define DELTA_DTB

0x01

//Direccion esclavo del convertidor

int main(void)
{
modbus_param_t mb_param;
int ret;
float a;
uint16_t data[2];
/* Parametros de configuracion, direccion hardware
velocidad de transmicion (Baud rate), paridad,
longitud de los datos, y bit de paro, respectivamente*/
modbus_init_rtu(&mb_param, "/dev/ttyUSB0", 9600, "none", 8, 1);
/* Abrimos el puerto serie-modbus */
if (modbus_connect(&mb_param) == -1) {
printf("ERROR Connection failed\n");
exit(1);
}
/*Usamos la funcion 03 (read holding registers), configurada
con los parametros:
1.- &mb_param = /dev/ttyUSB0
2.-Direccion del primer registro a leer
3.-Numero de registros
4.-Longitud de los datos (definido con "uint16_t data[2]" donde
el 2 es porque "No. de registros multiplicado por 2"
*/
ret = read_holding_registers(&mb_param, DELTA_DTB, 0x1000, 1, data);
a=data[0]/10;
direccion 0x1000 "PV"
printf("%.1f",a);
/* Cierra el puerto modbus */
modbus_close(&mb_param);
return(0);
}

//data[0] primer registro, con

Para compilarlo:
gcc delta.c -o delta -lmodbus

Con esto, obtenemos como salida, por ejemplo 12.5. Que es el PV, segn el manual de convertidor
de temperaturas marca Delta.

4.5 Manipulacin de los datos recibidos


Ahora tenemos un programa que hace uso del protocolo de comunicacin ModBus, podemos hacer
una herramienta mas para que, no solo obtener los datos del conversor, sino obtener un flujo
constante de datos a travs del tiempo, y aun mas, gratificarlos respecto al tiempo.
El siguiente script de bash, realiza la ejecucin del programa anterior un numero indefinido de veces,
con un intervalo de tiempo constante entre ejecuciones, y muestra en pantalla, el tiempo seguido de
la temperatura del conversor, tomando como tiempo inicial 0.
#!/bin/bash
tiempo=$1
nsum=0
while true
do
sum=$(echo $nsum*$tiempo|bc -l)
echo "$sum `./delta`"
nsum=$((nsum+1)) a
sleep $tiempo
done
Se toma como argumento el intervalo de tiempo entre ejecuciones; Asi la ejecucion del
script, se realiza de la siguiente forma (El nombre del script es delta_dtb):
./delta_dtb 5

Asi el programa que obtiene el PV del conversor se ejecuta una ves cada 5 seg. A continuacin se
muestra un ejemplo de la salida del script:
$./delta_dtb
0 50.0
5 50.1
10 50.2
15 51.3
.
.
.

Ademas, con la siguiente opcion:


./delta_dtb 5 >> datos.txt

Copiamos la salida del programa al archivo de texto datos.txt . Ahora este archivo lo podemos usar
para poder exportar los datos a una hoja de calculo, o gratificarlos directamente con algn programa
de grficos cientficos, como lo es Grace:
./cat datos.txt | xmgrace -pipe

Ver el manual de grace, para mas informacion delas variadas opciones que ofrece.
La grafica optenida se ve asi:

5 Conclusiones
Con este trabajo, hemos aprendido un poco acerca de cmo manejar los puerto seriales en Linux, no
incluimos ningn ejemplo especifico de este tema, pero con las funciones escritas, podramos realizar
drivers sencillos para el hardware que se conecte al puerto serie, por ejemplo un teclado.
En la parte de la biblioteca libmodbus, hemos comprobado la sencillez de esta biblioteca, una ves que
sea comprendido adecuadamente el protocolo de comunicacin ModBus, logrando hacer un pequeo
software para la adquisicin de datos de un dispositivo comercial, el cual contaba (comercialmente)
solo con software para windows; La sencillez de la programacin con libmodbus hace que este
software comercial sea fcilmente exportable a Linux.

6 Agradecimientos
Se agradece a Edgardo Sambrano del INTI, Argentina, y a Luis Guillermo Cota, profesor de la
facultad de ciencias, UNAM, Por todo el apoyo brindado a lo largo del tiempo que me mantuve
ocupado con la realizacin de este proyecto, y en primer lugar por haber impulsado a un servidor a
tomar la decisin de abordarlo.

You might also like