Acceder al puerto serie programando en Java

Pocos programadores habrá que no hayan utilizado el lenguaje de programación Java. Todos conocemos su historia, pero lo más importante para el tema que nos ocupa es tener en cuenta que Java es un lenguaje de alto nivel, y una de sus características más importantes es ser multiplataforma. Como veremos, en determinadas circunstancias, como cuando nos toca realizar tareas, como acceder a un puerto RS-232, esto puede convertirse en una gran desventaja.

Personalmente, me gusta mucho este lenguaje. Es orientado a objetos, la sintaxis toma lo mejor de C y C++, con un modelo de objetos más simple, y eliminando las herramientas de bajo nivel. Cuenta con varios ide’s buenos, aunque supongo que las estrellas son Netbeans y Eclipse, que cuentan a su favor con ser Open Source, y tener una gran comunidad de usuarios y desarrolladores. Parece que Oracle va a seguir respaldando a Netbeans, y que IBM hará lo propio con Eclipse, así que parece que tendremos entornos de desarrollo open source para Java para rato.

El escenario que planteo es el siguiente: imaginemos que hace muchos años nuestra empresa adopta este lenguaje de desarrollo porque «es lo que se lleva ahora», pero sin analizar realmente en que consiste, que puede hacerse con el, y que es mejor hacer con otro lenguaje. Lo de poder ejecutar una aplicación tanto en Windows como en Linux (¡incluso Mac!), escribiéndola solo una vez suena muy productivo. Que sea un lenguaje interpretado y no compilado, y que eso penalice su rendimiento no parece muy determinante, ya que el hardware promete ser cada día más barato y potente. Lo de la orientación a objetos, eso de crear entidades genéricas que permitirían reutilizar el software entre proyectos, suena especialmente prometedor.

Vamos, que la misma clase que utilizaré para imprimir informes en una impresora laser de oficina, sin necesidad de tocarla me servirá para imprimer etiquetas en el Dymo que tiene la chica de recepción. Pronto el sueño de programar arrastrando objetos desde una barra de herramientas, y hacer que esos objetos hagan cosas pintando flechas entre ellos será realidad. Adios a escribir código fuente, compilar, y comentarios, y documentar. Todo será mucho más visual, y más reutilizable, la productividad del programador medio subirá, los programas serán mejores y con menos fallos, y…

La realidad del programador diez años después de este sueño es bastante diferente. Si bien es cierto que se puede construir una interfaz gráfica de una forma más o menos visual, aun es necesario escribir el código que se ejecutará cuando pulsamos en un botón. Y la tan deseada reutilización de los objetos programados en un lenguaje OO, puede ser más o menos, dependiendo paradójicamente, no del programador, sino de los analistas, que tienen que conocer la orientación a objetos, y como aplicarla a la resolución de problemas informáticos en el mundo real.

Vamos, que la idea de que la misma clase que utilizo para imprimir en mi impresora laser, me sirva para imprimir en el Dymo de la chica del dpto de nóminas, sigue siendo un sueño hoy en día.

Java y los puertos serie.

Supongamos que recibimos el encargo de construir una aplicación, que sea multiplataforma ya que vamos aprovecharnos de la gratuidad del sistema operativo Linux para ahorrarnos un buen dinero en licencias. Además, todos sabemos que dicho sistema operativo es muy robusto, no sufre el acoso de los virus (parcialmente cierto), y no tiene tendencia a degradarse con el tiempo como una instalación de Windows. Se trata de hacer una aplicación tipo kiosko, en la que el usuario no puede realizar ninguna tarea más que encender su terminal, esperar a que se cargue el sistema operativo y la aplicación de trabajo, y producir. Para esto nos sobra una distribución cualquiera de Linux.

Todo suena estupendamente, hasta que nos enteramos de más detalles. La aplicación tiene que recibir datos por el puerto serie del sistema, de alguna clase de periférico que todavía utiliza esta interfaz. Con los datos recibidos por la interfaz serie tiene que realizar algún tipo de operación, e incluso tiene que enviar instrucciones y comandos al periférico.

En este momento al programador se le ocurren varias objeciones, tales como que un lenguaje de alto nivel como Java, no está pensado para realizar tareas a bajo nivel, como acceder al puerto serie. Tanto es así, que la API original de Sun (ahora Oracle), solo menciona de pasada el acceso al puerto serie, y a la hora de la verdad, tendremos que recurrir a librerías de terceros. Pero vayamos por partes.

