martes, 2 de febrero de 2010

Conectarse con el puerto Serie/paralelo desde Java

Bien pues para esta tarea existe una librería llamada Java Comunication API. Pero hay que tener en cuenta que para comunicarte a través de algo físico de la máquina Java nos proporciona una fachada de métodos que por debajo se conectan con diferentes drivers según si estamos corriendo el programa en un windows, macos o linux. Esto quiere decir que además de tener la máquina virtual de java instalada tendrás que instalar un driver en el sistema operativo. Yo empece a trabajar con la implementación de Sun pero me dió muchos problemas y no conseguía que me detectara los drivers instalados, así que finalmente opte por usar una implementación de Java Comunication API open source llamada RXTX. Con ella fue todo rodado por lo que os recomiendo que la useís.

Su página web es http://rxtx.qbang.org/

La instalación es muy simple os bajais la librería en la que os viene:

- RXTXcomm.jar (librería que implementa la especificación de Java Comunication API)
- Directorio con drivers para Linux
- Directorio con drivers para Solaris
- Directorio con drivers para Mac_OS_X
- Directorio con drivers para Windows


Añadis RXTXcomm.jar como librería a vuestro proyecto java y el driver lo meteis en la instalación del jre\bin

Ahora la implementación de una conexión al puerto serie.


