domingo, 22 de junio de 2014

Recibir datos desde arduino utilizando una interfaz en java

Ejecutar jbuttons desde arduino al presionar botones físicos.

Recientemente desarrollé una aplicación utilizando arduino para manipular una interfaz gráfica a través de botones físicos conectados a arduino. La interfaz gráfica fue hecha en java, utilizando netbeans 8.0 en fedora 20.

Fue la primera vez que utilicé arduino, además también fue la primera vez que conecté arduino con java.
Cuando se usa linux, por lo general se complica lo suficiente al querer ejecutar un programa, la mayoría de las veces es por cuestiones de permisos de archivos o permisos de ejecución de archivos, así también como los permisos que tenga el directorio donde está almacenado dicho ejecutable.

Antes de comenzar es necesario que tengan instalado:

  • Java
  • Arduino
  • Librerias de arduino
  • Netbeans 7.4 O superior


  • Tomaré en cuenta que ya tienen instalado los paquetes necesarios y continuaré con la explicación de la pequeña aplicación que he creado.
    Se utilizó la tarjeta arduino uno. 

    Descripción del proyecto: crear una interfaz en java utilizando jframe con 4 botones, cada botón realizará un determinada acción. Realizar un pequeño tablero con 4 pushbuttons conectados a arduino. La idea es que cuando se presione un botón en el tablero se ejecute la acción de un botón en la interfaz desarrollada en java. De tal modo que el dispositivo de interacción sea el tablero con botones.

    Realización:
    Primero se creará la interfaz gráfica, lo dejaré a su criterio, es un jframe que debe de tener 4 botones.

    Pueden crear una interfaz parecida a la imagen de arriba o hacer una que les convenga.
    Los botones físicos que estarán conectados a arduino pueden diseñarlos o colocarlos en un protoboard.
    El circuito es el siguiente:


    El programa que estará codificado en arduino es el siguiente:
    //se declaran los pines de entrada que se usaran
    const int buttonPin = 2;     //Se reserva el numero de pin de entrada
    const int buttonPin2 = 3;    //Se reserva el numero de pin de entrada
    const int buttonPin3 = 4;    //Se reserva el numero de pin de entrada
    const int buttonPin4 = 5;    //Se reserva el numero de pin de entrada

    const int ledPin =  12;      //Se reserva el numero de pin de entrada
    const int ledPin2 =  11;     //Se reserva el numero de pin de entrada
    const int ledPin3 =  10;     //Se reserva el numero de pin de entrada
    const int ledPin4 =  9;      //Se reserva el numero de pin de entrada
    //variables que cambian de valor durante la ejecucion
    int buttonState = 0;         //variable que almacena el valor del estado del pin de entrada buttonPin
    int buttonState2 = 0;        //variable que almacena el valor del estado del pin de entrada buttonPin2
    int buttonState3 = 0;        //variable que almacena el valor del estado del pin de entrada buttonPin3
    int buttonState4 = 0;        //variable que almacena el valor del estado del pin de entrada buttonPin4

    int buttonStateFinal1 = 0;         //variable que almacena el valor del estado del pin de entrada buttonPin
    int buttonStateFinal2 = 0;        //variable que almacena el valor del estado del pin de entrada buttonPin2
    int buttonStateFinal3 = 0;        //variable que almacena el valor del estado del pin de entrada buttonPin3
    int buttonStateFinal4 = 0;        //variable que almacena el valor del estado del pin de entrada buttonPin4

    int sensorPin = A0;          //entrada del sensor de ritmocardiaco se declara el pin que se usara
    int sensorValue = 0;         //variable en la que se almacena el valor leido del pin de entrada del sensor

    int leerDatos=0; //variable para indicar si se continua leyendo o no;
    void setup(){
      //Serial.begin(115200); //Open the serial port
      Serial.begin(9600); //se abre el puerto serial
      //se inicializan los pines salida en caso de que se necesiten
      pinMode(ledPin, OUTPUT);
      pinMode(ledPin2, OUTPUT);
      pinMode(ledPin3, OUTPUT);
      pinMode(ledPin4, OUTPUT);
      //se inicializan los pines de entrada y que seran los que se tienen que leer
      //para saber cual es estado de cada boton
      pinMode(buttonPin, INPUT);
      pinMode(buttonPin2, INPUT);
      pinMode(buttonPin3, INPUT);
      pinMode(buttonPin4, INPUT);
    }

    void loop(){
        //leerDatos = Serial.read();
        //if(leerDatos=='1'){
          // se leen los estados de cada uno de los pines de entrada
          buttonState = digitalRead(buttonPin);
          buttonState2 = digitalRead(buttonPin2);
          buttonState3 = digitalRead(buttonPin3);
          buttonState4 = digitalRead(buttonPin4);
          //sensorValue = analogRead(sensorPin);
          // verifica el valor leido de cada pin
          // si el valor es 1 los leds se encienden, en caso contrario
          // el led continua apagado
          if(buttonState == HIGH){
            digitalWrite(ledPin4, HIGH);
            buttonStateFinal1=1;
          }else{
            digitalWrite(ledPin4, LOW);
            buttonStateFinal1=0;
          }
          if(buttonState2 == HIGH){
            digitalWrite(ledPin3, HIGH);
            buttonStateFinal2=2;
          }else{
            digitalWrite(ledPin3, LOW);
            buttonStateFinal2=0;
          }
          if(buttonState3 == HIGH){
            digitalWrite(ledPin2, HIGH);
            buttonStateFinal3=3;
          }else{
            digitalWrite(ledPin2, LOW);
            buttonStateFinal3=0;
          }
          if(buttonState4 == HIGH){
            digitalWrite(ledPin, HIGH);
            buttonStateFinal4=4;
          }else{
            digitalWrite(ledPin, LOW);
            buttonStateFinal4=0;
          }
          //se imprimen los valores leidos a pantalla, este proceso es necesario
          //de aqui depende para que desde java se puedan leer dichos valores
          Serial.println(buttonStateFinal1);
          Serial.println(buttonStateFinal2);
          Serial.println(buttonStateFinal3);
          Serial.println(buttonStateFinal4);
          //Serial.println(sensorValue);
          //el programa se espera un tiempo de 100 milisegundos antes de volver
          //a realizar una lectura, no es tan necesario el retardo, para
          //para que java no se le haga pesado, es necesario un retardo
        //}
        delay(100); /*detiene el programa por 100 milisegundos antes de volver a leer los puerto*/

    }

    El código de arduino parece mucho, pero las impresiones son más bien para saber qué está pasando en cada momento, así que descuiden, dependiendo de la necesidad que tengan, así pueden ir modificando dicho código.

    Después de que su código ya esté codificado en la tarjeta, procedemos a crear la aplicación en java.

    La clase principal, que se mostró en la primera imagen lleva el siguiente código. En realidad pueden crear sólo un jframe con 4 botones o los que consideren necesarios y listo.

    /*
     * La clase VentanaJuego que extiende jframe, contiene el juego principal,
        es decir, ésta clase es donde se muesra en sí todo el proceso del juego.
     */

    package ventanasScoop;

    import java.util.logging.Level;
    import java.util.logging.Logger;

    /**
     *
     * @author neo Jesús Fernando Merino Merino
     */
    public class VentanaJuego extends javax.swing.JFrame implements  Runnable{

        /**
         * Se crea un objeto de tipo VentanaJuego para que reciba el objeto que le
         * envian de la interfaz iniciarScoop, dicho objeto.
         * Es aquí donde se crea el objeto que se usará para leer los datos desde arduino
         */
        /*Objeto creado para manipular la interfaz del juego scoop*/
        private VentanaJuego jugarJuego;
        /*Se crea el objeto de tipo DatosJugador dicho objeto almacenará toda la
        información necesari del jugador que inice sesión para jugar*/
        private DatosJugador datosJugador;
        /*variable que almacena la interfaz de inicio de sesión*/
        private iniciarScoop inicioSesion;
        /*variable que se usa para iniciar el sensado de la tarjeta arduino
        básicamente es un hilo que se lanza, y que sólo se consultan los datos
        almacenados en un vector declarado en dicha clase*/
        //LecturaFromArduino lectArduino=new LecturaFromArduino();
        private LecturaFromArduino lectArdReciv;
        
        /*variable que debe de estar activada para poder realizar validaciones cuando
        el usuario esté sobre esta ventana*/
        private boolean permitBotArdVentJueg=true;
        
        /*variable que almacena el hilo que se pone en ejecución para relizar la validadión
        de los datos que se reciben al presionar un botón en el tapete*/
        Thread obtDatArd;
        
        public VentanaJuego() {
            initComponents();
        }
        public VentanaJuego(VentanaJuego ventJuego,DatosJugador datJugador) {
            initComponents();
        }
        public VentanaJuego(iniciarScoop inicio,DatosJugador datJugador) {
            /*constructor que recibe el objeto de inicio de sesión y los datos del jugador*/
            initComponents();
            setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE);
            setLocationRelativeTo(null);
            setDatosJugador(datJugador);
            setInicioSesion(inicio);
            visulizarNomJugadorVentana();
        }
        public VentanaJuego(iniciarScoop inicio,DatosJugador datJugador, LecturaFromArduino lectArd) {
            /*constructor que recibe el objeto de inicio de sesión, los datos del jugador
            y el objeto que inicia la lectura desde arduino, para poder acceder al método
            que retorna el arreglo de datos obtenidos desde arduino*/
            initComponents();
            setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE);
            setLocationRelativeTo(null);
            setDatosJugador(datJugador);
            setInicioSesion(inicio);
            visulizarNomJugadorVentana();
            setLectArduReciv(lectArd);
            iniciaValidBotArd();
        }
        public void ninioONinia(DatosJugador datosJugador){
            /*método que valida si el usuario que a accedio es niño o niña, para modificar
            el color de los íconos*/   
        }
        public final void visulizarNomJugadorVentana(){
            /*se establece el nombre del jugador en el jlabel*/
            jlDatosJugador.setText("Bienvenido: "+datosJugador.getNomJugador());
        }
        /*inicia declaración de métodos de set y get de los atributos de esta interfaz*/
        public void setVentanaJugador(VentanaJuego ventana){
            jugarJuego=ventana;
        }
        public final void setDatosJugador(DatosJugador datos){
            datosJugador=datos;
        }
        public final void setInicioSesion(iniciarScoop inicio){
            inicioSesion=inicio;
        }
        public void setPermitBotArdVentJue(boolean permitir){
            permitBotArdVentJueg=permitir;
        }
        public final void setLectArduReciv(LecturaFromArduino let){
            /*inicializa el objeto recibido del sensado desde arudino*/
            lectArdReciv=let;
        }
        public boolean getPermitBotArdVentJue(){
            return permitBotArdVentJueg;
        }
        /*finaliza declaración de métodos de set y get de los atributos de esta interfaz*/
        
        public void detenerValidBotVentJuego(boolean  estado){
            /*detiene o reanuda la valición de los resultados que se obtienen de los
            hilos, dependiendo si el usuario se encuentra en la ventanaJuego*/
            permitBotArdVentJueg=estado;
        }
        public final void iniciaValidBotArd(){
            obtDatArd=new Thread(this);
            obtDatArd.start();
        }              

        private void jbSalirActionPerformed(java.awt.event.ActionEvent evt) {                                        
            new SalirJuego(this, true,datosJugador,inicioSesion,lectArdReciv).setVisible(true);
        }                                       
        

        // Variables declaration - do not modify                     
        private javax.swing.JButton jbAyuda;
        private javax.swing.JButton jbIniciar;
        private javax.swing.JButton jbRanking;
        private javax.swing.JButton jbSalir;
        private javax.swing.JLabel jlDatosJugador;
        private javax.swing.JLabel jlTutorial;
        private javax.swing.JPanel jpBackJuego;
        // End of variables declaration                   

        @Override
        public void run() {
            //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
            /*método que ejecuta acciones en la ventana de menú del jugador, de acuerdo
            a los datos que se reciben de los botones que se presionan desde el tapete*/
            String [] datosArd;
            while (permitBotArdVentJueg) {            
                datosArd = lectArdReciv.getDatRecArduino();
                //System.out.println("Datos de arduino: "+datosArd[0]+"  "+datosArd[1]+"  "+datosArd[2]+"  "+datosArd[3]);
                System.out.println("Datos de arduino: "+datosArd[0]);
                /*proceso de validar qué número de botón se ha presionado*/
                switch(datosArd[0]){
                    case "1":
                        jbIniciar.doClick();
                        break;
                    case "2":
                        jbRanking.doClick();
                        break;
                    case "3":
                        jbAyuda.doClick();
                        break;
                    case "4":
                        jbSalir.doClick();
                        break;
                    default:
                        break;
                }
                try {
                    Thread.sleep(100);
                } catch (InterruptedException ex) {
                    Logger.getLogger(VentanaJuego.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }


    }

    Básicamente las funciones que están de azul, son las más importantes.
    En primera se inicia un un hilo que crea un objeto de tipo zyncronized, el cual lee constantemente desde el puerto que esté conectado arduino, luego valida qué botón fue el que se pulsó, de acuerdo al número que se recibe de arduino.
    Y lo que se hace es, que se ejecuta el doclic() del botón que se necesario. Y listo.
    La clase de arduino es la siguiente:
    package ventanasScoop;

    import gnu.io.CommPortIdentifier;
    import gnu.io.PortInUseException;
    import gnu.io.SerialPort;
    import gnu.io.SerialPortEvent;
    import gnu.io.SerialPortEventListener;
    import gnu.io.UnsupportedCommOperationException;
    import static java.awt.image.ImageObserver.ERROR;
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStreamReader;
    import java.io.OutputStream;
    import java.util.Enumeration;
    import java.util.TooManyListenersException;

    /**
     *
     * @author neo Jesus Fernando Merino
     */
    public class LecturaFromArduino implements SerialPortEventListener{
        private OutputStream Output = null;
        private BufferedReader Input = null;
        private SerialPort serialPort;
        private String[] inputLine;
        private final String PORT_NAME = "/dev/ttyUSB0";
        private static final int TIME_OUT = 110;
        private static final int DATA_RATE = 9600;
        int cont=0;

        public LecturaFromArduino() {
            this.inputLine = new String[5];
        }
        public String[] getDatRecArduino(){
            /*retorna el arreglo que contien los datos que se reciben desde Arduino*/
            return inputLine;
        }
        @Override
        public synchronized void serialEvent(SerialPortEvent oEvent) {
            if (oEvent.getEventType() == SerialPortEvent.DATA_AVAILABLE){
                try{
                    inputLine[0]=Input.readLine();
                    //inputLine[1]=Input.readLine();
                    //inputLine[2]=Input.readLine();
                    //inputLine[3]=Input.readLine();
                    //System.out.println("Datos recibidos: "+inputLine[0]+" : "+inputLine[1]+" : "+inputLine[2]+" : "+inputLine[3]);
                }catch (IOException e){
                    System.err.println("Erorr función serialEvent: "+e.getMessage());
                }
            }
        }
        
        public void ArduinoConnection() {
            CommPortIdentifier portId = null;
            Enumeration portEnum = CommPortIdentifier.getPortIdentifiers();
            while (portEnum.hasMoreElements()) {
                CommPortIdentifier currPortId = (CommPortIdentifier) portEnum.nextElement();
                if (PORT_NAME.equals(currPortId.getName())) {
                    portId = currPortId;
                    break;
                }
            }
            if (portId == null) {
                System.exit(ERROR);
                return;
            }
            try {
                serialPort = (SerialPort) portId.open(this.getClass().getName(), TIME_OUT);
                serialPort.setSerialPortParams(DATA_RATE,
                        SerialPort.DATABITS_8,
                        SerialPort.STOPBITS_1, /*SerialPort.STOPBITS_1*/
                        SerialPort.PARITY_NONE);
                Output = serialPort.getOutputStream();
                Input = new BufferedReader(new InputStreamReader(serialPort.getInputStream()));
                serialPort.addEventListener(this);
                serialPort.notifyOnDataAvailable(true);
            } catch (PortInUseException | UnsupportedCommOperationException | IOException | TooManyListenersException e) {
                System.err.println("Error ArduinoConnection: "+e.getMessage());
            }
        }
        private void EnviarDatos(String data) {
            try {
                Output.write(data.getBytes());
            } catch (IOException e) {
                System.exit(ERROR);
            }
        }

        private void RecibirDatos() throws IOException {
            int Output1;
            Output1 = Integer.parseInt(Input.readLine());
            cont= Integer.parseInt(Input.readLine());
            System.out.println("Ouput1: "+Output1 +" valor recibido: "+cont);
        }

    }

    Ojo, necesitarán la librería librxtxSerial.so, también el jar RXTXcomm.jar, y lo colocan en la carpeta lib de su proyecto, y pues claro importan los jar para que funcione.
    Además en la pestaña propiedades de su proyecto deben especificar la ruta en donde se encuentra la librería librxtxSerial.so en la sección run en la descripción VM options: de preferencia muevan la librería librxtxSerial.so a la carptea lib de su proyecto, y coloquen la siguiente ruta.

    -Djava.library.path="../scoopControlButtons/lib/"


    Ejecutan su proyecto y deberá funcionar, en caso de que no, por favor envíenme un msj para aclarar dudas.

    6 comentarios:

    1. Buen dia, queria saber si no me podrias proporcionar el proyecto. Estoy teniendo un poco de dificultades para la realizacion de este.

      ResponderBorrar
    2. Puedes pasarme el proyecto? Estoy teniendo problemas.. en la realizacion
      Gracias de antemano

      ResponderBorrar
      Respuestas
      1. Cuál es el problema que estás teniendo??? A lo mejor te puedo ayudar. Ya que este proyecto lo desarrollé en el 2014 y ya no lo tengo. Pero si me dices cuál es el problema, tal vez pueda orientart-

        Borrar
    3. Interfaz Java y Arduino.

      Tutorial diseño de interfaz con Java bajo NetBeans para controlar Arduino desde el puerto serie / USB, odrás encender y apagar un Led, recibir mensajes de textos o comandos desde Arduino.

      Ver tutorial.
      http://www.slideshare.net/Metaconta2/interfaz-java-y-arduino

      Saludos.

      ResponderBorrar