Professional Documents
Culture Documents
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
/dev/ttySn
/dev/ttyUSB0
convertidor USB-serie 1
/dev/ttyUSBn
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.
Hay veces en las cuales Linux no asigna una direccin a nuestro dispositivo serie, si ese es el caso,
ver este Tutorial.
#include
#include
#include
#include
<termios.h>
<stdio.h>
<stdlib.h>
<string.h>
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;
}
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.
*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
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
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.
0x01
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);
}
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.
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
.
.
.
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.