Empezamos.

El desdichado programador que no sabe lo que hacer, suele recurrir a San Google, y lo primero que se le ocurre buscar es algo como «serial port java«. La cosa no pinta mal, y el primer resultado parece muy prometedor: la Java Communications API, nada menos que la version 3.0, lo cual nos hace pensar que incluso tiene cierta trayectoria. En esa página se nos promete que con dicha api, podremos acceder al puerto RS-232, y realizar todo tipo de operaciones.

java communications api web page
La página de la Java Communications API

La realidad es bien distinta. La primera vez que vi está página fue en el año 2006. Era exactamente igual que hoy, salvo por el logo de Oracle, que en aquel entonces era el de Sun. Las cosas se empiezan a poner feas cuando nos damos cuenta de que no hay níngún enlace para descargarse nada. Generalmente las apis con funcionalidades extras tienen algún tipo de librerías que tenemos que sumar a nuestro proyecto. Leyendo más detenidamente, nos damos cuenta de que se habla de librerias nativas (dll’s, archivos .so, etc), pero, ¿donde están?

Si os tomais la molestia de indagar más, vereis que hay muchos programadores que se preguntan donde están los enlaces de descarga de la api. Algunos de ellos suponen que cuando Oracle adquirió Sun, y se fusionaron los contenidos de sus sitios web, se traspapeló el enlace de descarga.

Lo cierto es que ya en el año 2006 era imposible descargar dicha librería, y la sensación que tuve entonces, y sigo teniendo ahora, es que el tema no interesa demasiado en la propia Oracle. La cosa puede tener su lógica, ya que Java es un lenguaje de alto nivel, lo de acceder a recursos del sistema, en principio no es una necesidad imperiosa. Pero para nosotros las cosas se empiezan a poner más difíciles.

Tras buscar y rebuscar, acabamos encontrando una librería llamada RXTX, distribuida bajo licencia GNU LGPL. La librería prometía en aquel entonces acceso al puerto serie desde Java, para Windows, Linux, Mac, Solaris, e ¡incluso para versiones de 64 bits!

Un alma caritativa llamado Keane Jarvi, decidió poner a disposición del público esta libreria en el año 2006. Posteriormente ha habido algunas actualizaciones, si bien parece que desde el año 2009 el proyecto esta parado. Probablemente sea porque la librería funciona, y hace bien lo que se espera de ella, al menos en Windows 32 bits y Linux, que son los entornos que he probado. ¿Para que tocarla entonces?

De la página web podemos descargarnos la librería en su versión 2.1.7-r2, y una vez que la descomprimimos en nuestro ordenador, nos encontramos un archivo RXTXcomm.jar, y varias subcarpetas que contienen las librerias nativas para cada sistema operativo (a saber, Linux, Windows, Sparc y Mac). Ya tenemos algo con lo que empezar a trabajar.

Cogemos algo de ritmo: añadimos el fichero .jar a nuestro proyecto, tenemos a mano el fichero rxtxSerial.dll y el librxtxSerial.so, pues suponemos que algo tendremos que hacer con ellos, aunque todavía no nos hemos leido la documentación, y no lo tenemos muy claro.

Como usar la librería RXTX.

El fichero rxtxcomm.jar, debe estar en nuestro classpath, como cualquier librería de java que queramos utilizar. Los ficheros .dll y  .so, son las librerias nativas para acceder al puerto serie desde windows o linux. Como nuestra aplicación va a correr sobre los dos sistemas operativos, nos hacen falta las dos. Leyendo más detenidamente la documentación, nos enteramos de que

  • rxtxserial.dll debe estar \jre\bin y
  • librxtxSerial.so debe estar en /jre/lib/[machine type], por ejemplo, /jre/lib/i386
Esto significa que tendremos que copiar la dll a la carpeta lib de donde esté instalado el jdk de nuestro equipo de desarrollo. Por ejemplo, en el equipo que estoy utilizando ahora mismo, la ruta sería:
C:\Archivos de programa\Java\jdk1.6.0_22\jre\bin
La cosa se complica si tenemos varios jdk’s o jre’s instalados (algo habitual en la máquina de un programador). Por otro lado, es bastante latoso, sobre todo a la hora de desplegar el programa en decenas de ordenadores, estar pendiente de copiar una dll a un directorio del equipo en el que vamos a instalar el programa. Tenemos varias alternativas para automatizar este proceso:
1.- Desplegar automáticamente las librerías nativas a los directorios del sistema:
Lo único que hay que hacer es incluir el fichero .dll y el .so en el jar de nuestra aplicación, o en uno auxiliar. En el momento que la aplicación se inicie, podemos comprobar si está presente la librería nativa, y si no lo está, copiarla.
Para curarnos en salud, lo mejor es utilizar las propiedades del sistema, y así podremos saber a donde tenemos que copiar el fichero.