public class EjemploRXTX {
    static CommPortIdentifier portId;
    static CommPortIdentifier saveportId;
    static Enumeration  portList;
    static InputStream inputStream;
    static OutputStream outputStream;
    static BufferedInputStream bufferedInputStream;
    static SerialPort  serialPort;
   
   
    public static void main(String[] args){
        boolean gotPort = false;
        String port;
        portList = CommPortIdentifier.getPortIdentifiers();
       
        while (portList.hasMoreElements()) {
            portId = (CommPortIdentifier) portList.nextElement(); //get next port to check
            if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
                if ( portId.getName().equals("COM4") ) {
                    port = portId.getName();
                    gotPort = true;
                }
               
                if (gotPort == true) {
                    try {
                        serialPort = (SerialPort)portId.open("SMSSender", 2000);
                    } catch (PortInUseException ex) {
                        ex.printStackTrace();
                    }
                    try {
                        outputStream = serialPort.getOutputStream();
                    } catch (IOException ex) {
                        ex.printStackTrace();
                    }
                    try {
                        serialPort.setSerialPortParams(19200,
                                SerialPort.DATABITS_8,
                                SerialPort.STOPBITS_2,
                                SerialPort.PARITY_NONE
                                );
                    } catch (UnsupportedCommOperationException ex) {
                        ex.printStackTrace();
                    }
                   
                    try {
                        inputStream = serialPort.getInputStream();
                        bufferedInputStream = new BufferedInputStream(inputStream);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                   
                    serialPort.notifyOnDataAvailable(true);
                }
            }
        }
       
       //Escribir en el puerto serie
       try {
            if (!(outputStream == null))
                outputStream.write("array de váis que queremos enviar");
           
            byte[] readBuffer = new byte[1];
            boolean read = false;
            while(!read) {
                try {
                    String getInfo = "";
                    Thread.sleep(100);
                    while (bufferedInputStream.available() > 0) {
                        int numBytes = bufferedInputStream.read(readBuffer);
                        getInfo += new String(readBuffer);
                        read = true;
                    }
                    feedback += getInfo;
                    int length = getInfo.length();
                } catch (Exception e){
                    e.printStackTrace();
                }
            }
        } catch (IOException e){
            e.printStackTrace();
        }
    }



En este ejemplo se usa por debajo una plataforma windows como podéis ver en el nombre del puerto "COM4" en un linux por ejemplo este sería algo como "/dev/ttyS0".

Tambíen para cada caso teneis que tener en cuenta que los datos de conexión con el dispositivo son particulares a cada dispositivo:


serialPort.setSerialPortParams(19200,
                                SerialPort.DATABITS_8,
                                SerialPort.STOPBITS_2,
                                SerialPort.PARITY_NONE
                                );


El dispositivo de este ejemplo trabajaba a 19200 baudios, tamaño de mensaje 8 bits, sin paridad y con el bit de parada 2.

Si ajustais estas cosas ya os debe funcionar para cualquier dispositivo.

37 comentarios:

rodanmuro dijo...

Jorge. Me parece excelente tu aporte. Es claro y conciso. Te tengo una pregunta. ¿Qué pasa con los puertos COM virtuales creados por un software bluetooth como el BlueSoleil por ejemplo? He tratado de leer o escribir información en estos puertos virtuales y se me han presentado errores (la verdad lo he hecho con el API javax.comm, comenzaré a ensayar con el rxtx) ¿Crees que hay definir algunos otros parámetros?¿O crees que realemnte no se puede? De antemano muchas gracias por tu aporte y pronta respuesta. Hasta la próxima

Jorge Cantón Ferrero dijo...

Hola rodanmuro,
Yo he usado este código con un puerto virtual creado a traves de un puerto USB, así que no tienes que tener ningún problema. Hay un error muy típico cuando ejecutamos esto sobre linux que no se nos puede olvidar que es dar permisos de escritura al puerto (ej /dev/ttyS0) al usuario con el que queramos lanzar nuestra aplicación, no se cual es el SO sobre el que trabajas pero igual te ayude tener esto en cuenta. Por último yo tuve muchos problemas con la API de Java ya que parece que sólo va bien en solaris, por lo que te recomiendo te pases a RXTX.

rodanmuro dijo...

Graciasssss. Oye mantienes muy pendiente de tu blog muy bien. Ya logré trabajar con los puertos virtuales. El error que estaba cometiendo era de novato. Creaba el puerto virtual pero debía tener encendido el otro dispositivo (móvil o pc) para poder escribir en el mismo. Gracias de nuevo y estaré muy pendiente de tu blog. Hasta la próxima.

Javier Rodriguez dijo...

Saludos. Estoy haciendo una aplicación en java para leer los datos por puerto serie y pasarlo a hexadecimal, y resulta que no logro recibir valores mayores de 127 (0x7F), cuando pasa de 127 me lee 192 o 195, estoy usando el puerto serial giovynet. NO se que hacer para que lo que envie sea lo mismo que reciba. Saben como leer caracteres ascii mayores de 127?? Por favor necesito ayuda con esto es para un trabajo final y no me dejan utilizar labvioew que con ese programa si logro que me lea caracteres mayores de 127.
Primero trate de usar el rxtx pero no he logrado configurarlo con exito por eso uso el giovynet...

Jorge Cantón Ferrero dijo...

Hola Javier,

Pues si te soy sincero no he usado la librería giovynet, de todas formas no se porque quieres conseguir valores superiores a 127 cuando si miras la tabla ASCII http://www.ascii.cl/es/ no existe más de ese valor, es decir el 128 parece no tener correspondencia ASCII.

Fernando dijo...

Hola Jorge, he estado viendo su articulo sobre "Conectarse con el puerto Serie/paralelo desde Java", y quisiera usarlo para leer los datos existentes en el puerto cuando entra una llamda o sea crear un caller ID (me ha asegurado de tener el servicio activo con mi compañia de telefonos), como puedo implementar la rutina de lectura del puerto?.

Muchas gracias.

Jorge Cantón Ferrero dijo...

Hola Fernando, me pillas de casualidad porque mañana me voy de vacaciones hasta el 16 de Agosto. Comentarte que no me queda muy clara tu pregunta, deduzco que estas programando un modem GPRS con java tipo Siemens/Matrix MTX65 y quieres hacer un programa que cuando se produzca una llamada telefónica a dicho modem este ejecute una acción. Si fuera esto, tendrías que usar la API del propio modem y comandos AT para ponerte a la escucha de dicho evento. La API de java sólo sirve para transmitir datos a través del puerto paralelo.

Fernando dijo...

Hola Jorge, el modem del que quiero tomar los datos es un modem convencional conectado a mi pc a este modem tengo conectada la linea telefonica. La idea es usar ese modem para hacer un identificador de llamadas y poder capturar por el puerto COMx donde esta el modem el numero desde el cual estan llamando.

Gracias por su gentil respuesta.

Jorge Cantón Ferrero dijo...

Hola Fernando, lo que quieres hacer lo tendrás que hacer con comandos AT que es el lenguaje estandar que usan los modems. Para hacer pruebas si usas windows puedes usar el Hyperterminal y conectarte con el modem, cuando recibes una llamada y estas conectado verás en el hyperterminal la palabra "RING". Esto te puede servir para gestionar las llamadas. Mi consejo coge un manual de comandos AT y el hyperterminal de windows para aprender que comandos AT tendrás que transmitir a través del programa que más tarde codificarás. Quizás este enlace te sirva de punto de partida http://www.modemsite.com/56k/usehyper.asp

Sirius_5 dijo...

Hola Jorge,
Mi nombre es Cristóbal y lo que necesito hacer es hacer la gestion de un monedero electrónico, quiero hacer un programa en JAVA que recoga los impulsos enviados por el monedero al puerto, pero no se como hacerlo.
¿Puedes ayudarme?
Muchas gracias de antemano.
Saludos

Jorge Cantón Ferrero dijo...

Hola Cristóbal,

Comentarte que el puerto paralelo no es más que un medio a través del que comunicarte con dispositivos como puede ser el bluetooth. Por lo que en este artículo hablo de como enviar información a través de este medio de comunicación, ahora el que enviar es algo que depende del dispositivo con el que queramos comunicarnos. En tu caso, debes encontrar cual es el protocolo de comunicación para comunicarte con ese monedero electrónico.

¿Si dices el modelo exacto igual yo o alguién pueda decirte donde encontrar ese protocolo, que suele ser en la web oficial del dispositivo?

Un saludo

Sirius_5 dijo...

Hola Jorge,
Cuando te hice la pregunta me equivoqué.
El monedero es un monedero mecánico que cuando pasa la moneda, envía un impulso electrico.
¿Como tengo que hacer para conectar ese monedero al ordenador?, a traves del puerto paralelo?
A través de un programa, como puedo saber que se ha introducido una moneda en el monedero, cuando el monedero envia un impulso, que es lo que le llega al ordenador para recogerlo en un programa.
Espero puedas ayudarme, porque estoy hecho un lio.
Muchas gracias de antemano.
Saludos.

Jorge Cantón Ferrero dijo...

Hola Sirius_5,

La verdad es que tu proyecto es ambicioso. No sé muy bien el tipo de conexión que te dará este dispositivo para poder manejarlo desde el PC o si no te da ningún protocolo. Lo primero que debes preguntarte ¿Tiene algún cable que pueda conectarse al PC? Respuestas posibles: Puerto Paralelo, USB.
La segunda pregunta si dispone de una salida para conexión con el PC dispondrá de unos drivers para poder construir una aplicación que controle dicho dispositivo. Si fuera así pero no dispones de drivers quizás llendote a la web del fabricante de dicha máquina puedas conseguir dichos drivers.

Responde estas preguntas y así podremos continuar...

(Siento el retraso al responder pero he pasado unos días de vacaciones estas navidades)
Un saludo

siulpolb dijo...

Hola
antes que nada excelente aporte

y ahora tengo una pequeña duda

en esta parte
serialPort = (SerialPort)portId.open("SMSSender", 2000);

que significa el 2000???

o que parametro es ese que recibe la funcion???

si pudieras resolver mi duda te estare muy agradecido

de antemano muchas gracias

Jorge Cantón Ferrero dijo...

Hola siulpolb,

Hace tiempo que no toco esta parte pero creo recordar que ese parámetro significaba el "timeout" es decir el tiempo en milisegundo que la aplicación va a estar intentando abrir el puerto. Si este tiempo es superado se devuelve una timeout diciendo que ha sido imposible abrir dicho puerto.
Por lo tanto 2000 es un valor estándar suficiente de lo que tiene que esperar una aplicación.

henpar dijo...

Hola.

He intentado enviar comandos At como mensaje:
static String mensaje = "ATD3137328070";

y el programa se queda bloquedado y no ejecuta la orden.

que sucedera???

Jorge Cantón Ferrero dijo...

Siento haber tardado tanto en responder henpar. Sigues con el problema que me comentabas o ya lo has resuelto?¿

Andrea Canino dijo...

Hola Jorge, mi nombre es Andrea, estoy programando una conexion para puerto Serial y se me vienen muchas dudas, por ejemplo:

1.- Estoy empleando la libreria javax.comm y tambien la SerialPort pero no se si trabajar con ambas me genere algun problema.

2.- Tengo conectador 2 dispositivos en 2 COM diferentes: un Scanner y una impresora fiscal. Para leer los datos del scanner no tengo problema alguno pero para la escritura se me hace imposible, no se si tenga que ver algo respecto a los permisos del puerto o si es el driver de la impresora, allí si necesito ayuda.

3.- Existe la documentacion oficial para estas librerias?? porque no doy con ellas y me cuesta mucho saber que son los parametros que lleva cada metodo.

Gracias de antemano por la ayuda y por cierto excelente que estes al dia con los post de los usuarios...

Saludos! :)

