Lo primero que necesitamos para ejecutar un código java en el JDK de java, que es el kit de desarrollo de java, lo podemos descargar desde la página oficial de java:
https://www.oracle.com/java/technologies/downloads/
Una vez tenemos JDK de java instalado, podremos compilar y ejecutar nuestro código de java desde nuestro entorno de programación. En concreto para el código que tenemos abajo nnecesitaremos importar una libreria extra que es: «jSerialComm» para manejar el puerto USB. Para importar esta libreria he utilizado la herramienta «maven», dejo abajo el link para descargar la herramienta:
https://maven.apache.org/download.cgi
Una vez tengamos instaladda la herramienta «maven» podemos compilar y ejecutar nuestro proyecto utilizando el comando «mvn clean install», en el caso de que estemos en IOS desde terminal, y escribiremos «mvn clean package» en el caso de estar en Windows.
Importante crear los directorios src/main/java, dentro de la carpeta de nuestro proyecto, y dentro del directorio de java nuestro archivo principal de java. También debemos crear un archivo «pom.xml» donde indicaremos las librerias que queremos que la herramienta «maven» nos descargue.
Abajo dejo la jerarquia y el orden de directorios que debemos respetar para trabajar con la herramienta «maven» de Java.
El código de abajo pertenece al archivo .java principal que tendra nuestro proyecto.
import com.fazecast.jSerialComm.*;
import java.util.Scanner;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.WindowConstants;
import javax.swing.BoxLayout;
import java.awt.Component;
import java.awt.FlowLayout;
public class control {
// Variables para almacenar los valores de los sensores
static double dhtValue1 = 0.0;
static double dhtValue2 = 0.0;
static int moistureValue = 0;
static int humedadPlanta = 0;
public static void main(String[] args) throws IOException {
// Inicia el hilo que lee los datos del puerto USB
new Thread(() -> {
SerialPort comPort = SerialPort.getCommPort("/dev/tty.usbserial-0001");
//SerialPort comPort = SerialPort.getCommPort("/dev/ttyUSB0");
comPort.setBaudRate(9600);
comPort.openPort();
comPort.setComPortTimeouts(SerialPort.TIMEOUT_READ_SEMI_BLOCKING, 0, 0);
Scanner dataScanner = new Scanner(comPort.getInputStream());
while (dataScanner.hasNextLine()) {
String line = dataScanner.nextLine();
String[] values = line.split(",");
try {
if (values[0].equals("dht")) {
dhtValue1 = Double.parseDouble(values[2]);
dhtValue2 = Double.parseDouble(values[3]);
} else if (values[0].equals("moisture")) {
moistureValue = Integer.parseInt(values[1]);
}
} catch (NumberFormatException e) {
System.out.println("Error al convertir los valores a números de punto flotante: " + e.getMessage());
}
}
comPort.closePort();
}).start();
// Código para la interfaz gráfica
File file1 = new File("/Users/cesarhernandez/Desktop/video/video2/sol.jpg");
//File file1 = new File("/home/control/Schreibtisch/ventana/sol.jpg");
BufferedImage originalImage1 = ImageIO.read(file1);
BufferedImage resizedImage1 = new BufferedImage(200, 200, BufferedImage.TYPE_INT_RGB);
resizedImage1.getGraphics().drawImage(originalImage1.getScaledInstance(200, 200, Image.SCALE_SMOOTH), 0, 0,
null);
ImageIcon imageIcon1 = new ImageIcon(resizedImage1);
File file2 = new File("/Users/cesarhernandez/Desktop/video/video2/planta.jpg");
//File file2 = new File("/home/control/Schreibtisch/ventana/planta.jpg");
BufferedImage originalImage2 = ImageIO.read(file2);
BufferedImage resizedImage2 = new BufferedImage(200, 200, BufferedImage.TYPE_INT_RGB);
resizedImage2.getGraphics().drawImage(originalImage2.getScaledInstance(200, 200, Image.SCALE_SMOOTH), 0, 0,
null);
ImageIcon imageIcon2 = new ImageIcon(resizedImage2);
JFrame jFrame = new JFrame();
jFrame.setLayout(new FlowLayout());
jFrame.setSize(1000, 1000);
JPanel panel1 = new JPanel();
panel1.setLayout(new BoxLayout(panel1, BoxLayout.Y_AXIS));
JLabel jLabel1 = new JLabel();
jLabel1.setIcon(imageIcon1);
jLabel1.setText("La temperatura que hace es " + dhtValue1 + " grados.");
jLabel1.setAlignmentX(Component.CENTER_ALIGNMENT);
panel1.add(jLabel1);
jFrame.add(panel1);
JPanel panel2 = new JPanel();
panel2.setLayout(new BoxLayout(panel2, BoxLayout.Y_AXIS));
JLabel jLabel2 = new JLabel();
jLabel2.setIcon(imageIcon2);
jLabel2.setText("La humedad de la planta es de " + moistureValue + "%");
jLabel2.setAlignmentX(Component.CENTER_ALIGNMENT);
panel2.add(jLabel2);
jFrame.add(panel2);
jFrame.pack();
jFrame.setSize(Math.max(jFrame.getWidth(), 900), Math.max(jFrame.getHeight(), 500));
jFrame.setVisible(true);
jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
jFrame.setLocationRelativeTo(null);
// Actualiza los valores en la interfaz gráfica cada segundo
new Thread(() -> {
while (true) {
jLabel1.setText("La temperatura que hace es " + dhtValue1 + " grados.");
humedadPlanta = (int)(100 - (moistureValue / 4095.0) * 100);
jLabel2.setText("La humedad de la planta es de " + humedadPlanta + "%");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
Vamos a ver el código poco a poco, primero empezaremos con la importación de librerias, por ejemplo:
import com.fazecast.jSerialComm.*;
import java.util.Scanner;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.WindowConstants;
import javax.swing.BoxLayout;
import java.awt.Component;
import java.awt.FlowLayout;
La descripción de las mfunciones de las librerías se describe como:
- com.fazecast.jSerialComm.*: Esta es una biblioteca para la comunicación serial en Java. Se utiliza para interactuar con puertos serie, como los puertos COM en Windows o /dev/tty en Linux. Puede ser útil para comunicarse con hardware como microcontroladores (Arduino, por ejemplo).
- java.util.Scanner: Esta es una clase utilitaria de Java que se utiliza para analizar texto para tipos de datos primitivos y strings. Es muy útil para leer entrada del usuario desde la consola.
- java.awt.Image, java.awt.image.BufferedImage: Estas son clases para representar imágenes en memoria. BufferedImage es una subclase de Image que tiene más funcionalidades, como la capacidad de manipular directamente los píxeles de la imagen.
- java.io.File, java.io.IOException: File es una clase que se utiliza para interactuar con archivos en el sistema de archivos. IOException es una clase de excepción que se utiliza para manejar errores de entrada/salida.
- javax.imageio.ImageIO: Esta es una clase utilitaria para leer y escribir imágenes en varios formatos.
- javax.swing.ImageIcon, javax.swing.JFrame, javax.swing.JLabel, javax.swing.JPanel, javax.swing.WindowConstants, javax.swing.BoxLayout: Estas son clases para crear interfaces de usuario en Java. JFrame es una ventana, JPanel es un contenedor para otros componentes, JLabel puede mostrar texto o imágenes, ImageIcon es una implementación de Icon que pinta un icono a partir de una imagen. BoxLayout es un administrador de diseño que organiza los componentes en una caja, ya sea vertical u horizontalmente.
- java.awt.Component, java.awt.FlowLayout: Component es la superclase abstracta para los componentes de interfaz gráfica de usuario AWT (como botones, checkboxes, etc.). FlowLayout es un administrador de diseño que coloca los componentes en un contenedor en un flujo, similar a cómo fluye el texto en un párrafo.
new Thread(() -> {
En este código, se utilizan hilos «Thread» para realizar dos tareas independientes simultáneamente: la lectura de datos del puerto serie y la actualización de la interfaz gráfica de usuario (GUI). Esto se hace para evitar que la interfaz gráfica de usuario se bloquee mientras se leen los datos del puerto serie.
- Lectura de datos del puerto serie: Esto se hace en un hilo aparte porque la lectura de datos de un puerto serie puede ser una operación bloqueante, lo que significa que el hilo que realiza la lectura se bloqueará hasta que haya datos disponibles. Si se hiciera esto en el hilo principal de la GUI, la interfaz de usuario se congelaría y no respondería hasta que se hayan leído los datos.
- Actualización de la interfaz gráfica de usuario: La GUI se actualiza en un hilo aparte porque se deben realizar actualizaciones regulares (cada segundo, en este caso) independientemente de lo que esté sucediendo con la lectura de datos del puerto serie.
Al utilizar múltiples hilos, estas dos tareas pueden ejecutarse de manera concurrente. El hilo de lectura del puerto serie puede bloquearse y esperar a que lleguen datos sin afectar la capacidad de la GUI para responder a la entrada del usuario y realizar actualizaciones regulares. Del mismo modo, la GUI puede seguir funcionando incluso si hay un problema con la lectura de datos del puerto serie.
SerialPort comPort = SerialPort.getCommPort("/dev/tty.usbserial-0001");
//SerialPort comPort = SerialPort.getCommPort("/dev/ttyUSB0");
comPort.setBaudRate(9600);
comPort.openPort();
comPort.setComPortTimeouts(SerialPort.TIMEOUT_READ_SEMI_BLOCKING, 0, 0);
En el código de arriba abrimos la comunicacion con el puerto Serial USB, y esperamos a recibir datos para pasar a la siguiente instrucción.
Scanner dataScanner = new Scanner(comPort.getInputStream());
Con este código creamos un objeto de Scanner y leemos los datos que nos llegan por USB.
while (dataScanner.hasNextLine()) {
String line = dataScanner.nextLine();
String[] values = line.split(",");
try {
if (values[0].equals("dht")) {
dhtValue1 = Double.parseDouble(values[2]);
dhtValue2 = Double.parseDouble(values[3]);
} else if (values[0].equals("moisture")) {
moistureValue = Integer.parseInt(values[1]);
}
} catch (NumberFormatException e) {
System.out.println("Error al convertir los valores a números de punto flotante: " + e.getMessage());
}
}
Este bloque de código está en un bucle while que se ejecuta siempre que dataScanner, un objeto Scanner que se usa para leer datos del puerto serie, tenga una siguiente línea para leer. Este bucle se utiliza para leer y procesar continuamente los datos que se reciben a través del puerto serie.
En cada iteración del bucle:
- Se lee la siguiente línea de datos con dataScanner.nextLine(). Esta línea se espera que sea una cadena de caracteres que representa los valores de los sensores.
- La línea se divide en elementos individuales con line.split(«,»). Esto divide la línea en sus componentes individuales en función del separador «,», y los almacena en el array values.
- A continuación, el código verifica si el primer elemento del array (values[0]) es igual a «dht» o «moisture». Estos son aparentemente los identificadores para los datos del sensor de temperatura/humedad y del sensor de humedad del suelo, respectivamente.
- Si values[0] es «dht», el código intenta parsear values[2] y values[3] (los terceros y cuartos elementos del array) como números de punto flotante y almacenarlos en dhtValue1 y dhtValue2, respectivamente.
- Si values[0] es «moisture», el código intenta parsear values[1] (el segundo elemento del array) como un entero y almacenarlo en moistureValue.
- Todo el proceso de parseo está envuelto en un bloque try/catch para manejar las NumberFormatException que pueden ocurrir si las cadenas que se intentan parsear no representan números válidos. Si ocurre tal excepción, se imprime un mensaje de error en la consola.
comPort.closePort();
}).start();
Este código se utiliza para cerrar el puerto serie y luego iniciar el hilo que fue creado.
- comPort.closePort();: Este método se utiliza para cerrar el puerto serie que fue abierto anteriormente en el código. Es una buena práctica cerrar los recursos como los puertos serie cuando ya no se necesitan para liberar los recursos del sistema y evitar posibles fugas de recursos.
- }).start();: Este código se utiliza para iniciar el hilo que fue creado anteriormente. En Java, los hilos se crean y luego se inician llamando al método start(). Cuando se llama a start(), el método run() del hilo se invoca en un nuevo hilo de ejecución.
En el contexto de este código, se crea un nuevo hilo para leer datos del puerto serie. Una vez que se han leído todos los datos y se ha cerrado el puerto, se inicia el hilo. Sin embargo, parece que el cierre del puerto (comPort.closePort()) debería ocurrir después de que el bucle while haya terminado de leer todas las líneas del dataScanner. Si se coloca antes de }).start();, se ejecutará inmediatamente después de que el puerto se abra y se configure, lo que podría no ser lo deseado.
// Código para la interfaz gráfica
File file1 = new File("/Users/cesarhernandez/Desktop/video/video2/sol.jpg");
//File file1 = new File("/home/control/Schreibtisch/ventana/sol.jpg");
BufferedImage originalImage1 = ImageIO.read(file1);
BufferedImage resizedImage1 = new BufferedImage(200, 200, BufferedImage.TYPE_INT_RGB);
resizedImage1.getGraphics().drawImage(originalImage1.getScaledInstance(200, 200, Image.SCALE_SMOOTH), 0, 0,
null);
ImageIcon imageIcon1 = new ImageIcon(resizedImage1);
Este bloque de código es responsable de la carga, redimensionamiento y creación de un icono de imagen a partir de una imagen almacenada en el disco.
- File file1 = new File(«/Users/cesarhernandez/Desktop/video/video2/sol.jpg»);: Este es un objeto File que representa la ubicación de un archivo de imagen en el sistema de archivos. El texto entre comillas es la ruta al archivo. La ruta que se usa depende de dónde esté almacenada la imagen.
- BufferedImage originalImage1 = ImageIO.read(file1);: Esto lee el archivo de imagen representado por file1 en un objeto BufferedImage. BufferedImage es una subclase de Image que se utiliza para manejar y manipular imágenes.
- BufferedImage resizedImage1 = new BufferedImage(200, 200, BufferedImage.TYPE_INT_RGB);: Esto crea una nueva imagen BufferedImage de 200×200 píxeles. BufferedImage.TYPE_INT_RGB es un tipo de imagen que utiliza tres bytes por píxel, uno para cada componente de color rojo, verde y azul (RGB).
- resizedImage1.getGraphics().drawImage(originalImage1.getScaledInstance(200, 200, Image.SCALE_SMOOTH), 0, 0, null);: Esto redimensiona originalImage1 a 200×200 píxeles y luego dibuja la imagen redimensionada en resizedImage1. Image.SCALE_SMOOTH es un algoritmo de escalado que proporciona una mayor prioridad a la suavidad de la imagen que a la velocidad de escalado.
- ImageIcon imageIcon1 = new ImageIcon(resizedImage1);: Esto crea un ImageIcon a partir de resizedImage1. ImageIcon es una implementación de la interfaz Icon que pinta iconos a partir de imágenes. Los ImageIcon se utilizan comúnmente en componentes como JLabel en Swing para mostrar imágenes.
JFrame jFrame = new JFrame();
jFrame.setLayout(new FlowLayout());
jFrame.setSize(1000, 1000);
Este bloque de código crea una ventana utilizando la biblioteca de interfaz gráfica de usuario Swing de Java.
- JFrame jFrame = new JFrame();: Esta línea crea una nueva instancia de un JFrame. Un JFrame es una ventana de nivel superior con una barra de título y un borde. La ventana de tamaño predeterminado es lo suficientemente grande como para mostrar la barra de título, pero no lo suficientemente grande para mostrar contenido.
- jFrame.setLayout(new FlowLayout());: Esto establece el administrador de diseño del JFrame en una nueva instancia de FlowLayout. Un administrador de diseño es un objeto que controla la forma en que los componentes (como botones, etiquetas, paneles, etc.) se organizan en un contenedor. FlowLayout es uno de los administradores de diseño más simples y coloca los componentes en un contenedor en orden, de izquierda a derecha y de arriba a abajo.
- jFrame.setSize(1000, 1000);: Esto establece el tamaño de la ventana JFrame en 1000 píxeles de ancho por 1000 píxeles de alto. Este tamaño es solo el tamaño inicial; el usuario todavía puede cambiar el tamaño de la ventana manualmente una vez que se muestra.
JPanel panel1 = new JPanel();
panel1.setLayout(new BoxLayout(panel1, BoxLayout.Y_AXIS));
JLabel jLabel1 = new JLabel();
jLabel1.setIcon(imageIcon1);
jLabel1.setText("La temperatura que hace es " + dhtValue1 + " grados.");
jLabel1.setAlignmentX(Component.CENTER_ALIGNMENT);
panel1.add(jLabel1);
jFrame.add(panel1);
Este bloque de código crea un panel (que es un contenedor) con una etiqueta que muestra un icono de imagen y algún texto. Luego añade el panel a la ventana principal (el JFrame).
- JPanel panel1 = new JPanel();: Esto crea una nueva instancia de JPanel, que es un contenedor ligero que se puede usar para agrupar otros componentes, como botones o etiquetas.
- panel1.setLayout(new BoxLayout(panel1, BoxLayout.Y_AXIS));: Esto establece el administrador de diseño de panel1 como un nuevo BoxLayout. BoxLayout es un administrador de diseño que apila componentes ya sea en una única fila (BoxLayout.X_AXIS) o en una única columna (BoxLayout.Y_AXIS). En este caso, BoxLayout.Y_AXIS significa que los componentes se añadirán en una columna vertical.
- JLabel jLabel1 = new JLabel();: Esto crea una nueva instancia de JLabel. Un JLabel puede mostrar texto, una imagen o ambos.
- jLabel1.setIcon(imageIcon1);: Esto establece el icono del JLabel en imageIcon1.
- jLabel1.setText(«La temperatura que hace es » + dhtValue1 + » grados.»);: Esto establece el texto del JLabel. El texto será «La temperatura que hace es «, seguido del valor de dhtValue1, seguido de » grados.»
- jLabel1.setAlignmentX(Component.CENTER_ALIGNMENT);: Esto centra el JLabel en el JPanel.
- panel1.add(jLabel1);: Esto añade el JLabel al JPanel.
- jFrame.add(panel1);: Finalmente, esto añade el JPanel al JFrame (la ventana principal).
jFrame.pack();
jFrame.setSize(Math.max(jFrame.getWidth(), 900), Math.max(jFrame.getHeight(), 500));
jFrame.setVisible(true);
jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
jFrame.setLocationRelativeTo(null);
Este bloque de código ajusta el tamaño de la ventana principal (el JFrame), la hace visible y configura su comportamiento cuando se cierra, y luego centra la ventana en la pantalla.
- jFrame.pack();: Esto ajusta el tamaño de la ventana para que se ajuste a todos los componentes que contiene. El tamaño de la ventana será lo suficientemente grande como para acomodar todos sus componentes a su tamaño preferido.
- jFrame.setSize(Math.max(jFrame.getWidth(), 900), Math.max(jFrame.getHeight(), 500));: Esto establece el tamaño de la ventana en el tamaño más grande entre su tamaño actual y 900 píxeles de ancho y 500 píxeles de alto. Esto asegura que la ventana tenga al menos 900×500 píxeles.
- jFrame.setVisible(true);: Esto hace que la ventana sea visible. Inicialmente, cuando se crea un JFrame, no es visible, por lo que este método necesita ser llamado para mostrar la ventana en la pantalla.
- jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);: Esto establece la operación que se realizará cuando el usuario cierra la ventana. WindowConstants.EXIT_ON_CLOSE significa que la aplicación se cerrará cuando se cierre la ventana. Esto es útil para las aplicaciones que sólo tienen una ventana y deben cerrarse completamente cuando esa ventana se cierra.
- jFrame.setLocationRelativeTo(null);: Esto centra la ventana en medio de la pantalla. Pasar null a setLocationRelativeTo significa que la ventana se centrará en relación a la pantalla completa.
while (true) {
jLabel1.setText("La temperatura que hace es " + dhtValue2 + " grados.");
humedadPlanta = (int)(100 - (moistureValue / 4095.0) * 100);
jLabel2.setText("La humedad de la planta es de " + humedadPlanta + "%");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Este bloque de código representa un bucle infinito que actualiza las etiquetas jLabel1 y jLabel2 con los valores de temperatura y humedad de la planta cada segundo.
- jLabel1.setText(«La temperatura que hace es » + dhtValue2 + » grados.»);: Esto actualiza el texto de la etiqueta jLabel1 con la temperatura actual almacenada en la variable dhtValue2. El texto será «La temperatura que hace es «, seguido del valor de dhtValue2, seguido de » grados».
- humedadPlanta = (int)(100 – (moistureValue / 4095.0) * 100);: Esto calcula el porcentaje de humedad de la planta a partir del valor de moistureValue. Parece que moistureValue es un valor entre 0 y 4095, y esta línea lo convierte a un porcentaje del 0% al 100%.
- jLabel2.setText(«La humedad de la planta es de » + humedadPlanta + «%»);: Esto actualiza el texto de la etiqueta jLabel2 con el porcentaje de humedad de la planta calculado en la línea anterior. El texto será «La humedad de la planta es de «, seguido del valor de humedadPlanta, seguido de «%».
- Thread.sleep(1000);: Esto hace que el hilo actual duerma (deje de ejecutarse) durante 1000 milisegundos, o un segundo. Esto significa que las actualizaciones de las etiquetas ocurrirán cada segundo.
- catch (InterruptedException e) { e.printStackTrace(); }: Esto maneja la excepción InterruptedException, que puede ser lanzada por Thread.sleep(). Si se produce esta excepción, el código imprimirá la traza de la pila de la excepción, lo que puede ayudar a diagnosticar el problema.
El bucle while(true) significa que estas actualizaciones continuarán indefinidamente hasta que el programa se cierre o se interrumpa de alguna otra manera.
Código correspondiente al archivo pom.xml de la herramienta «maven».
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>me.Cesar</groupId>
<artifactId>control</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>com.fazecast</groupId>
<artifactId>jSerialComm</artifactId>
<version>2.9.3</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.1.2</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>control</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>control</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Estructura del proyecto desarrollado en Java, jerarquia y directorios necesarios.
Deja una respuesta Cancelar la respuesta