String ruta = System.getProperty(«java.home»);
String sistemaOperativo = System.getProperty(«os.name»);

La propiedad «java.home» nos devolverá la ruta de la máquina virtual que está utilizando el programa. No es dificil hacer una pequeña función que copie la librería nativa a dicha localización.

La segunda propiedad nos dirá que sistema operativo estamos utilizando. Es necesario saberlo, ya que la ruta a donde debemos copiar la librería difiere ligeramente dependiendo de si es Windows o Linux.

Este método es bastante engorroso y el menos recomendado. Tendremos que implementar un pequeño procedimiento que copie el fichero, averiguar a donde tenemos que copiarlo, y si estamos distribuyendo la aplicación con JWS, tendremos que firmar nuestros ficheros jar para que la aplicación pueda copiar las librerias nativas a directorios del sistema, por no hablar de que en Linux no se puede copiar alegremente a cualquier carpeta, y tampoco en las version más recientes de windows, como windows 7. Esto se soluciona ejecutando nuestro programa con permisos de administrador, pero sigue siendo engorroso.

2.- Indicar en la línea de comando donde están las librerías nativas:

Lo más cómodo es indicar a la máquina virtual donde están las librerías nativas. Al emplear la linea de comandos para ejecutar nuestra aplicación, podemos cambiar una propiedad del sistema (en este caso la ruta de las librerias nativas) utilizando el parametro -D. Por ejemplo

java -Djava.library.path=»/mi/ruta/a/las/librerias» ClaseInicial

(Nota: si la ruta a la librerías contiene espacios, hay que entrecomillarla)

La carpeta «librerías», contendrá las librerías nativas.

Es responsabilidad del programador cargar la librería nativa antes de usarla. Esto se hace con el método System.loadLibrary. En este caso en nuestro programa tendríamos que escribir:

System.loadlibrary(«rxtxSerial»);

No es necesario indicar la extensión del archivo, ni el prefijo lib en el caso de las librerias nativas de Linux.

3.- Desplegar librerías nativas utilizando Java Web Start.

En el caso de que nuestra aplicación se distribuya utilizando Java Web Start, deberemos indicar en el archivo jnlp que contiene la información sobre las diferentes librerías que componen nuestra aplicación, cuales son librerías nativas, de la siguiente manera:

  <resources os="Windows"> 
        <nativelib href="lib/win/rxtx.jar"/> 
    </resource>

  <resources os="Linux"> 
        <nativelib href="lib/linux/rxtx.jar"/> 
    </resource>

Cada fichero jar contendrá la librería nativa que corresponda, en el directorio raiz. En el momento del despliegue el cliente descargará uno u otro fichero dependiendo  de que sistema operativo se trate.

Supongo que se pueden distribuir ambas librerías nativas en un solo jar, y no indicar para que sistema operativo es el jar con la etiqueta resources, sino simplemente poner en nuestro jnlp (no lo he probado):

        <nativelib href="lib/rxtx.jar"/> 

Dependiendo del tamaño de las librerías nativas puede darnos igual que se desplieguen cuando no van a ser usadas, pero personalmente, por una cuestión de orden, prefiero desplegar el fichero adecuado a la plataforma y nada más.

A pesar de ser multiplataforma, tenemos que pensar en la plataforma.

Un momento. ¿Por que tengo que discernir en mi código fuente si estoy trabajando sobre una máquina Linux o Windows? ¿No se supone que Java es un lenguaje multiplataforma, y que puedo programar en cualquier en una y ejecutar mi aplicación en cualquier otra? ¿Y si quiero que la aplicación funcione en un Mac? ¿Tendré que tener en cuenta este caso también?

Lo de que Java es multiplataforma, es cierto, pero también es cierto que siempre hay que hacer pequeños ajustes. Por ejemplo, es casi imposible que diseñando nuestra aplicación en un sistema Linux, la interfaz gráfica se vea igual de bien en un sistema Windows. Siempre habrá algun detallito que tendremos que retocar.