Jorge Cantón Ferrero dijo...

Hola Andrea, pues te cuento hace tiempo que hice este proyecto por lo que no tengo muy fresco estos conocimientos en cualquier caso y siguiendo la pregunta que me haces yo te recomiendo que uses RXTX es una implemetación de Java Comunication API, la que tu usas es la oficial y esa también me dio muchos problemas a mi.

También preguntas por una documentación de dicha librería la puedes encontrar en este wiki http://rxtx.qbang.org/wiki/index.php/Main_Page

Espero que esta información te sea de utilidad y gracias por comentar el artículo.

Un saludo :)

luis dijo...

Bueno el aporte es muy bueno pero necesito saber dos cosas lo que pasa es q quiero hacer que mi aplicativo me reciba y texto que me genera una pila, se que este genera como 15 lineas entre numeros y letras, quisiera saber como puedo hacer esto. soy muy novato pero quiero aprender

Jorge Cantón Ferrero dijo...

Hola Luis,

No entiendo muy bien la pregunta, ¿podrías pegar en un post esas 15 líneas de las que hablas, para ver si me puedo hacer una idea de lo que estas intentado hacer?

Andrea Canino dijo...

Hola Jorge, te cuento que logre hacer todo con la libreria SerialPort de Java, estoy trabajando con una impresora y puedo manejar sin problemas el envio de instrucciones y las respuestas generadas por la impresora. Muchas gracias por la ayuda y cualquier cosa estoy a la disposicion...

