Acceder al puerto serie programando en Java, 2ª parte

En el anterior artículo vimos que opciones tenemos para acceder al puerto serie desde nuestros programas desarrollados en Java. Dichas opciones, se reducían prácticamente a una opción: utilizar la librería rxtx, galántemente puesta a disposición del público por su creador de manera gratuita.

En la segunda parte de este artículo, explicaremos como configurar los entornos de desarrollo Eclipse o Netbeans para hacer nuestras pruebas, y veremos de forma resumida como se trabaja con un puerto serie.

Paso 1: Configurar la librería RXTX en el entorno de desarrollo.

En Netbeans

En las propiedades del proyecto, añadimos la librería:

Añadimos RXTXcomm2.1-7.jar al proyecto
Añadimos RXTXcomm2.1-7.jar al proyecto

A continuación indicamos donde está la librería nativa. Si la ruta contiene espacios es necesario ponerla entre comillas:

Ruta a la librería nativa rxtx
Ruta a la librería nativa rxtx

En Eclipse

En eclipse podemos hacer los dos pasos en la misma pantalla:

Librería RXTX y librería nativa en Eclipse
Librería RXTX y librería nativa en Eclipse

Paso 2: cargar la librería nativa

Lo primero que hay que hacer es asegurarnos que podemos acceder a la librería nativa. En el caso de que no se cargue correctamente, podemos analizar la excepción producida para diagnosticar el problema. Lo más normal, es que la libreria no haya sido copiada al directorio adecuado, o no le estemos indicando correctamente el path con el parametro «-Djava.library.path».

[sourcecode language=»java»]

try{

System.loadLibrary(«rxtxSerial»);
System.out.println(«Se ha cargado la librería nativa correctamente»);

} catch (UnsatisfiedLinkError u) {
System.err.println(«No se ha encontrado la librería nativa de puerto serie»);
}[/sourcecode]

No es necesario indicar la extensión del archivo (.dll en sistemas windows, .so en linux), ni el prefijo «lib» en el caso de los sistemas Linux. Simplemente especificar el nombre de la librería «rxtxSerial».

Si no se encuentra la librería, se producirá una excepción, que tendremos que tratar adecuadamente.

Paso 3: enumerar los puertos disponibles en el sistema.

Con el siguiente código, enumeramos los puertos serie y paralelo que hay disponibles en el sistema. Si queremos centrarnos solo en los puertos serie, tenemos que comprobar el tipo de cada puerto comparandolo con la constante CommPortIdentifier.PORT_SERIAL.

[sourcecode]
Enumeration listaPuertos = CommPortIdentifier.getPortIdentifiers();
CommPortIdentifier idPuerto = null;
boolean encontrado = false;
while (listaPuertos.hasMoreElements() && !encontrado) {
idPuerto = (CommPortIdentifier) listaPuertos.nextElement();
if (idPuerto.getPortType() == CommPortIdentifier.PORT_SERIAL) {
if (idPuerto.getName().equals(«COM1»)) {
encontrado = true;
}
}
}
[/sourcecode]

Recorremos la lista de puertos disponibles hasta encontrar el que buscamos. En este caso, el puerto COM1, pero podría ser otro, y tener una nomenclatura diferente, por ejemplo, el equivalente al com1 en linux es /dev/ttyS0.

En sistemas windows me ha ocurrido que si un puerto serie está en uso por alguna aplicación, el método .getPortIdentifiers() no lo incluye en la lista. Sin embargo en Linux si lo incluye (mas adelante cuando intentas abrirlo, falla, obviamente).

Otro detalle digno de mención es que no podemos abrir dos veces el mismo puerto, al mismo tiempo, desde dos aplicaciones diferentes. Esto sería una obviedad, si no fuera por que me ha pasado en ordenadores Linux, que si se puede abrir dos instancias de la misma aplicación, que abran el mismo puerto, y funcionan correctemnte las dos aplicaciones.
Os estareis preguntando como distingue el sistema operativo a que aplicación enviar los datos recibidos por el puerto serie, o si las envía a las dos. Lo que he observado es que recibe los eventos del puerto serie la aplicación que tiene el foco en ese momento. Esto me ha ocurrido en un Ubuntu versión 9. Desconozco si en otras versiones de Linux también se da este extraño comportamiento.

Paso 4: abrir el puerto.

Supongamos que en el bucle anterior hemos decidido que nos vamos a conectar con el puerto COM1. De la lista de puertos, tendremos que coger el objeto idPuerto cuyo método getName() nos devuelva «COM1».

Una vez tenemos seleccionado este objeto, abrimos el puerto:

[sourcecode]
SerialPort puertoSerie =null;

try {
puertoSerie = (SerialPort)idPuerto.open( «DescripcionPropietario»,2000 );
} catch( PortInUseException e ) {
System.out.println(«Error abriendo el puerto serie»);
}

[/sourcecode]

Al método idPuerto.open, hay que pasarle dos parámetros. Un cadena que describe al propietario del puerto (puede ser el nombre de nuestra aplicación), y el tiempo en milisegundos que se esperará por un puerto bloqueado antes de lanzar la excepción de puerto en uso (en este caso, dos segundos).