Con el tema del puerto serie, ¿podriamos decir que estamos lo estamos llevando un paso más allá de para lo que fue ideado? Si, se puede utilizar el puerto serie, pero para ello tenemos que recurrir a librerías de terceros, e ingeniárnoslas para desplegarlas y que funcionen, respetando la idiosincrasia de cada sistema operativo.

Más peculiaridades dependiendo de la plataforma.

Otro detalle a tener en cuenta, es la manera en que se nombran los puertos serie. En windows, todos estamos acostumbrados a la nomenclatura com1, com2, etc. Sin embargo en Linux se nombran asi: /dev/ttyS0, /dev/ttyS1, etc.

Hay que tenerlo en cuenta a la hora de acceder al puerto para abrirlo y trabajar con el. Pero eso lo trataremos en un próximo artículo, donde veremos un poco más detalladamente como abrir un puerto serie, recibir y enviar datos, y finalmente cerrarlo.

13 comentarios en “Acceder al puerto serie programando en Java”

  1. Bueno, acabo de empezar, y tengo poco tiempo. A ver si soy capaz de ir a artículo cada 15 días. Es que los artículos técnicos lleva su tiempo el prepararlo.

    Saludos!

    • ¡Pues parece que estás en lo cierto! Te aseguro, que en su día, el enlace estaba roto, no habia manera de contactar con nadie que lo arreglase, y me volví loco intentando encontrar la librería 🙂

  2. Estoy comenzando en esto de java pero esta muy interesante saque unos apuntes aca para aprender un poquito, gracias

  3. Woow, me encanto habia estado trabajando con la libreria giovynet con buenos resultados en win, pero ahora en linux no me funciona, parece que esta es una buena alternativa, si tienes mas informacion me seria muy util que me la mandes :). Trabajo con ubuntu 12.04 para 64

    • Bueno, con lo que hay publicado, debería llegarte para empezar. El tema del sistema operativo de 64 bits es lo unico que te puede enredar un poco, y como contesté a otra persona mas arriba, en mi opinión no merece la pena usar el JRE de 64 bits, con el de 32 bits llega y puedes usar las librerias de serie de 32 bits.
      Ya nos contarás que tal te va con el proyecto.

  4. Vaya tio, esto es lo que estaba buscando tienes todo documentado y para variar tienes un poco del tema cómico que atrae al lector.

  5. Gracias.
    Gente como tu ayudais a reciclarnos a los programadores que venimos de otros entornos no web y que nos vemos en la necesidad de actualizarnos para encontrar curro.

  6. Hola,

    Primero felicitarte por el post, ya que está muy bien escrito y explicado. Por otro lado, aunque desde el año pasado que no publicas nada, te lanzo (a ti y a quien pueda contestarme) mi pregunta. Yo estoy utilizando la librería rxtx y estoy bastante contenta, excepto por la velocidad de lectura. Según he visto, sólo puedo leer byte por byte, en vez de leer un mensaje completo.

    Si estoy equivocada y sí que se puede, me puedes dar alguna pista de cómo hacerlo? Si no, hay alguna librería alternativa? En mi caso estoy desarrollando para Windows.

    Muchas gracias de antemano y ánimo para continuar el blog! 😀

    • Si que lo tengo muy abandonado, ya que ahora profesionalmente me dedico a otras cosas. Hace mas de dos años que no me dedico a trabajar con dispositivos por el puerto serie desde Java, pero te comento lo recuerdo.

      Que el puerto serie vaya lento o no no creo que dependa de como leas la informacion que te entre. Es decir, que la leas byte a byte, o con algun buffer acelerador de por medio. De hecho, en los programas que hacia antaño para trabajr con perifericos por el puerto serie, a veces era mejor leer lentamente la informacion que entraba por el serie que rápido, ya que extrañas cosas pasaban si se leia demasiado rapido.

      El puerto serie no es un puerto conocido por su velocidad, por lo tanto si te funciona lento, quizas es que lo estes utilizando para tareas para las que no esta pensado o no es idoneo.

      Respecto a librerias, creo que la rxtx cayo en desuso en favor de una llamada JSSC (https://code.google.com/p/java-simple-serial-connector/), aunque no creo que el usar una u otra libreria cambie la velocidad a la que puedes leer los datos.

      Un saludo

Los comentarios están cerrados.