Saludos ;)

Jorge Cantón Ferrero dijo...

Estupendo Andrea, me alegro :).

puul124 dijo...

Jorge, una pregunta hacerca del codico en la parte de feedback, me puedes explicar que funciones realiza el codigo.
Muchas gracias....

cristian rodriguez dijo...

Hola Jorge que pena molestarte con este tema lo que sucede es que estoy realizando una aplicación java conectando una bascula por puerto serial y necesito que me muestre los pesos que me este dando la bascula el problema es que la bascula nunca me deja de enviar datos y no se como capturar tantos datos a la vez .... yo tengo una clase que realize que me muestra el dato enviado atraves de un puerto virtual ... por favor si me puedes colaborar estaria muy agradecido... gracias

Jorge Cantón Ferrero dijo...

Cristian ¿cuál es exactamente la información que lees de la báscula? ¿Qué estructura de datos?

luisao dijo...

Hola Jorge, te felicito por tus aportes, mira empiezo a desarrollar un pequeño sistema con una balanza Marca Precisa Modelo BJ-1000 , baje la rxtx segui todos los pasos dejando las dll en su lugar y adjuntando libreria RXTXcomm.jar al proyecto que hago con netbeans y windows 7, el problema es que me tira un error al hacer el import javax.comm.SerialPort;
no entiendo que puede ser, te agradeceria una mano.
Saludos
Luis