Si lo anterior se ejecuta correctamente, ya tenemos abierto el puerto serie. Es hora de configurar el puerto serie con los parámetros de comunicación adecuados al periférico que tengamos conectado.

[sourcecode]

try {
puertoSerie.setSerialPortParams(9600,
SerialPort.DATABITS_8,
SerialPort.STOPBITS_1,
SerialPort.PARITY_EVEN );
puertoSerie.notifyOnDataAvailable(true);
} catch( UnsupportedCommOperationException e ) {
System.out.println(«Error configurando parámetros de configuración»);
}

[/sourcecode]

En el ejemplo se ha configurado la comunicación a 9600 baudios, 8 bits de datos, un bit de parada y paridad impar. La configuración variará dependiendo del tipo de dispositivo que conectemos al puerto serie. En general, la mayoría de dispositivos susceptibles de conectarse a un puerto serie pueden configurarse, lo cual aumenta el lío, ya que tenemos que asegurarnos que tenemos el dispositivo configurado con los mismos parámetros que después usamos en nuestro programa.

Finalmente, solo nos queda recoger la información que hay en el puerto, y procesarla. Esto dependerá de la finalidad de cada programa en concreto, pero a grandes rasgos puede hacerse así:

[sourcecode]
InputStream entrada = puertoSerie.getInputStream();

String s=»»;
while (entrada.available() > 0) {

int dato = entrada.read();
s=s+(char)dato;

}
[/sourcecode]

Dependiendo del sistema operativo, y de las características del periférico que nos esté enviando información por el puerto, tendremos que usar pequeños trucos para que la cosa funcione bien. Por ejemplo, a veces es conveniente entre cada lectura del puerto, es decir, entre cada iteración del bucle while(entrada.available()>0), introducir un pequeño retardo, de algunos milisegundos. Se me ha presentado el caso, en el que el dispositivo que enviaba la información por el puerto serie no era capaz de enviarla tan rápido como la procesaba el pc que la recibía, y el método entrada.available nos indicaba que ya no quedaban datos en el canal de entrad (lo cual era cierto, estaban por llegar).

En ciertos casos tendremos que leer todos los datos del canal de entrada hasta el final. En otros casos tendremos que parsear la cadena que vamos recibiendo hasta que encontremos una bandera que nos indique el final de un telegrama, típicamente un salto de carro o de línea, u otro carácter cualquiera.

También podemos utilizar una clase que implemente la interfaz SerialPortEventListener para estar pendientes de lo que ocurre en el puerto serie:

[sourcecode]

class SerialListener implements SerialPortEventListener{

@Override
public void serialEvent(SerialPortEvent arg0) {

if (arg0.getEventType() == SerialPortEvent.DATA_AVAILABLE) {

//Tenemos nuevos datos disponibles en el puerto, podemos poner aquí el código que recibe los datos y los procesa.
(…)
}

}

[/sourcecode]

El listener  se añade al puerto serie tal que así:

[sourcecode]

SerialListener miListener=new SerialListener();

puertoSerie.addEventListener(miListener);

[/sourcecode]

No podemos olvidarnos de cerrar todos los canales y el puerto una vez finalizada nuestra tarea:

[sourcecode]
puertoSerie.removeEventListener();//En el caso de que lo tenga.
puertoSerie.close();

[/sourcecode]

Herramientas útiles.

Una herramienta muy útil para desarrollar aplicaciones relacionadas con el puerto serie es  PortMon para Windows. Con esta herramienta podeis monitorizar la información que fluye por un puerto serie de vuestro sistema, capturarlo a un archivo, e incluso crear puertos serie virtuales que lean de un archivo y finjan recibir la información en el contenida.

De esta manera podemos programar aplicaciones para puerto serie sin necesidad de tener físicamente el dispositivo que envía la información al puerto serie (habiendo capturado primero alguna sesión de envío de datos de dicho dispositivo). Incluso podemos crear puertos serie virtuales para probar nuestros programas en los portatiles modernos, que ya no suelen traer este puerto.

Desconozco si hay alguna otra librería de terceros con la que se pueda manejar el puerto serie. Si algun lector conoce alguna alternativa a la librería rxtx, estaría encantado de conocerla.

32 comentarios en “Acceder al puerto serie programando en Java, 2ª parte”

  1. Hola, muy bien explicado.
    Serias tan amable de enviarme el codigo de ejemplo o decirme de donde lo puedo descargar?
    Saludos

    • Hola Raul,

      realmente, el código de ejemplo que solicitas, depende bastante de lo que quieras que haga la aplicación final. No es lo mismos leer de un puerto serie un chorro de información contínua, que responder solo a eventos, etc, por lo tanto, cualquier codigo que pudiera enviarte, no te serviría de mucho.

      Creo que en los dos artículos hay bastante información para poder empezar a desarrollar una pequeña aplicacion que lea del puerto serie. Realmente, esta parte no suele ser lo mas dificil. Lo dificil viene cuando tenemos que tratar la información que leemos del puerto.

      Si me explicas algo más de lo que intentas hacer, puedo intentar ayudarte.

      Un saludo

  2. Hola.
    Super útil la información. A mí en especial me viene de perlas pues justo estoy empezando a desarrollar la parte software de mi tesis y requiero una comunicación serial para transmitir datos entre mi PC y un microcontrolador.

    Q buen aporte y me uno al comentario del otro artículo: Vaya nivel!
    Gracias!!!!

    • Hola Carlos,

      En la wiki del proyecto Rxtx, cuentan que hay un par de páginas de colaboradores altruistas que han compilado las librerias nativas para sistemas de 64 bits, por ejemplo esta: http://www.cloudhopper.com/opensource/rxtx/

      Ahora bien, yo me encontré con la necesidad de utilizar las aplicaciones en sistemas de 64 bits hace un tiempo, en concreto, cuando se empezaron a popularizar los pc’s de escritorio con windows de 64 bits. Despues de algunas pruebas, llegué a la conclusión de que no merecía la pena enredarme con las librerías de 64 bits. Mi solución fue más sencilla: si en un sistema operativo de 64 bits, instalas el JRE de 32 bits, ya puedes utilizar tranquilamente las librerias rxtx compiladas para sistemas de 32 bits.

      Por consiguiente, si tu software no está sacando ventaja de las funcionalidades del sistema de 64 bits en cuanto a manejo de memoria o procesamiento de cpu no merece la pena. Asi a voz de pronto, si no haces cálculos muy complicados que involucren manejo de datos de 64 bits, o manejo de más de 4Gb de memoria (para la misma aplicación), no deberías sacar mucho beneficio de un JRE de 64 bits (quizás hasta al contrario).

      En cualquier caso, si llegas a probarlo, espero que nos lo cuentes 😉

      Un saludo

  3. Hola, la verdad es un gran aporte de tu parte, pero tenia dudas con lo del listener, ya que no lo pude implementar como yo quería, agradecería si pudieras ayudarme, mi correo es cesarcuellar1989@gmail.com
    de ante mano muchas gracias.

    • Hola Cesar,

      si me cuentas que problema tienes con el listener mas detalladamente, o que quieres hacer exactamente, podemos estudiar la mejor manera de hacerlo. Pero necesito mas detalles, obviamente 😉

      Un saludo

  4. package pruebajframe;

    import gnu.io.CommPortIdentifier;
    import gnu.io.SerialPort;
    import gnu.io.SerialPortEvent;
    import gnu.io.SerialPortEventListener;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.util.Enumeration;
    //import javax.swing.ImageIcon;
    import javax.swing.JOptionPane;

    public class NumeroSegmentos extends javax.swing.JFrame {

    private static final String TURN_Cero_ON=»0″;
    private static final String TURN_Uno_ON=»1″;
    private static final String TURN_Dos_ON=»2″;
    private static final String TURN_Tres_ON=»3″;
    private static final String TURN_Cuatro_ON=»4″;
    private static final String TURN_Cinco_ON=»5″;
    private static final String TURN_Seis_ON=»6″;
    private static final String TURN_Siete_ON=»7″;
    private static final String TURN_Ocho_ON=»8″;
    private static final String TURN_Nueve_ON=»9″;
    private static String recivido=»»;
    //Variables de conexi�n
    private OutputStream output=null;
    private InputStream entrada=null;
    SerialPort serialPort;
    private final String PUERTO=»COM3″;

    private static final int TIMEOUT=2000; //Milisegundos

    private static final int DATA_RATE=9600;

    public NumeroSegmentos() {
    inicializarConexion();
    initComponents();
    SerialListener miListener=new SerialListener();
    try{
    JOptionPane.showMessageDialog(null,»Se creo bien el listener»);
    serialPort.addEventListener(miListener);

    }catch(Exception e){
    mostrarError(e.getMessage());
    System.exit(ERROR);
    }

    }
    public void inicializarConexion(){

    CommPortIdentifier puertoID=null;
    Enumeration puertoEnum=CommPortIdentifier.getPortIdentifiers();

    while(puertoEnum.hasMoreElements()){
    CommPortIdentifier actualPuertoID=(CommPortIdentifier) puertoEnum.nextElement();
    if(PUERTO.equals(actualPuertoID.getName())){
    puertoID=actualPuertoID;
    break;
    }
    }

    if(puertoID==null){
    mostrarError(«No se puede conectar al puerto»);
    System.exit(ERROR);
    }

    try{
    serialPort = (SerialPort) puertoID.open(this.getClass().getName(), TIMEOUT);
    //Par�metros puerto serie

    serialPort.setSerialPortParams(DATA_RATE, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);

    output = serialPort.getOutputStream();
    } catch(Exception e){
    mostrarError(e.getMessage());
    System.exit(ERROR);
    }
    }

    private void enviarDatos(String datos){
    try{
    output.write(datos.getBytes());
    } catch(Exception e){
    mostrarError(«ERROR»);
    System.exit(ERROR);
    }
    }
    /*PROBAR ESTA FUNCION HABER SI FUNCIONA XD*/
    public String reciveDatos(){

    String Datos=»»;
    try{
    entrada = serialPort.getInputStream();
    while (entrada.available() > 0)
    {
    int dato = entrada.read();//LEE DATO POR DATO EN ASCII
    //JOptionPane.showMessageDialog(null,dato);
    Datos=Datos+(char)dato;//VA ALMACENANDO LA CADENA DE CARACTERES
    }

    } catch(Exception e){
    mostrarError(«ERROR»);
    System.exit(ERROR);
    }
    return Datos;
    }
    public String sacarDatos(String cadena){
    cadena=cadena.substring(cadena.indexOf(«:»)+1,cadena.length());
    return cadena;
    }