Oriettas Diseños dijo...

Hola Jorge me he encontrado con este foro y me parece de gran ayuda, yo necesito enviar al puerto serial un arreglo de bytes pues el dispositivo los lee unicamente asi son numeros por lo menos en C# yo los enviaba como new bytearray(137, 134, 156) lo que tengo que enviar son esos numeros pero no se si en el mensaje que dijiste se colocaba en el codigo ya eso hace el envio como arreglo de bytes o debo hacer el cambio previamente ya que el dispositivo no los lee ni como caracteres ni numeros solo asi como arrreglos de bytes.

Saludos Isabel.
Gracias.

Maka7 dijo...

Hola, muy interesante su blog! Le hago una pregunta, estoy implementando una aplicación en Java para comunicarme con un controlador PID, el problema que tengo es que cuando envío las órdenes al controlador, una vez responde correctamente y otras responde después de intentarlo varias veces, tiene idea de qué puedo estar haciendo mal. Gracias. Un saludo.

eloy dijo...

Hola buenos días.
Quiero realizar un programa en java que controle varios dispositivos externos (unas máquinas) mediante bluetooth. He encontrado una librería que es javax.bluetooth y javax.obex (jsr82.jar), pero creo que necesitan extender la clase MIDlet que está definida en J2ME (micoredition), pero yo quiero utilizarlo con el ordenador (o sea, una máquina que "lleve" el runtime de J2SE) y no con un dispositivo móvil o de bajas prestaciones.
También me he descargado la librería RXTXCOMM, pero no me queda muy claro como buscar dispositivos, o servicios...

Gracias de antemano.

Unknown dijo...

Hola, jorge, como envio el siguiente mensaje >> 0106 0007 0080 39AB, es para activar salidas de un PLC, utiliza el protocolo MODBUS RTU..

Julio Andrés Barrera C. dijo...

Hola excelente post, tengo una duda con esta API (RXTX) puedo enviar y recibir datos a través de un puerto USB?

Jorge Cantón Ferrero dijo...

Hola julio,

RXTX es para acceder al puerto serie/paralelo para trabajar con el puerto USB te recomiendo otras APIs Java como:

http://libusbjava.sourceforge.net/wp/
http://www.steelbrothers.ch/jusb/
ó
http://www.jcp.org/en/jsr/detail?id=80

Espero que te ayude
Saludos!

Jose Mario Barrios Flores dijo...

Hola, he tenido problemas probando tu codigo en estas lineas, y en unos catch, espero y me puedas decir que podria estar pasando.

outputStream.write("array de váis que queremos enviar");

feedback += getInfo;

Jorge Enrique Ferrel Rivera dijo...

Yo tuve el mismo problema del
outputStream.write("array de váis que queremos enviar");

y

feedback += getInfo;

Jorge me puedes dar una mano porfa?

Dalia Sánchez dijo...

Excelente información, fue de gran ayuda muchas gracias :)

Richard Portillo dijo...

Buenas amigo. . Por ahí no tienen ejemplo de cómo manejarse con las balanzas. . La balanza genera un código de barras. . Luego el sistema debería de leer y obtende el producto y su peso por ejemplo. habría que conectar por cable o como. . Desde ya gracias y saludos