    public class SerialListener implements SerialPortEventListener{
    @Override
    public void serialEvent(SerialPortEvent e)
    {
    recivido=reciveDatos();
    JOptionPane.showMessageDialog(null,»El Listener Funciona»);
    /*//if (e.getEventType() == SerialPortEvent.DATA_AVAILABLE) {

    //Tenemos nuevos datos disponibles en el puerto, podemos poner aquí el código que recibe los datos y los procesa.
    //(…)
    JOptionPane.showMessageDialog(null,»Entro al IF»);
    recivido=reciveDatos();
    recivido=sacarDatos(reciveDatos());
    switch(recivido){
    case «48»:
    jLabel1.setIcon(new javax.swing.ImageIcon(getClass().getResource(«/pruebajframe/Cero.PNG»)));
    JOptionPane.showMessageDialog(null,recivido);
    break;
    case «49»:
    jLabel1.setIcon(new javax.swing.ImageIcon(getClass().getResource(«/pruebajframe/Uno.PNG»)));
    JOptionPane.showMessageDialog(null,recivido);
    break;
    case «50»:
    jLabel1.setIcon(new javax.swing.ImageIcon(getClass().getResource(«/pruebajframe/Dos.PNG»)));
    JOptionPane.showMessageDialog(null,recivido);

    break;
    case «51»:
    jLabel1.setIcon(new javax.swing.ImageIcon(getClass().getResource(«/pruebajframe/Tres.PNG»)));
    JOptionPane.showMessageDialog(null,recivido);
    break;
    default: jLabel1.setIcon(new javax.swing.ImageIcon(getClass().getResource(«/pruebajframe/Apagado.PNG»)));
    JOptionPane.showMessageDialog(null,recivido);
    break;
    }

    }*/

    }
    }

    public void mostrarError(String mensaje){
    JOptionPane.showMessageDialog(this, mensaje, «ERROR», JOptionPane.ERROR_MESSAGE);
    }

    @SuppressWarnings(«unchecked»)
    //
    private void initComponents() {

    jButton1 = new javax.swing.JButton();
    jButton2 = new javax.swing.JButton();
    jButton3 = new javax.swing.JButton();
    jComboBox1 = new javax.swing.JComboBox();
    jLabel1 = new javax.swing.JLabel();

    setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
    setTitle(«Display 7 Segmentos»);
    setName(«PanelCentral»); // NOI18N
    setResizable(false);

    jButton1.setText(«Mostrar»);
    jButton1.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.RAISED));
    jButton1.addActionListener(new java.awt.event.ActionListener() {
    public void actionPerformed(java.awt.event.ActionEvent evt) {
    jButton1ActionPerformed(evt);
    }
    });

    jButton2.setText(«Encender»);
    jButton2.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.RAISED));
    jButton2.addActionListener(new java.awt.event.ActionListener() {
    public void actionPerformed(java.awt.event.ActionEvent evt) {
    jButton2ActionPerformed(evt);
    }
    });

    jButton3.setText(«Apagar»);
    jButton3.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.RAISED));
    jButton3.addActionListener(new java.awt.event.ActionListener() {
    public void actionPerformed(java.awt.event.ActionEvent evt) {
    jButton3ActionPerformed(evt);
    }
    });

    jComboBox1.setFont(new java.awt.Font(«Tahoma», 2, 18)); // NOI18N
    jComboBox1.setModel(new javax.swing.DefaultComboBoxModel(new String[] { «Cero», «Uno», «Dos», «Tres», «Cuatro», «Cinco», «Seis», «Siete», «Ocho», «Nueve» }));
    jComboBox1.setBorder(javax.swing.BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.RAISED));
    jComboBox1.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
    jComboBox1.setName(«Jcombo»); // NOI18N

    jLabel1.setIcon(new javax.swing.ImageIcon(getClass().getResource(«/pruebajframe/Apagado.PNG»))); // NOI18N
    jLabel1.setLabelFor(jComboBox1);
    jLabel1.setText(«jLabel1»);

    javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
    getContentPane().setLayout(layout);
    layout.setHorizontalGroup(
    layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
    .addGap(31, 31, 31)
    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
    .addComponent(jComboBox1, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
    .addComponent(jButton1, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
    .addGroup(layout.createSequentialGroup()
    .addGap(0, 0, Short.MAX_VALUE)
    .addComponent(jButton2, javax.swing.GroupLayout.PREFERRED_SIZE, 77, javax.swing.GroupLayout.PREFERRED_SIZE)
    .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
    .addComponent(jButton3, javax.swing.GroupLayout.PREFERRED_SIZE, 82, javax.swing.GroupLayout.PREFERRED_SIZE)))
    .addGap(29, 29, 29))
    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
    .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
    .addComponent(jLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 92, javax.swing.GroupLayout.PREFERRED_SIZE)
    .addGap(62, 62, 62))
    );
    layout.setVerticalGroup(
    layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
    .addContainerGap(88, Short.MAX_VALUE)
    .addComponent(jLabel1)
    .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
    .addComponent(jComboBox1, javax.swing.GroupLayout.PREFERRED_SIZE, 44, javax.swing.GroupLayout.PREFERRED_SIZE)
    .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
    .addComponent(jButton1, javax.swing.GroupLayout.PREFERRED_SIZE, 35, javax.swing.GroupLayout.PREFERRED_SIZE)
    .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
    .addComponent(jButton2, javax.swing.GroupLayout.DEFAULT_SIZE, 37, Short.MAX_VALUE)
    .addComponent(jButton3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
    .addContainerGap())
    );

    pack();
    }//

    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
    switch(jComboBox1.getSelectedIndex()){
    case 0:
    enviarDatos(TURN_Cero_ON);
    jLabel1.setIcon(new javax.swing.ImageIcon(getClass().getResource(«/pruebajframe/Cero.PNG»)));

    break;
    case 1:
    enviarDatos(TURN_Uno_ON);
    jLabel1.setIcon(new javax.swing.ImageIcon(getClass().getResource(«/pruebajframe/Uno.PNG»)));
    break;
    case 2:
    enviarDatos(TURN_Dos_ON);
    jLabel1.setIcon(new javax.swing.ImageIcon(getClass().getResource(«/pruebajframe/Dos.PNG»)));
    break;
    case 3:
    enviarDatos(TURN_Tres_ON);
    jLabel1.setIcon(new javax.swing.ImageIcon(getClass().getResource(«/pruebajframe/Tres.PNG»)));
    break;
    case 4:
    enviarDatos(TURN_Cuatro_ON);
    jLabel1.setIcon(new javax.swing.ImageIcon(getClass().getResource(«/pruebajframe/Cuatro.PNG»)));
    break;
    case 5:
    enviarDatos(TURN_Cinco_ON);
    jLabel1.setIcon(new javax.swing.ImageIcon(getClass().getResource(«/pruebajframe/Cinco.PNG»)));
    break;
    case 6:
    enviarDatos(TURN_Seis_ON);
    jLabel1.setIcon(new javax.swing.ImageIcon(getClass().getResource(«/pruebajframe/Seis.PNG»)));
    break;
    case 7:
    enviarDatos(TURN_Siete_ON);
    jLabel1.setIcon(new javax.swing.ImageIcon(getClass().getResource(«/pruebajframe/Siete.PNG»)));
    break;
    case 8:
    enviarDatos(TURN_Ocho_ON);
    jLabel1.setIcon(new javax.swing.ImageIcon(getClass().getResource(«/pruebajframe/Ocho.PNG»)));
    break;
    case 9:
    enviarDatos(TURN_Nueve_ON);
    jLabel1.setIcon(new javax.swing.ImageIcon(getClass().getResource(«/pruebajframe/Nueve.PNG»)));
    break;
    }
    }

    private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {
    //reciveDatos();
    JOptionPane.showMessageDialog(null,reciveDatos());
    }

    private void jButton3ActionPerformed(java.awt.event.ActionEvent evt) {
    serialPort.close();
    serialPort.removeEventListener();//En el caso de que lo tenga.

    System.exit(0);

    }

    public static void main(String args[]) {

    try {
    for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
    if («Nimbus».equals(info.getName())) {
    javax.swing.UIManager.setLookAndFeel(info.getClassName());
    break;
    }
    }
    } catch (ClassNotFoundException ex) {
    java.util.logging.Logger.getLogger(NumeroSegmentos.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (InstantiationException ex) {
    java.util.logging.Logger.getLogger(NumeroSegmentos.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (IllegalAccessException ex) {
    java.util.logging.Logger.getLogger(NumeroSegmentos.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (javax.swing.UnsupportedLookAndFeelException ex) {
    java.util.logging.Logger.getLogger(NumeroSegmentos.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    }

    java.awt.EventQueue.invokeLater(new Runnable() {
    @Override
    public void run() {
    new NumeroSegmentos().setVisible(true);
    }
    });
    }
    // Variables declaration – do not modify
    private javax.swing.JButton jButton1;
    private javax.swing.JButton jButton2;
    private javax.swing.JButton jButton3;
    private javax.swing.JComboBox jComboBox1;
    private javax.swing.JLabel jLabel1;
    // End of variables declaration
    }

    • Lo que pasa es que no se por que el listener no captura los datos que se dan en el puerto serie, ya lo probe y si se envian los datos a ese puerto, pero a la hora de que este listener lo reciva no pasa nada, no se si lo he hecho bien, si me pudieras hechar una mano, seria genial, de ante mano muchas gracias.

      A grandes rasgos lo que estoy tratando de hacer es que al momento de que se reciba: «He recivido: 48″(que es el cero por ejemplo), que cambien la imagen del label, si leo intencionalmente el puerto si puedo capturar lo que he mandado, pero que me funcione automáticamente con el Listener, eso no lo he logrado. gracias por tu ayuda.

      • Hola Cesar,

        he estado mirando tu codigo, y falta un detalle, que debes añadir despues de asignarle el listener al SerialReader

        serialPort.notifyOnDataAvailable(true);

        No se si me he olvidado de explicarlo yo en el articulo, o no. Despues lo reviso.

        A mayores, yo suelo hacer que la clase SerialReader, extienda de Thread, para que pueda leer en segundo plano y no se bloquee la interfaz grafica.

        Ya me cuentas si te ha servido de algo.

        Un saludo

  5. Tengo problemas para detectar errores de paridad,
    seteo serialPort.notifyOnParityError(true);
    y luego en el listener reviso si event.getEventType() == SerialPortEvent.PE
    pero no los detecta.

    Te agradezco si tienes algun comentario al respecto.

    • Hola,

      lamento decirte que nunca me he visto en la necesidad de controlar los errores de paridad. No se en que escenario estas trabajando, pero en los casos que yo he utilizado el puerto serie, nunca he tenido problemas de comunicación que me obligasen a controlar los errores de paridad.

      Una pregunta, quizas tonta: si no se producen eventos de error de paridad, ¿estas seguro de que se estan produciendo errores de paridad?.

      Un saludo

  6. Saludos.
    Si me puedes ayudar, resulta que al leer del puerto con la instruccion:
    try {
    int Leido = inputStream.read();
    //datoLeido = (byte)Leido;

    } catch (IOException e) {}

    la variable devuelve -1,
    Al escribir al puerto funciona, pero al leer del puerto no obtengo nada.
    Estoy usando Virtual serial Port para emular el puerto, no se si se me escapa algún detalle de configuración. Parece como si no se almacenaran los datos en el puerto.
    Gracias por la ayuda!

    • Hola Alfredo,

      si recibes -1, es que se ha llegado al final de la trama que estas recibiendo por el puerto serie. Esto puede suceder por varios motivos. Principalmente, porque no te estan enviando mas datos por el puerto serie. O incluso, porque estas leyendo datos más rapido de lo que te los envían, eso ya depende del dispositivo que te los esté mandando. Yo trabajo con algún «cacharro», que se toma su tiempo en enviar datos, y el flujo no es continuo.

      Ahora, si me dices que trabajar con un emulador de puerto serie, que te hace las funciones de puerto serie, ya estás añadiendo otro posible punto de fallo. ¿No tienes la posibilidad de probar con un puerto serie de verdad que reciba de cualquier aparato? En todo caso, las veces que he hecho lo que tu dices (usar un emulador), he pasado más tiempo configurando el emulador que haciendo algo util. Yo utilizaba «PortMon for Windows», y al final conseguía hacerlo funcionar: http://technet.microsoft.com/es-es/sysinternals/bb896644

      Un saludo, y suerte.

  7. Existe una librería de IBM que también proporciona el manejo de puertos. La he usado en Linux y me ha traído una serie de problemas. Probé con RXTX y mis aplicaciones ya no han tenido problemas. Al parecer la librería (que se llama javax.comm) no es tan estable. Saludos

  8. Hola.

    Estoy haciendo pruebas con el puerto serie conectado a un arduino, cuyo codigo es
    void setup (){
    Serial.begin(9600);
    }

    void loop(){
    //Lectura del puerto serie
    Serial.write(1);
    //delay(2000);
    }
    Para que este enviando todo el rato.

    Y en java tengo puesto lo que aconsejas:
    public class Prueba {

    public static void main(String args[]) {
    try {
    //System.loadLibrary(«rxtxSerial»);

    // System.out
    // .println(«Se ha cargado la librería nativa correctamente»);

    Enumeration listaPuertos = CommPortIdentifier.getPortIdentifiers();
    CommPortIdentifier idPuerto = null;
    boolean encontrado = false;
    while (listaPuertos.hasMoreElements() && !encontrado) {
    idPuerto = (CommPortIdentifier) listaPuertos.nextElement();
    if (idPuerto.getPortType() == CommPortIdentifier.PORT_SERIAL) {
    System.out.println(«is a Serial Port: »
    + idPuerto.getName());
    if (idPuerto.getName().equals(«COM4»)) {
    encontrado = true;
    }
    }
    }
    SerialPort puertoSerie = null;

    try {
    puertoSerie = (SerialPort) idPuerto.open(«Prueba», 2000);
    try {
    puertoSerie.setSerialPortParams(9600,
    SerialPort.DATABITS_8, SerialPort.STOPBITS_1,
    SerialPort.PARITY_EVEN);
    puertoSerie.notifyOnDataAvailable(true);

    InputStream entrada = puertoSerie.getInputStream();

    String s = «»;
    int dato;
    while (entrada.available() > 0) {
    dato = entrada.read();

    //while ((dato = entrada.read())>-1) {
    //s = s + (char) dato;
    System.out.println(«Dato: » + dato);

    }
    } catch (UnsupportedCommOperationException e) {
    System.out
    .println(«Error configurando parámetros de configuración»);
    } catch (IOException e) {
    e.printStackTrace();
    }
    } catch (PortInUseException e) {
    System.out.println(«Error abriendo el puerto serie»);
    }
    } catch (UnsatisfiedLinkError u) {

    System.err
    .println(«No se ha encontrado la librería nativa de puerto serie»);

    }

    }

    }
    No se si es problema de la consola del eclipse pero los valores que recibo son:
    Stable Library
    =========================================
    Native lib Version = RXTX-2.1-7
    Java lib Version = RXTX-2.1-7
    is a Serial Port: COM4
    Dato: 64
    Dato: 1
    Dato: 64
    Dato: 1
    Dato: 64
    Dato: 1
    Dato: 64
    Dato: 1
    Dato: 64
    Dato: 1
    Dato: 64
    Dato: 1
    Dato: 64
    Dato: 1
    Dato: 64
    Dato: 1
    Dato: 64
    Dato: 1
    Dato: 64
    Dato: 1
    Dato: 64
    Dato: 1
    Dato: 64
    Dato: 64
    Dato: 1
    Dato: 64
    Dato: 1
    Dato: 224

    Cuando entiendo que todos deberían ser 1. ¿Se te ocurre que puede ser?
    Gracias.

  9. Es curioso el comportamiento que describes. ¿Has probado a enviar un caracter en vez de un byte a ver que recibe el programa de java?

    Lo unico que se me ocurre, es que estes enviando información muy rápido, de hecho, en el sketch el delay lo tienes comentado. Por lo tanto es posible que al sketch le de tiempo a iterar varias veces en el loop, y ejecutar varias veces serial.write(1), y el programa de java cuando recibe el evento de que hay datos en el puerto, pues a veces tendrás esperando un byte solo, o muchos.

    Yo haría mis pruebas con caracteres de texto, al menos para poder tracear mas facilmente y ver en la consola de eclipse algo que nos de mas pistas.

    Ya me contarás si esto te ha ayudado en algo.

  10. Hola.
    Perdón por la tardanza. He puesto un delay(10) después de ir ajustando, epro el problema que tengo es:
    – Arranco el arduino antes que el pc, por lo que el programa hasta que registra el valor, correcto, que le envía, pasa por valores de 0 y -1 hasta establizarse, y no se como evitar eso. Es decir que un sensor esta siempre emitiendo y la aplicación que lo comprueba se apaga y se enciende. Al encender tengo que tener una condición que no se me ocurre para saltarse los valores iniciales no correctos.
    – La otra es que si quiero hacer una escrituradel pc y arduino al recibir el valor, escribe y el pc hace la lectura. En ese momento me da puerto ocupado. He puesto tiempos de espera, pero quizas no lo suficiente. ¿Puede ser? ¿Se puede emplear en ambos sentidos?
    Me han comentado que Processing tiene un pluging para Eclipse que soluciona el puerto seríe, con sus librerías. ¿Te suena? Lo iré probando.

  11. En el envío y respuesta, ya lo he ajustado y era porque tenía mal posicionado el while e intentaba abrir el puerto otra vez.

    Gracias por todo.

  12. hola

    estoy trabajando en una aplicacion de ubicacion. tengo una antena conectada al puerto com2 pero nose como recibir los datos que la antena me genera si me puedes ayudar te lo agradeceria mucho

    saludos

  13. una pregunta
    como puedo realizar para pasar a una pagina web, con el Jframe de java si funciona, pero hago el mismo procedimiento para que el dato me aparezca en la pagina web me da problemas

    • Si eres un poco mas especifico con los detalles de tus problemas quizas pueda ayudarte, aunque la programacion web no es mi fuerte. Un saludo

  14. Buenas noches tengo algunas dudas
    1. La comunicacion serial entre java y arduino para que me sirve ?
    2. Quiero hacer un proyecto que implique java con arduino para mover un brazo robotico con servomotores me serviria la comunicacion serial?

    • Hola!

      La plataforma arduino utiliza la comunicación serie para varias cosas. Por ejemplo, traspasar el sketch de tu pc al arduino (aunque en el modelo YUN por ejemplo, esto puede hacerse a traves de la wifi tambien). Por otro lado, la comunicación serie te sirve tanto para enviar como para recibir datos del arduino, y si, te serviria para enviar las ordenes necesarias para mover un brazo robotico con servomotores.

      UN saludo

  15. Buenas noches
    Gracias por su contestacion ahora mi duda es esta encontre un codigo este es el codigo para la comunicacion serial entre java y arduino

    import gnu.io.CommPort;
    import gnu.io.CommPortIdentifier;
    import gnu.io.SerialPort;

    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.util.Enumeration;
    public class LecturaSerial {
    //inicializamos y decalramos variables
    CommPortIdentifier portId;
    Enumeration puertos;
    SerialPort serialport;
    static InputStream entrada = null;
    Thread t;
    //creamos un constructor para realizar la conexion del puerto
    public LecturaSerial() {
    super();
    puertos=CommPortIdentifier.getPortIdentifiers();
    t = new Thread(new LeerSerial());
    while (puertos.hasMoreElements()) { //para recorrer el numero de los puertos, y especificar con cual quiero trabajar
    //hasmorelements mientras tenga mas eleementos
    portId = (CommPortIdentifier) puertos.nextElement(); //next elemento recorre uno por uno
    System.out.println(portId.getName()); //puertos disponbibles
    if (portId.getName().equalsIgnoreCase(«COM7»)) {
    try {
    serialport= (SerialPort)portId.open(«LecturaSerial», 500);//tiempo en ms
    entrada = serialport.getInputStream();//esta variable del tipo InputStream obtiene el dato serial
    t.start(); // inciamos el hilo para realizar nuestra accion de imprimir el dato serial

    } catch (Exception e) {
    } } }
    }
    //con este metodo del tipo thread relaizamos

    public static class LeerSerial implements Runnable {
    int aux;
    public void run () {
    while(true){
    try {
    aux = entrada.read(); // aqui estamos obteniendo nuestro dato serial
    Thread.sleep(100);
    if (aux>0) {
    System.out.println((char)aux);//imprimimos el dato serial
    }
    } catch (Exception e) {
    } } }
    }public static void main(String[] args) {
    new LecturaSerial();
    }}

    Gracias por su atencion

  16. Buenas noches

    Este es el otro codigo que encontre para la comunicacion serial entre java y arduino para poder un brazo robotico con servomotores cual de los dos codigos son optimos para aquella tarea

    package proyecto;

    import gnu.io.CommPortIdentifier;
    import gnu.io.PortInUseException;
    import gnu.io.SerialPort;
    import java.io.IOException;
    import java.io.OutputStream;
    import java.util.Enumeration;

    public class PROYECTO {

    /**
    * @param args the command line arguments
    */
    public static void main(String[] args)
    {
    Enumeration puertos; //busca todos los puertos y los guarda en el objeto puertos
    OutputStream ops;
    puertos=CommPortIdentifier.getPortIdentifiers(); //ojo tiene que tener la -s- al ultimo porque hay otro metodo sin -s-
    CommPortIdentifier portId; // identifica los puertos com
    SerialPort serialport; // esta clase abre puertos

    while (puertos.hasMoreElements()) { //para recorrer el numero de los puertos, y especificar con cual quiero trabajar
    //hasmorelements mientras tenga mas eleementos
    portId = (CommPortIdentifier) puertos.nextElement(); //next elemento recorre uno por uno
    System.out.println(portId.getName()); //puertos disponbibles
    if (portId.getName().equalsIgnoreCase(«COM3»)) {
    try {
    serialport= (SerialPort)portId.open(«EscrituraSerial1″, 500);//tiempo en ms
    ops=serialport.getOutputStream();
    ops.write (» UPS».getBytes()); //get bytes transforma el string a bytes
    ops.close();
    serialport.close();
    } catch (Exception e) {
    }
    }
    }
    }

    }

    Gracias por su atención

    • Hola,

      los snippets que has pegado lo unico que hacen es enumerar los puertos serie del sistema, abrirlos y enviar un pequeño dato por uno. Realmente, los dos hacen lo mismo y no se puede decir que uno sea mejor que otro, ya que en realidad, no hacen nada. Creo que lo que te falta es saber exactamente tu interfaz de comunicación con el robot, es decir, que comandos tienes que enviarle, y cuales recibes, y en que secuencia. Ademas de eso, tienes que entender un poco como funciona el paradigma de la comunicación serie. Dudo de que vayas a encontrar un codigo Java en la red que puedas utilizar sin modificarlo para tu proyecto. Creo que tendras que programarlo tu mismo.

      Un saludo.

  17. Roque!

    primero que todo te doy gracias por simplificar este rollo de las conexion mediante la libreria rxtx ha sido de muchas ayuda tu explicacion. Te comento, tengo una bascula que se conectar mediante un puerto serial, eh realizado todo al piede de la letra , probé en windows y me funciono las librerias nativas y ya tengo un prototipo, pero en linux utilizando ubunto 10.04 se me ha presentado un error con la libreria nativa:

    Java HotSpot(TM) Client VM warning: You have loaded library /home/administrador/jdk1.8.0_25/jre/lib/i386/librxtxSerial.so which might have disabled stack guard. The VM will try to fix the stack guard now.
    It’s highly recommended that you fix the library with ‘execstack -c ‘, or link it with ‘-z noexecstack’.
    java.lang.UnsatisfiedLinkError: Native Library /home/administrador/jdk1.8.0_25/jre/lib/i386/librxtxSerial.so already loaded in another classloader thrown while loading gnu.io.RXTXCommDriver
    Experimental: JNI_OnLoad called.
    Exception in thread «main» java.lang.UnsatisfiedLinkError: Native Library /home/administrador/jdk1.8.0_25/jre/lib/i386/librxtxSerial.so already loaded in another classloader
    at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1895) at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1895)
    at java.lang.ClassLoader.loadLibrary0(ClassLoader.java:1895)
    .java:1835)
    at java.lang.Runtime.loadLibrary0(Runtime.java:870)
    at java.lang.System.loadLibrary(System.java:1119)
    at gnu.io.CommPortIdentifier.(CommPortIdentifier.java:123)
    at bucaposlinux.BucaPOSLinux.main(BucaPOSLinux.java:23)
    Java Result: 1

    por que se puede estar presentado este error?

    de antemano muchas gracias Roque!

    • Aparentemente ya has cargado esa libreria en alguna otra parte de tu programa, o quizas en otro programa diferente. ¿Puede ser?

  18. Hola, antes que nada me pareció excelente tu aporte.
    Yo estoy haciendo un convertidor analógico digital que procese diversos sensores, y por medio de un cable adaptador de usb a serial conectar una programa en java que me detecte la señal del conversor y la guarde en una variable y de ahí me diga el tipo de sensor que se activo, por ejemplo de temperatura, de falla de energía y de mas.
    Y la verdad no tengo idea de donde comenzar, no se si me pudieras orientar un poco, porque la verdad apenas estoy comenzando a este mundo de la programación.

    De antemano gracias.

Los comentarios están cerrados.