scieee AI-readable full text Open interactive document viewer

Aplicación Android para la gestión de un invernadero inteligente con control por voz utilizando OpenHAB y Kafka

Vinuesa Serrano, Jesús

Abstract

Con el paso del tiempo, el mundo se ha vuelto más digitalizado gracias a Internet. En el transcurso de los días, miles son los elementos que usamos de forma cotidiana y que se encuentran conectados a dicha tecnología intercambiando información. Es el llamado Internet de las cosas (IoT). Este trabajo pretende poner de manifiesto la gran importancia del IoT en nuestra época. Con él se podrá gestionar y controlar exhaustivamente las condiciones ambientales existentes en un invernadero a través de una aplicación Android. El uso de la voz para su control en el caso de querer manipular el dispositivo móvil lo menos posible, así como la vigilancia del agua empleada para el riego de los cultivos son algunas de las funcionalidades de esta app. Permitirá, entre otras cosas, aprovechar todos los recursos disponibles, con el menor costo y contaminación ambiental posibles. Entre las tecnologías utilizadas destacan OpenHAB, núcleo del proyecto encargado de la automatización del sistema y Kafka, responsable del envío de mensajes entre OpenHAB y Voice2JSON. Este último será el programa utilizado para la transcripción de comandos de audio a texto, lo que permitirá el empleo de la voz humana. También se hará uso de MQTT, protocolo de comunicación empleado para la interconexión entre OpenHAB y la placa ESP32, configurada por medio de Arduino.

Full text

Trabajo Fin de Grado Grado en Ingeniería de las Tecnologías de Telecomunicación Aplicación Android para la gestión de un invernadero inteligente con control por voz utilizando OpenHAB y Kafka Autor: Jesús Vinuesa Serrano Tutora: María Teresa Ariza Gómez Dpto. Ingeniería Telemática Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2023 ii iii Trabajo Fin de Grado Grado en Ingeniería de las Tecnologías de Telecomunicación Aplicación Android para la gestión de un invernadero inteligente con control por voz utilizando OpenHAB y Kafka Autor: Jesús Vinuesa Serrano Tutora: María Teresa Ariza Gómez Departamento de Ingeniería Telemática Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2023 iv v Trabajo Fin de Grado: Aplicación Android para la gestión de un invernadero inteligente con control por voz utilizando OpenHAB y Kafka Autor: Jesús Vinuesa Serrano Tutora: María Teresa Ariza Gómez El tribunal nombrado para juzgar el Proyecto arriba indicado, compuesto por los siguientes miembros: Presidente: Vocales: Secretario: Acuerdan otorgarle la calificación de: El Secretario del Tribunal Sevilla, 2023 vi vii A mi gran familia viii ix Agradecimientos En este pequeño apartado se me permite declarar de forma resumida unos agradecimientos hacia todas aquellas personas cercanas que me han influido positivamente durante la carrera. Por ello, me gustaría remarcar que estas palabras son solo una pequeñísima parte del gran sentimiento de gratitud que tengo hacia cada una de las personas que voy a nombrar a continuación. Dichos agradecimientos tienen que empezar dirigiéndose a tres personas que conforman la base de mi vida y son los principales responsables de que hoy me encuentre escribiendo estas palabras: mis padres y mi hermano. Ellos han sabido escucharme en cada uno de mis momentos, tanto buenos como malos, me han aconsejado en multitud de ocasiones y me han dado los empujones necesarios cuando las cosas no se veían agradables, todo ello de manera incondicional. Gracias. El paso por la universidad me ha dado la oportunidad de conocer a nuevas personas las cuales han hecho que éste fuera mucho más ameno, sobre todo en estos últimos años. Agradecer especialmente a Ishika y Marcelino por todas las vivencias compartidas durante este recorrido. Agradecer también a Diego y Nacho, mis compañeros de toda la vida. Agradecer de todo corazón a mi tutora, María Teresa Ariza Gómez, por haberme brindado la oportunidad de realizar este trabajo. Por las extensas reuniones, por los consejos y por todos los conocimientos impartidos en sus clases, gracias. Me gustaría acabar recordando a aquellos integrantes de mi familia que ya no están conmigo. Agradecerles, desde aquí, cada segundo que he vivido junto a ellos. Jesús Vinuesa Serrano Sevilla, 2023 xvi Índice Agradecimientos ............................................................................................................................. ix Resumen ......................................................................................................................................... xi Abstract ........................................................................................................................................ xiii Índice ............................................................................................................................................ xvi Índice de Tablas ............................................................................................................................. xix Índice de Figuras ............................................................................................................................. xx Notación ..................................................................................................................................... xxiv 1 Introducción .............................................................................................................................. 1 1.1 Motivación del trabajo .............................................................................................................. 1 1.2 Objetivos y enfoque................................................................................................................... 2 1.2.1 Objetivos específicos ............................................................................................................. 3 1.2.2 Funcionalidad de cara al cliente o al usuario final ............................................................... 4 1.2.3 Esquema de la arquitectura .................................................................................................. 4 1.3 Estructura de la memoria ......................................................................................................... 5 1.4 Antecedentes ............................................................................................................................. 5 2 Tecnologías requeridas ............................................................................................................. 7 2.1 Recursos software ..................................................................................................................... 7 2.1.1 OpenHAB ............................................................................................................................... 7 2.1.2 Kafka ...................................................................................................................................... 7 2.1.3 Voice2JSON ............................................................................................................................ 8 2.1.4 MQTT ..................................................................................................................................... 8 2.1.5 Arduino .................................................................................................................................. 8 2.1.6 Ping ........................................................................................................................................ 9 2.2 Herramientas software ............................................................................................................. 9 2.2.1 Android Studio....................................................................................................................... 9 2.2.2 PostgreSQL ............................................................................................................................ 9 2.2.3 Spring ................................................................................................................................... 10 2.2.4 GNU/Linux ........................................................................................................................... 10 2.2.5 Windows 11 Home .............................................................................................................. 10 2.2.6 Python .................................................................................................................................. 11 2.2.7 Driver Silicon Labs ............................................................................................................... 11 2.2.8 Extractor de tokens de Xiaomi ........................................................................................... 11 2.3 Recursos Hardware ................................................................................................................. 11 2.3.1 Ordenador portátil .............................................................................................................. 12 2.3.2 Teléfono Móvil .................................................................................................................... 12 2.3.3 Punto de Acceso WIFI ......................................................................................................... 12 2.3.4 Módulo ESP32 ..................................................................................................................... 13 xvii 2.3.5 Protoboard .......................................................................................................................... 14 2.3.6 Módulo Relé 5V ................................................................................................................... 14 2.3.7 Motor de agua ..................................................................................................................... 14 2.3.8 Portapilas 4xAA ................................................................................................................... 15 2.3.9 Cables ................................................................................................................................... 16 2.3.10 Cables de alimentación ................................................................................................... 16 2.3.11 LEDs .................................................................................................................................. 17 2.3.12 Resistencias ..................................................................................................................... 17 2.3.13 Transistor NPN................................................................................................................. 17 2.3.14 Módulo DHT11 ................................................................................................................ 18 2.3.15 Fotorresistencia ............................................................................................................... 18 2.3.16 Sensor de nivel resistivo.................................................................................................. 19 2.3.17 Sensor de humedad del suelo ........................................................................................ 19 2.3.18 Bombilla Xiaomi ............................................................................................................... 19 2.3.19 Multímetro ...................................................................................................................... 20 2.3.20 Soldador Eléctrico ........................................................................................................... 21 2.3.21 Pilas .................................................................................................................................. 21 3 Reconocimiento de voz y comunicación Kafka con OpenHAB ................................................ 23 3.1 Voice2JSON .............................................................................................................................. 23 3.1.1 Herramientas de línea de comandos ................................................................................. 24 3.1.2 Diccionarios de pronunciación ........................................................................................... 28 3.1.3 Funcionamiento .................................................................................................................. 29 3.2 Kafka ........................................................................................................................................ 37 3.2.1 Patrón Publish/Subscribe.................................................................................................... 39 3.2.2 Componentes básicos de Kafka .......................................................................................... 39 3.2.3 Interfaces de Kafka .............................................................................................................. 41 3.2.4 Formato de los mensajes .................................................................................................... 41 3.2.5 Conexión Voice2JSON-Kafka............................................................................................... 44 3.3 OpenHAB ................................................................................................................................. 45 3.3.1 Componentes básicos de OpenHAB .................................................................................. 46 3.3.2 Reglas ................................................................................................................................... 57 3.3.3 Persistencia .......................................................................................................................... 63 3.3.4 API REST de OpenHAB ........................................................................................................ 64 3.3.5 Conexión Kafka-OpenHAB .................................................................................................. 68 4 Comunicación MQTT entre OpenHAB y la placa ESP32 ........................................................... 71 4.1 MQTT ....................................................................................................................................... 71 4.1.1 Componentes básicos de MQTT ........................................................................................ 71 4.1.2 Estructura de un Paquete de Control MQTT ..................................................................... 73 4.1.3 Paquetes de control ............................................................................................................ 78 4.1.4 Arquitectura MQTT del proyecto ....................................................................................... 80 4.2 Arduino .................................................................................................................................... 82 4.2.1 Placa ESP32 .......................................................................................................................... 82 4.2.2 Conexión física de los dispositivos con la placa ESP32 ...................................................... 84 4.2.3 Código Arduino .................................................................................................................... 85 5 Descripción del almacenamiento de datos y aplicación Android ............................................ 89 5.1 Base de datos PostgreSQL ...................................................................................................... 89 5.2 Servidor REST ........................................................................................................................... 91 5.3 Aplicación Android .................................................................................................................. 99 xviii 5.3.1 Organización ...................................................................................................................... 100 5.3.2 Conexión entre componentes .......................................................................................... 106 6 Conclusiones y futuras ampliaciones .................................................................................... 109 6.1 Líneas futuras: Seguridad ..................................................................................................... 110 6.2 Líneas futuras: Diseño ........................................................................................................... 110 6.3 Líneas futuras: Funcionalidad ............................................................................................... 110 Referencias .................................................................................................................................. 112 Anexo A: Instalación de Voice2JSON ........................................................................................... 114 Anexo B: Comandos para la ejecución del servicio Kafka ............................................................ 116 Anexo C: Instalación y configuración de Arduino y ESP32 ........................................................... 117 Anexo D: Configuración de la base de datos ................................................................................ 121 xix Índice de Tablas Tabla 2-1: Características del módulo ESP32 13 Tabla 2-2:Características del motor de agua 15 Tabla 2-3: Características del módulo DHT11 18 Tabla 2-32-4: Características del sensor resistivo 19 Tabla 2-42-5: Características de la bombilla Xiaomi 19 Tabla 3-1: Idiomas permitidos en Voice2JSON 23 Tabla 3-2: Comandos Voice2JSON 25 Tabla 3-3: Estructura oraciones aceptadas 36 Tabla 3-4: Conjunto de metapalabras 36 Tabla 3-5: Estados de los things 46 Tabla 3-6: Detalles de los estados 49 Tabla 3-7: Tipos de Items 51 Tabla 3-8: Estructura de una regla en OpenHAB 57 Tabla 3-9: Triggers 58 Tabla 3-10: Subexpresiones 58 Tabla 4-1: Tipos de paquetes de control MQTT 74 Tabla 4-2: Flags de los paquetes de control MQTT 75 Tabla 4-3: Carga útil de los paquetes de control MQTT 78 Tabla 4-4: Topics MQTT 81 Tabla 4-5: Pines utilizados en el sistema 84 Tabla 4-6: Periodos de muestreo 87 xx Índice de Figuras Figura 1-1: Esquema de la arquitectura del Trabajo Fin De Grado 5 Figura 2-1: OpenHAB 7 Figura 2-2: Kafka 7 Figura 2-3: Voice2JSON 8 Figura 2-4: MQTT 8 Figura 2-5: Arduino 8 Figura 2-6: Ping 9 Figura 2-7: Android Studio 9 Figura 2-8: PostgreSQL 9 Figura 2-9: Spring Framework 10 Figura 2-10: Linux 10 Figura 2-11: Windows 11 10 Figura 2-12: Python 11 Figura 2-13: Silicon Labs 11 Figura 2-14: Asus Vivobook 16x OLED 12 Figura 2-15: Huawei P30 12 Figura 2-16: Punto de acceso WiFi 12 Figura 2-17: Modulo ESP32 13 Figura 2-18: Protoboard 14 Figura 2-19: Módulo Relé 14 Figura 2-20: Motor de Agua 15 Figura 2-21: Portapilas 4xAA 15 Figura 2-22: Cable USB-Micro USB 16 Figura 2-23: Cables Arduino 16 Figura 2-24: LEDs 17 Figura 2-25: Resistencia 17 Figura 2-26: Transistor NPN 17 Figura 2-27: Modulo DHT11 18 Figura 2-28: Fotorresistencia 18 Figura 2-29: Sensor Nivel Resistivo 19 Figura 2-30: Sensor Humedad Terrestre 19 Figura 2-31: Bombilla Xiaomi 20 Figura 2-32: Multímetro 20 Figura 2-33: Soldador Eléctrico 21 xxi Figura 2-34: Pila 21 Figura 3-1: Archivo sentences.ini 26 Figura 3-2: Diccionario base 28 Figura 3-3: Diccionario 29 Figura 3-4: Componentes de Voice2JSON 29 Figura 3-5: Esquema general Voice2JSON 30 Figura 3-6: Modelo del idioma base 32 Figura 3-7: Modelo del idioma 33 Figura 3-8: Modelo lingüístico 34 Figura 3-9: Sintaxis del estado de una bombilla 34 Figura 3-10: Grafo del estado de una bombilla 35 Figura 3-11: Reconocedor de intenciones 37 Figura 3-12: Estructura previa a Kafka 38 Figura 3-13: Estructura con Kafka 38 Figura 3-14: Patrón Publish/Subscribe 39 Figura 3-15: Formato de los paquetes Kafka 42 Figura 3-16: Serializer 43 Figura 3-17: Deserializer 43 Figura 3-18: Código enviar_script.sh 44 Figura 3-19: Diagrama de estados OpenHAB 48 Figura 3-20: Lista de things instalados 50 Figura 3-21: Binding HTTP 53 Figura 3-22: Configuración channel de HTTP URL Thing 54 Figura 3-23: Binding MQTT 54 Figura 3-24: Configuración Bróker MQTT 55 Figura 3-25: Enlace con el thing MQTT Broker 56 Figura 3-26: Binding Xiaomi 56 Figura 3-27: Ejecución del programa extractor de tokens de dispositivos Xiaomi 56 Figura 3-28: Channels para el thing de la bombilla de Xiaomi 57 Figura 3-29: Binding JavaScript 60 Figura 3-30: Regla OpenHAB para el envío de la luminosidad 61 Figura 3-31: Código de la primera regla OpenHAB 61 Figura 3-32: Regla OpenHAB para comprobar la temperatura del invernadero 62 Figura 3-33: Código de la regla OpenHAB encargada de comprobar la temperatura y humedad 62 Figura 3-34: Binding PostgreSQL 63 Figura 3-35: Selección persistencia tipo jdbc 63 Figura 3-36: Configuración acceso base de datos 63 xxii Figura 3-37: Configuración items persistidos 64 Figura 3-38: Api REST OpenHAB 65 Figura 3-39: Estructura URL 65 Figura 3-40: Configuración autenticación de acceso básica 66 Figura 3-41: Configuración acceso OAuth 66 Figura 3-42: Token generado 67 Figura 3-43: Configuración regla del cortafuegos 67 Figura 3-44: Configuración Kafka en Python 68 Figura 3-45: Obtención del mensaje de Kafka 68 Figura 3-46: Configuración para el acceso al servicio REST de OpenHAB 69 Figura 3-47: Conexión con el servicio REST de OpenHAB con autenticación básica 69 Figura 3-48: Posibles opciones para apagar la bombilla 69 Figura 4-1: Estructura paquete de control MQTT 73 Figura 4-2: Cabecera fija 73 Figura 4-3: Esquema QoS=0 76 Figura 4-4: Estructura QoS=1 76 Figura 4-5: Esquema QoS=2 77 Tabla 4-3: Carga útil de los paquetes de control MQTT 78 Figura 4-6: Arquitectura MQTT del proyecto 80 Figura 4-7: Pinout ESP32 83 Figura 4-8: Conexión física entre componentes 84 Figura 4-9: Inicio del sensor DHT11 y conexión WiFi 85 Figura 4-10: Conexión servidor MQTT y asociación con la función callback() 86 Figura 4-11: Subscripción a los topics 86 Figura 4-12: Configuración fotorresistencia 87 Figura 5-1: Tabla item0001 90 Figura 5-2: Tabla limites 90 Figura 5-3: Tabla usuarios 90 Figura 5-4: Esquema ffmpeg 92 Figura 5-5: Configuración de ffmpeg 92 Figura 5-6: Configuración para realizar la transcripción 93 Figura 5-7: Configuración para obtener las fechas 93 Figura 5-8: Comprobación de credenciales 95 Figura 5-9: Definición de /valoresLimite 95 Figura 5-10: Definición clase limites 96 Figura 5-11: Configuración ConsultarLimites 97 Figura 5-12: Peticiones enviadas a la base de datos 97 xxiii Figura 5-13: Configuración EnviarComando 98 Figura 5-14: Configuración IntroducirDatoIntBBDD 98 Figura 5-16: Diagrama de casos de uso 100 Figura 5-17: Actividad MainActivity 101 Figura 5-18: Actividad Inicio 102 Figura 5-20: Actividad Gestión 103 Figura 5-19: Menú desplegable 103 Figura 5-21: Comandos predefinidos 103 Figura 5-22: Implementación librerías Retrofit 104 Figura 5-23: InterfazAPI 104 Figura 5-25: Gráfica humedad ambiental 105 Figura 5-24: Gráfica temperatura 105 Figura 5-26: Implementación librerías MPAndroidChart 105 Figura 5-27: Actividad Información 106 Figura 5-28: Actividad Configuración 106 Figura 5-29: Diagrama de secuencia 108 Figura A-1: Arquitectura máquina virtual 114 Figura A-2: Paquetes para diferentes arquitecturas 114 Figura A-3: Comando para otorgar permiso de lectura 115 Figura A-4: Comando para instalar el paquete libffi6_3.2.1-8_amd64.deb 115 Figura B-1: Comando para ejecutar el zookeeper 116 Figura B-2: Comando para ejecutar el servidor 116 Figura B-3: Comando para crear el topic 116 Figura C-1: Descarga del software Arduino 117 Figura C-2: Software Arduino USB Driver 117 Figura C-3: Software Adafruit 118 Figura C-4: Software Genuino USB Driver 118 Figura C-5: Configuración del IDE de Arduino 119 Figura C-6: Instalación de la placa ESP32 en el IDE de Arduino 119 Figura C-7: Software reconocible en el PC 120 Figura D-1: Comando para cambiar de contraseña al usuario ‘postgres’ 121 Figura D-2: Creación del nuevo usuario 'openhab' 121 Figura D-3: Creación de la base de datos 'openhabdb' 121 xxiv Notación MQTT Message Queuing Telemetry Transport OpenHAB Open Home Automation Bus JSON JavaScript Object Notation IoT Internet of Things SQL Structured Query Languaje REST Representational State Transfer OS Operating System PC Personal computer USB Universal Serial Bus UART Universal Asynchronous Receiver/Transmitter BLE Bluetooth Low Energy RAM Random Access Memory SoC System on a Chip PCB Printed Circuit Board MIT Massachusetts Institute of Technology IPA International Phonetic Alphabet JSFG JSpeech Grammar Format MFCC Mel Frequency Cepstral Coefficients G2P Grapheme to Phoneme FST Finite State Transducer ASF Apache Software Foundation CSV Comma Separated Values API Application Programming Interfaces JVM Java Virtual Machine JDBC Java Database Connectivity OAuth Open Authorization UTF-8 8-bit Unicode Transformation Format M2M Machine to Machine QoS Quality of Service I2C Inter Integrated Circuit SPI Serial Peripheral Interface LGPL GNU Lesser General Public License GPL GNU Public License PCM Pulse Code Modulation WAV Waveform Audio Format HTTP HyperText Transfer Protocol PWM Pulse Width Modulation ROM Read Only Memory URL Uniform Resource Locator xxv GPIO General Purpose Input/Output RGB Red Green Blue ADC Analog to Digital Converter DAC Digital to Analogue Converter SRAM Static Random Access Memory ISR In Sync Replica VCP Virtual Com Port MSI Microsoft Windows Installer 6 7 2 TECNOLOGÍAS REQUERIDAS “Cada día sabemos más y entendemos menos” Albert Einstein En este capítulo se aclararán cuáles han sido las tecnologías y herramientas necesarias para el desarrollo del trabajo, tanto software como hardware, definiendo el uso de cada una de ellas. 2.1 Recursos software Se entienden como recursos software los programas que han sido utilizados para la ejecución y gestión del trabajo. 2.1.1 OpenHAB Programa principal sobre el que se apoya el trabajo. Se trata de una tecnología cuyo software es de código abierto, diseñada para integrar diferentes sistemas de automatización y dispositivos dentro de una única solución. 2.1.2 Kafka Programa que forma parte de la idea base del trabajo junto con OpenHAB. Usado para enviar los comandos definidos por el usuario a OpenHAB. Figura 2-1: OpenHAB Figura 2-2: Kafka 8 2.1.3 Voice2JSON Programa de código abierto empleado para la conversión de audio a texto, en formato JSON. El texto resultante de la conversión se enviará al bróker de Kafka. 2.1.4 MQTT Se trata de un protocolo de mensajería para Internet de las cosas (IoT). Está diseñado como transporte de mensajes de tipo publicador/suscriptor ideal para conectar dispositivos remotos. Se utiliza en una amplia variedad de industrias, como las telecomunicaciones, la automotriz, la manufacturera, etc. 2.1.5 Arduino El software Arduino de código abierto facilita la escritura de algoritmos y la carga en la placa. Este software se puede utilizar con cualquier módulo compatible con Arduino. Se utilizará para programar el módulo ESP32, sensores y actuadores. Figura 2-3: Voice2JSON Figura 2-4: MQTT Figura 2-5: Arduino 9 2.1.6 Ping Aplicación móvil utilizada para probar la conectividad entre el dispositivo móvil y el ordenador a través de la red local. 2.2 Herramientas software Se entienden como herramientas software a los programas que han sido utilizados para facilitar el desarrollo y la gestión del trabajo. 2.2.1 Android Studio Es el entorno de desarrollo integrado oficial para la plataforma Android. Usado para programar la aplicación móvil del usuario con el que accederá al control y gestión del invernadero. 2.2.2 PostgreSQL Es un sistema de gestión de base de datos relacional de código abierto. Se usará para desarrollar la base de datos donde se almacenará información de interés del invernadero, basado en el lenguaje SQL. Figura 2-6: Ping Figura 2-7: Android Studio Figura 2-8: PostgreSQL 10 2.2.3 Spring Es un framework para el desarrollo de aplicaciones y contenedor de inversión de control, de código abierto para la plataforma Java. Usado para la creación del servicio REST. 2.2.4 GNU/Linux Es una familia de sistemas operativos tipo Unix compuesto por software libre y de código abierto. Es el sistema operativo utilizado en la máquina virtual que permite el funcionamiento de gran parte del proyecto. 2.2.5 Windows 11 Home Windows es el nombre de una familia de distribuciones de software para PC, servidores, sistemas empotrados y antiguamente teléfonos inteligentes desarrollados y vendidos por Microsoft y disponible para múltiples plataformas. Es la distribución software sobre la que se instalará el servidor local de OpenHAB. Figura 2-9: Spring Framework Figura 2-10: Linux Figura 2-11: Windows 11 11 2.2.6 Python Lenguaje de alto nivel de programación interpretado, multiparadigma, que soporta parcialmente la orientación a objetos, y en menor medida, una programación funcional. 2.2.7 Driver Silicon Labs Los controladores del puerto COM virtual del puente USB a UART CP210x son necesarios para el funcionamiento del dispositivo (módulo ESP32) como un puerto COM virtual para facilitar la comunicación con el host. 2.2.8 Extractor de tokens de Xiaomi Es un script que se utiliza como herramienta de extracción para recuperar tokens de todos los dispositivos conectados a la cuenta de Xiaomi almacenados en la nube y claves de cifrado para dispositivos BLE. En el trabajo se empleará para obtener el token de la bombilla Xiaomi. 2.3 Recursos Hardware Se entienden como recursos hardware a todos los dispositivos físicos empleados para el desarrollo y despliegue del proyecto. Figura 2-12: Python Figura 2-13: Silicon Labs 12 2.3.1 Ordenador portátil Ordenador portátil utilizado para alojar todos los programas y servidores empleados en el trabajo. 2.3.2 Teléfono Móvil Dispositivo móvil utilizado para acceder al control y gestión del invernadero desde la aplicación Android creada. (Teléfono Huawei P30 ELE-L29) 2.3.3 Punto de Acceso WIFI Punto de acceso WIFI que permite el establecimiento de una red local y de esta forma una comunicación entre el dispositivo móvil y el ordenador. Figura 2-14: Asus Vivobook 16x OLED Figura 2-15: Huawei P30 Figura 2-16: Punto de acceso WiFi 13 2.3.4 Módulo ESP32 Modulo compatible con Arduino que hará de puente entre los sensores y actuadores y el resto del invernadero. Es el modelo predecesor de la placa ESP8266. Algunas de sus características técnicas son: Tabla 2-1: Características del módulo ESP32 Voltaje de alimentación 5V Voltaje de entrada/salida 3.3V Corriente de Funcionamiento Min. 500mA SoC ESP32-WROOM 32 Frecuencia de Reloj 80Mhz / 240Mhz RAM 512kB Memoria Flash externa 4MB Pines I/O 34 Interfaces SPI, I2C, I2S, CAN, UART Protocolos Wi-Fi 802.11 b/g/n (802.11n hasta 150 Mbps) Frecuencia Wi-Fi V4.2 – BLE y Bluetooth clásico Antena Inalámbrica PCB Dimensiones 56x28x13mm Figura 2-17: Modulo ESP32 14 2.3.5 Protoboard Tablero con orificios que se encuentran conectados eléctricamente entre sí de manera interna, habitualmente siguiendo patrones de líneas, en los cuales se pueden insertar componentes electrónicos, cables, prototipado de circuitos y similares. Permitirá conectar los componentes electrónicos del trabajo. 2.3.6 Módulo Relé 5V Es un dispositivo electromagnético que actúa como un interruptor. Se suele emplear para controlar un circuito de salida de mayor potencia que el de entrada. En el trabajo, se utilizará para que el motor de agua pueda gestionarse, ya que necesita un suministro de potencia mayor que el del resto de los componentes electrónicos. 2.3.7 Motor de agua Mini motor de agua de 4.5V, modelo JT-DC3V-4.5, utilizado para bombear el agua. Algunas de las características técnicas: Figura 2-18: Protoboard Figura 2-19: Módulo Relé 15 Tabla 2-2:Características del motor de agua 2.3.8 Portapilas 4xAA Portapilas para 4 pilas AA que proporcionará la tensión suficiente para el funcionamiento del motor de agua. Voltaje de alimentación 4.5V Corriente de Funcionamiento 0.18A Potencia de Funcionamiento 0.81W Capacidad máxima de bombeo 0.55M Caudal Máximo 100L/h Grado de Impermeabilización IP68 Figura 2-20: Motor de Agua Figura 2-21: Portapilas 4xAA 22 23 3 RECONOCIMIENTO DE VOZ Y COMUNICACIÓN KAFKA CON OPENHAB “Los niños deben aprender cómo pensar, no qué pensar” Margaret Mead En este capítulo se detallará el funcionamiento de los programas que conforman el núcleo del proyecto (Voice2JSON, Kafka y OpenHAB), los cuales permiten la comunicación entre el usuario final, los actuadores y sensores. Se explicarán cuales son los principales comandos de Voice2JSON utilizados en el Trabajo Fin de Grado necesarios para realizar las transcripciones de audio a texto. También se indicará como se ha configurado el publicador y subscriptor en Kafka, así como su conexión con Voice2JSON. Por último, se especificarán los pasos que han sido necesarios para poner en funcionamiento el servicio de OpenHAB y su unión con Kafka. 3.1 Voice2JSON Voice2JSON es una colección de herramientas de línea de comandos para el reconocimiento de voz offline en Linux. Es gratuito, de código abierto (MIT) y admite los siguientes idiomas: Tabla 3-1: Idiomas permitidos en Voice2JSON Identificación Idioma es Español cs Checo ca Catalán de Alemán el Griego en Inglés 24 fr Francés hi Hindi it Italiano ko Coreano kz Kazajo nl Holandés pl Polaco pt Portugués ru Ruso sv Sueco vi Vietnamita zh Mandarín Voice2json admite audio mono de 16 bits a una frecuencia de 16 KHz como entrada, en archivos WAV, para poder hacer la conversión. 3.1.1 Herramientas de línea de comandos El formato general de un comando voice2json es: $ voice2json [--debug] [--profile <PROFILE>] <COMMAND> [<COMMAND_ARG>…] , donde <PROFILE> puede ser: • Un nombre de idioma compatible, como puede ser “es” o alguno de los identificadores de la tabla anterior. • Un nombre de un perfil conocido, como puede ser “es_kaldi_rhasspy” para el idioma español (de España). • Un directorio que contenga los ficheros profile.yml, sentences.ini u otros específicos del idioma. En caso de que no se indique <PROFILE>, se utilizará el perfil predeterminado de inglés de EEUU. Dentro de <COMMAND> se puede especificar uno de los siguientes comandos: 25 Tabla 3-2: Comandos Voice2JSON Comando Definición download-profile Descargar los archivos necesarios para un perfil train-profile Generar los objetos de voz/intención transcribe-wav Transcribir archivo WAV a texto transcribe-stream Transcribir secuencias de audio en directo a texto recognize-intent Reconocer la intención de un JSON o texto wait-wake Escuchar la transmisión de audio en directo para la palabra de activación record-command Grabar el comando de voz desde la transmisión de audio en directo pronounce-word Buscar o adivinar cómo se pronuncia una palabra speak-sentence Pronunciar una frase utilizando la conversión de texto a voz generate-examples Generar intentos aleatorios record-examples Generar y grabar ejemplos de voz test-examples Ejemplos de prueba de voz grabada show-documentation Ejecutar un servidor HTTP localmente con documentación print-profile Mostrar la configuración del perfil print-downloads Mostrar información de descarga del archivo de perfil print-files Mostrar archivos de perfil de usuario para copias de seguridad print-version Mostrar la versión actual de Voice2Json De estos comandos solo se explicarán con detalle, en los siguientes subapartados, los que han sido utilizados en el trabajo: 3.1.1.1 Download-profile El comando completo detallado sería: $voice2json - -profile es_kaldi_rhasspy download-profile Permite la descarga de todos los ficheros necesarios para el perfil del idioma español. 26 3.1.1.1.1 El perfil Un perfil de voice2json es un conjunto de archivos que contienen todo lo necesario para poder realizar las transcripciones del idioma español especificado en el anterior comando y reconocer los comandos de voz. Incluye: • profile.yml → Un archivo YAML que contiene, entre otros datos, el nombre del idioma del perfil, un código de configuración regional o la voz de eSpeak usada para la pronunciación de palabras. • sentences.ini → Un archivo plantilla en el que se describirán todos los comandos de voz que se quieren reconocer. En la siguiente figura se muestra parte de la configuración de este fichero: • Modelos voz/intención → Compuestos por: ▪ acoustic_model → Un directorio con los artefactos del modelo de voz. ▪ intent.pickle.gz → Durante el proceso de entrenamiento, se crea un grafo dirigido que posteriormente se transforma en un transductor de estados finitos. Este transductor es el modelo encargado de convertir secuencias de audio en secuencias de texto mediante el análisis y procesado de los diversos estados y transiciones presentes en el conjunto de datos. • Diccionarios de pronunciación → Cómo voice2json espera que se pronuncien las palabras. Se detallarán más adelante. • Modelos lingüísticos → Deducción estadística sobre qué palabras siguen a otras en los comandos de voz introducidos. Se detallarán más adelante. • Modelos de grafema a fonema → Utilizados para conocer cómo se deben pronunciar las palabras desconocidas. ▪ g2p.fst → Un transductor de estado finito creado usando phonetisaurus, una biblioteca utilizada para entrenar, evaluar y usar modelos G2P para el reconocimiento de voz. Figura 3-1: Archivo sentences.ini 27 • Mapas de fonemas → Utilizados para relacionar fonemas de voz a texto y de texto a voz. ▪ espeak-phonemes.txt → Mapa de fonemas de eSpeak. ▪ marytts_phonemes.txt → Mapa de fonemas de MaryTTS. ▪ Ipa_phonemes.txt → Mapa del Alfabeto Fonético Internacional (IPA). 3.1.1.2 Train-profile El comando completo detallado sería: $voice2json - -profile es_kaldi_rhasspy train-profile Permite generar todos los objetos necesarios de un perfil para el reconocimiento de voz/intención. Esto es, a partir de los comandos de voz que se quieren reconocer, descritos en el archivo sentences.ini, se generarán los modelos lingüísticos, los grafos para cada comando, así como los diccionarios necesarios para poder realizar las conversiones de audio a texto. A partir del apartado 3.1.2 se detallará el uso y funcionamiento de todos estos objetos. Voice2Json está diseñado para reconocer no solo los comandos de voz especificados en el fichero sentences.ini, sino que también es capaz de transcribir palabras, frases, etc que no estén incluidas en esos comandos. Esto se debe a que Voice2json permite la mezcla de los llamados modelos lingüísticos, expuestos en el apartado 3.1.3.4. En cada perfil, Voice2json incluye un diccionario base y un modelo lingüístico. El primero contiene las pronunciaciones de todas las palabras posibles y el segundo es un gran modelo entrenado con un contenido muy amplio de texto para el idioma del perfil del español. 3.1.1.3 Transcribe-wav El comando completo detallado sería: $voice2json - -profile es_kaldi_rhasspy < nombre_audio.wav Generará una sola línea de json por cada transcripción donde: • “text” → es la transcripción más probable de los datos de audio. Se representa como un string. • “transcribe_seconds” → es el número de segundos que tardó en transcribir el audio. Se representa como un número. • “wav_name” → es el nombre del archivo de audio WAV. Se representa como un string. • “wav_seconds” → es la duración del archivo de audio WAV, en segundos. Se representa como un número. Permite transcribir archivos WAV o datos de audio sin procesar. Como se comentará en el apartado 3.2.5, el resultado de esta instrucción se escribirá en un fichero de texto. Con este comando, si se le añade el argumento ‘- -open’, voice2json ignorará los comandos de voz introducidos y usará en su lugar el modelo de voz grande ya entrenado que está presente en cada perfil. 28 3.1.2 Diccionarios de pronunciación Los diccionarios están compuestos por multitud de líneas en las que, se escribe una palabra y tras ella una lista de fonemas, separados por espacios en blanco, con las que se pronuncia esa palabra. Pueden existir varias pronunciaciones para una misma palabra. Estos fonemas deben coincidir con los que el modelo acústico ha sido entrenado para reconocer. Un perfil de voice2json puede contener 3 diccionarios: 3.1.2.1 Diccionario base Este diccionario, llamado “base_dictionary.txt”, es un diccionario grande que viene ya construido en cada perfil con la mayoría de las palabras del idioma concreto. En la siguiente imagen se puede ver un pequeño contenido de este diccionario: 3.1.2.2 Palabras personalizadas Este diccionario, llamado custom_words.txt, es un diccionario pequeño, definido por el usuario, con palabras o pronunciaciones personalizadas. 3.1.2.3 Diccionario Este diccionario, llamado “dictionary.txt”, contiene exactamente el vocabulario necesario para el perfil especificado anteriormente y es generado automáticamente al ejecutar el comando ‘train-profile’. En la siguiente imagen se muestra el contenido de este diccionario, que contiene las palabras que Figura 3-2: Diccionario base 29 forman parte de los comandos escritos en el archivo sentences.ini, junto con sus fonemas, : 3.1.3 Funcionamiento Voice2JSON necesita una descripción de los comandos de voz que se desean reconocer en un archivo de configuración llamado sentences.ini por cada perfil. Para dicha configuración, se sigue una gramática JSFG simplificada [14]. Posteriormente, el contenido de este fichero se utiliza para crear un modelo de lenguaje ARPA, el cual permite describir la probabilidad de ocurrencia de secuencias de palabras, y un reconocedor de intenciones. La funcionalidad central de voice2JSON se puede dividir en dos componentes: voz e intención. Cuando los comandos de voz son reconocidos por el componente de voz, la transcripción se entrega al reconocedor de intenciones para que la procese. El resultado final de este proceso es un evento JSON estructurado con: Figura 3-3: Diccionario Figura 3-4: Componentes de Voice2JSON 30 • El texto reconocido a partir del comando de voz, precedido por el campo “text”. • El nombre de la intención al que pertenece, precedido por el campo “name”. • Metadatos opcionales sobre el proceso de reconocimiento de voz, como puede ser el tiempo de conversión, fichas, etc. La transcripción de los comandos de voz se realiza mediante uno de los tres sistemas de código abierto siguientes: • Pocketsphinx, uno de los motores de reconocimiento de voz de la Universidad Carnegie Mellon, EEUU. • Kaldi, motor desarrollado por Johns Hopkins • DeepSpeech, motor de Speech-To-Text que utiliza un modelo con técnicas de aprendizaje automático, desarrollado por Mozilla. Tanto Pocketsphinx como Kaldi requieren de: • Un modelo acústico: asigna funciones de audio a fonemas. • Un diccionario de pronunciación: para mapear fonemas a palabras. • Un modelo lingüístico: permite describir la frecuencia con la que algunas palabras siguen a otras. 3.1.3.1 Modelo Acústico Un modelo acústico asigna características acústicas del habla (como el tono, la intensidad, el timbre) a fonemas probables de un idioma determinado. Los fonemas son unidades indivisibles de pronunciación de palabras, específicos del idioma, incluso de lugares concretos. Por lo general, los coeficientes de cepstrum de frecuencia Mel (MFCC) se utilizan como características acústicas, los cuales destacan matemáticamente aspectos útiles del habla humana. El modelo acústico es un mapeo estadístico entre características de audio (MFCC) y uno o más Figura 3-5: Esquema general Voice2JSON 31 fonemas. Este mapeo se aprende a partir de una gran colección de ejemplos de voz junto con sus transcripciones correspondientes. Se necesita un diccionario de pronunciación preconstruido para mapear las transcripciones a los fonemas antes de que se pueda entrenar un modelo. Sin embargo, recopilar, transcribir y validar estos grandes conjuntos de datos de voz supone un factor limitante en el reconocimiento de voz de código abierto. 3.1.3.2 Diccionario de pronunciación Se necesita un diccionario que mapee secuencias de fonemas en palabras, tanto para entrenar un modelo acústico como para realizar el reconocimiento de voz. Durante el entrenamiento, voice2json copia las pronunciaciones de cada palabra de las plantillas de comandos de voz de un gran diccionario de pronunciación preconstruido. La pronunciación de las palabras que no se encuentran en este diccionario se solventa utilizando un modelo de grafema a fonema previamente entrenado. 3.1.3.3 Grafema a fonema Se puede usar un modelo de grafema a fonema (G2P) para buscar la pronunciación fonética de las palabras. Se trata de un modelo estadístico que asigna secuencias de caracteres (grafemas) a secuencias de fonemas, y se entrena a partir de un gran diccionario de pronunciación prediseñado. Para ello, voice2json utiliza la herramienta Phonetisaurus. 3.1.3.4 Modelo lingüístico El modelo lingüístico describe con qué frecuencia algunas palabras siguen a otras. Se crea a partir de una gran colección de textos, como pueden ser libros, sitios de noticias, etc. No todas las combinaciones estarán en el material de entrenamiento, por lo que sus probabilidades deben predecirse mediante una heurística. Los modelos lingüísticos deben estar en formato ARPA de texto plano. Un perfil voice2json suele tener 2 modelos lingüísticos: 3.1.3.4.1 Modelo del idioma base Este modelo, llamado ‘base_languaje_model.txt’, es un modelo de idioma que viene ya construido y resume un idioma dado. Se utiliza cuando se indica el argumento ‘- -open’ en el comando transcribewav, empleado durante la mezcla de modelos lingüísticos. En la siguiente imagen se muestra una pequeña parte de este modelo: 38 En el proyecto Kafka se ha utilizado como medio de comunicación entre Voice2JSON-OpenHAB y el servidor REST-OpenHAB, de manera que permitirá enviar los comandos por voz una vez transcritos por el único topic creado ‘thing1’. En caso de no tratarse de comandos por voz, estos serán enviados de igual forma a través de este servicio. La configuración del productor y del consumidor se detallarán en los apartados 3.2.5: Conexión Voice2JSON-Kafka, 5.2: Servidor REST y 3.3.5: Conexión Kafka-OpenHAB. Tradicionalmente, la arquitectura organizativa empleada para el funcionamiento de las corporaciones era la siguiente: Sin embargo, esta estructura implica un mantenimiento y una evolución complejos y costosos en la que hay que definir los formatos de datos intercambiados (como CSV o JSON) y los protocolos para cada conexión (como REST). Kafka soluciona todos estos inconvenientes, estableciéndose como un sistema intermedio entre los productores y los consumidores: Para ello, combina tres capacidades que son clave para su funcionamiento: Figura 3-12: Estructura previa a Kafka Figura 3-13: Estructura con Kafka 39 • Publicar (escribir) y suscribirse (leer) a flujos de eventos, incluida la importación y exportación continua de sus datos desde otros sistemas. • Almacenar flujos de eventos de forma duradera y fiable durante todo el tiempo que se desee. • Procesar flujos de eventos en el momento en que se producen o a posteriori. La transmisión de eventos se centra en capturar datos en tiempo real de fuentes, como bases de datos, sensores, dispositivos móviles, etc, en forma de flujos de eventos. Este flujo permite una interpretación continua de la información, de modo que ésta se encuentre en el lugar y momento adecuados. 3.2.1 Patrón Publish/Subscribe El patrón publicador/subscriptor, también llamado productor/consumidor o publish/subscribe, se emplea para la comunicación entre aplicaciones a través de mensajes. Es decir, se trata de un sistema de eventos distribuidos donde un subscriptor tiene interés por ciertos eventos. El publicador es el que genera los eventos, los cuales serán enviados posteriormente a los subscriptores interesados. El subscriptor puede incorporar un filtro para consumir solo los mensajes que le interese y suscribirse a uno o varios tipos de mensajes. Este modelo de mensajería es un paradigma asíncrono y desacoplado en el espacio, ya que los productores y los subscriptores no se conocen entre sí. En él, puede existir un proceso intermediario, que es lo ocurre en Apache Kafka, permitiendo el aumento del desacoplamiento, pero también sufriendo el inconveniente de ser un cuello de botella y un punto de fallo. Por eso, una posible solución a este problema se basa en distribuir este proceso en una red de intermediarios (los llamados brokers), que proporciona la escalabilidad y fiabilidad necesaria. De este modo, si un bróker fallase o se quedara sin conectividad, otro bróker podría tomar su lugar y gestionar las peticiones. 3.2.2 Componentes básicos de Kafka Los componentes básicos de Kafka son: Figura 3-14: Patrón Publish/Subscribe 40 3.2.2.1 Evento Un evento describe la ocurrencia de algún hecho en el mundo real y es comunicado al sistema Kafka a través de un mensaje. 3.2.2.2 Topic Los topics en Kafka son las categorías en las que se clasifican los mensajes. Así, se puede entender cada topic como si fuera un flujo de datos. Existen dos tipos de topics: - Topics regulares: Los temas regulares pueden configurarse con un tiempo de retención o un límite de espacio. Si hay registros que son más antiguos que el tiempo de retención especificado o si se supera el límite de espacio para una partición, Kafka puede eliminar los datos más antiguos para liberar espacio de almacenamiento. Por defecto, los temas se configuran con un tiempo de retención de 7 días, pero también es posible almacenar datos de forma indefinida. - Topics compactados: En este caso, los registros no caducan en función de los límites de tiempo o espacio. En su lugar, Kafka trata los mensajes posteriores como actualizaciones de mensajes más antiguos que tengan la misma clave y garantiza que nunca se borrará el último mensaje de una clave. Como se explicó al inicio del apartado 3.2, el único topic configurado en Kafka se llama ‘thing1’, por el que se enviarán todos los comandos de texto. 3.2.2.3 Partición Cada topic está dividido en particiones, que permiten a Kafka distribuir los datos en los nodos conectados (brokers). Es la unidad de paralelismo para dotar a Kafka de una escalabilidad horizontal. A cada mensaje que se quiera escribir en un topic, se le asigna una clave de partición y en caso de que no se le fije ninguna, ésta se calcula de forma aleatoria. Cada partición tiene un offset asociado que se corresponde con un identificador incremental asignado a cada mensaje. De esta forma, un mensaje se puede identificar por el nombre del topic que lo contiene, la partición a la que pertenece y su offset. 3.2.2.4 Replicación Una réplica en Kafka consiste en realizar una copia de una partición disponible en otro bróker. Este mecanismo permite a Kafka ser tolerante a fallos y asegura que no haya pérdidas de datos. Cuando existen varias réplicas disponibles, una de ellas es elegida como líder, y el resto como seguidoras, de modo que cuando están sincronizadas se marcan como ISR (In Sync Replica). Para que un topic se encuentre en buen estado, el valor del ISR debe ser igual al factor de replicación. 41 3.2.2.5 Productor Los productores son los clientes conectados a Kafka encargados de publicar mensajes en un bróker. Son los responsables de serializar, particionar, comprimir y repartir la carga entre los brokers en función de las particiones. La asignación de mensajes a topics puede realizarse con un método llamado round-robin o con alguna función semántica que determine la partición. Los mensajes están compuestos por una clave, el valor y una marca de tiempo asignada. 3.2.2.6 Consumidor Los consumidores de Kafka son los clientes conectados suscritos a los topics que consumen los mensajes. Cada consumidor tiene asociado un grupo de consumidores. 3.2.2.7 Bróker Kafka se ejecuta en un clúster de uno o más servidores llamados brokers y las particiones de todos los temas se distribuyen entre los distintos nodos del clúster. Además, las particiones se replican en varios brokers. Todo esto permite a Kafka entregar flujos masivos de mensajes de forma tolerante a fallos. 3.2.3 Interfaces de Kafka Hay cinco interfaces principales en Kafka: • Producer API: Permite a las aplicaciones enviar flujos de datos a los topics del clúster de Kafka. • Consumer API: Permite a las aplicaciones leer flujos de datos de los topics del clúster de Kafka. • Connector API: Interfaz de importación/exportación para la conexión con sistemas de terceros. • Streams API: Biblioteca Java para el procesamiento de flujos de datos, convirtiendo los flujos de entrada en salida y produciendo el resultado. • Admin API: Se utiliza para gestionar los temas de Kafka, los brokers y otros objetos. Las interfaces de consumidor y productor se basan en el protocolo de mensajes Kafka y están desacopladas de la funcionalidad principal del programa. El protocolo de mensajes real de Kafka es binario y, por tanto, permite que los clientes consumidores y productores se desarrollen en cualquier lenguaje de programación. Por ello, Kafka no está vinculado al ecosistema JVM. 3.2.4 Formato de los mensajes Los mensajes de Kafka son creados por el productor. Estos se identifican por una clave y un valor en formato binario y pueden ser nulos. [25] 42 En la imagen anterior también se puede apreciar como en el mensaje viaja el tipo de compresión utilizado por el productor para enviar todos los datos. El valor por defecto es none (sin compresión), aunque también admite los siguientes tipos: gzip, snappy, Iz4 y zstd. Por otro lado, existen unos encabezados opcionales de pares clave-valor utilizados para la agregación de metadatos en el mensaje. Por último, al mensaje se le agregará la partición, el offset y una marca de tiempo, establecida por el sistema o por el usuario. Como es natural, cuando se crea el mensaje, el usuario utilizará objetos de nivel superior para los campos clave-valor. Sin embargo, estos campos se envían como un flujo de bytes y para realizar esta transformación, Kafka proporciona serializadores (serializer) predeterminados, aunque también permite la posibilidad de implementar serializadores personalizados. 3.2.4.1 Serializer Debido a que los brokers de Kafka solo trabajan con bytes, este instrumento es utilizado para convertir objetos de alto nivel (ya sean cadenas de texto, enteros, etc) en flujos de bytes. Según el contenido que el productor vaya a enviar en los campos clave-valor del mensaje Kafka, se deberán modificar los atributos ‘key.serializer’ y ‘value.serializer’ en su configuración. Los valores posibles de Figura 3-15: Formato de los paquetes Kafka 43 estos atributos se corresponden con los tipos de datos java que se puede serializar, como: • IntegerSerializer • StringSerializer • FloatSerializer • BytesSerializer 3.2.4.2 Deserializer De igual forma, existe un deserializador en la parte del consumidor para transformar el flujo de bytes proveniente de los brokers en objetos legibles. Tal y como se hacía en el productor, hay que modificar los atributos ‘key.deserializer’ y ‘value.deserializer’ para obtener los datos de forma correcta. Algunos de los posibles valores para estos atributos son: • IntegerDeserializer • StringDeserializer • FloatDeserializer • BytesDeserializer Figura 3-16: Serializer Figura 3-17: Deserializer 44 Los pasos necesarios para la ejecución de Kafka se explican en el Anexo B. 3.2.5 Conexión Voice2JSON-Kafka Tal y como se explicará en el apartado 5.2, el servidor REST será el responsable de ejecutar los comandos necesarios para que Voice2JSON realice la transcripción del fichero de audio que se reciba. El resultado del proceso, compuesto principalmente por los campos “transcribe-seconds”, donde se indica el tiempo necesario para la transcripción, o “text”, donde se almacena el comando convertido, se escribirá en un fichero de texto simple. Sin embargo, para que el contenido de este fichero forme parte de un mensaje enviado a través de Kafka, se ha creado un shell script que puede considerarse como el productor responsable de enviar los paquetes que contienen los resultados de estas transcripciones. Un shell script es un archivo de texto que está creado con una serie de instrucciones que son ejecutadas línea a línea por un shell o intérprete de comandos (CLI) de Linux, especificado habitualmente en la primera línea del script. A continuación, se muestra su código: Como se puede apreciar en el script, llamado “enviar_script.sh”, el shell utilizado para la ejecución es el BASH, especificado mediante el shebang “#!”. En la variable ‘FICHERO’ se guarda la ubicación del archivo ‘fichero.txt’, donde se escribirá el resultado del proceso de transcripción ordenado por el servidor REST. Sin embargo, en este fichero, el servidor REST también escribirá las ordenes establecidas por el usuario sin voz. Estos procesos se explicarán en el apartado 5.2 cuando se detallen el funcionamiento de los métodos del servicio REST configurados. Posteriormente, cuando existe este fichero, mediante el comando ‘cat’ se obtiene su contenido y utilizando una tubería, el resultado de la anterior instrucción es enviada como el mensaje del productor Kafka que se ejecuta con el comando: ./bin/Kafka-console-producer.sh –bootstrap-server localhost:9092 –topic thing1 Como argumentos, se especifica la dirección del bróker donde se está ejecutando y el topic ‘thing1’ al que se dirige. Por último, una vez ejecutada esta instrucción, se elimina el fichero de texto y los dos audios (tanto Figura 3-18: Código enviar_script.sh 45 en formato 3gp como wav) con el objetivo de no generar errores al realizar posteriores transcripciones. La obtención de los archivos de audio se explicará en el apartado 5.2 y 5.3. 3.3 OpenHAB Open Home Automation Bus (OpenHAB) es una plataforma de automatización del hogar desarrollada en Java, independiente de la tecnología y de código abierto que se ejecuta como centro del control domótico. Algunas de las características fundamentales de OpenHAB son: • Su capacidad para integrar multitud de otros dispositivos y sistemas. • Proporcionar una interfaz de usuario uniforme y un enfoque común para las reglas de automatización en todo el sistema, independientemente de la cantidad de fabricantes y subsistemas involucrados. Es un software altamente modular que puede ser ampliado mediante add-ons o complementos. Estos brindan a OpenHAB una amplia gama de capacidades, desde interfaces de usuario hasta la posibilidad de interactuar con un gran número de dispositivos físicos (sensores, cámaras de video, altavoces, etc). Estos complementos pueden proceder de la distribución de OpenHAB o de otras fuentes externas. OpenHAB se puede configurar de dos maneras diferentes: a través de archivos de configuración o por la interfaz de usuario. A continuación, se detallan algunas ventajas e inconvenientes de usar una u otra forma: Ventajas de usar archivos de configuración: • En las versiones anteriores de OpenHAB, el uso de los archivos de configuración era lo habitual, por lo que en muchos ejemplos de la documentación y de los foros se utiliza este enfoque. • Con configuraciones basadas en archivos es más fácil realizar cambios y actualizaciones masivas, duplicar objetos similares y separar dominios en archivos distintos • Es más fácil compartir configuraciones entre múltiples instancias de OpenHAB. Inconvenientes de usar archivos de configuración: • Son más propensos a errores. El más mínimo error tipográfico produce un error de sintaxis que podría hacer que todo el contenido del archivo deje de ser válido y no sea reconocido. • La sintaxis, en ocasiones, puede resultar difícil de entender. Ventajas de usar la interfaz de usuario: • Facilidad de entendimiento. • Los things se pueden descubrir automáticamente. 46 Inconvenientes de usar la interfaz de usuario: • Complejidad al eliminar cosas obsoletas. • No todo se puede configurar aún a través de ella. 3.3.1 Componentes básicos de OpenHAB A continuación, se detallan los componentes básicos con los que trabaja OpenHAB: 3.3.1.1 Things Los things (cosas) son las entidades que se pueden agregar físicamente a un sistema. No tienen que ser dispositivos físicos, también pueden representar un servicio web o cualquier otra fuente manejable de información y funcionalidad. 3.3.1.1.1 Estados de los Things Cada thing tiene un estado que permite identificar posibles problemas con el dispositivo o servicio. Los posibles estados se recogen a continuación: Tabla 3-5: Estados de los things Estado Descripción UNINITIALIZED Es el estado inicial de un thing cuando se agrega o se inicia en el sistema. Este estado también se asigna si el proceso de inicialización falló o el enlace no está disponible. Los comandos enviados a los canales no se procesarán. 47 INITIALIZING Este estado se asigna mientras el enlace inicializa el thing. Depende del enlace cuánto tiempo lleva el proceso de inicialización. Los comandos enviados a los canales no se procesarán. UNKNOWN El controlador está inicializado, pero todavía se desconoce si el thing está ONLINE o OFFLINE. Es el propio controlador quién cambia el thing a ONLINE o OFFLINE. ONLINE El dispositivo/servicio representado por el thing funciona correctamente y puede procesar comandos. OFFLINE El dispositivo/servicio representado por el thing no funciona correctamente y es posible que no procese los comandos. REMOVING El dispositivo/servicio representado por el thing debe eliminarse, pero el enlace aún no ha confirmado la eliminación. REMOVED El dispositivo/servicio representado por el thing se eliminó del sistema. Las posibles transiciones entre cada uno de estos estados se representan en el siguiente diagrama: 54 4. Por último, al añadir el nuevo Item al canal llamado HTTP_URL_Thing_Limite Temperatura, se específica que el tipo de dato representado es de tipo “String”. • MQTT Binding: utilizado para enviar y recibir datos a la placa ESP32. El bróker MQTT empleado para la interconexión es el servidor público proporcionado por EMQX Cloud [30]. En la siguiente figura se muestra toda la configuración necesaria para ejecutar dicho servidor. Figura 3-23: Binding MQTT Figura 3-22: Configuración channel de HTTP URL Thing 55 Se especifica un ID de cliente generado automáticamente, la dirección ‘broker.emqx.io’ donde se aloja el servidor acompañado por el puerto 8083 (este puerto es utilizado para WebShocket, el puerto TCP es el 1883), además del usuario y contraseña ‘pruebaMQTT’ utilizado por los clientes para poder acceder. Por último, se indica que la versión del protocolo MQTT es la 3.1.1 y el nivel de QoS = 0 (en el apartado 4.1 se explicará el significado de este último parámetro). Para obtener los datos recibidos del servidor MQTT en OpenHAB, es necesario crear un thing que representa al propio bróker, además del empleado para recibir la información de un topic determinado. Su nombre será ‘MQTT Broker’ y los pasos necesarios para su configuración son: 1. Igual que el paso número 1 del binding HTTP para crear el thing representativo del bróker MQTT. 2. Se indican en los atributos correspondientes la dirección ‘broker.emqx.io’ y el puerto 1883 donde se ejecuta el servidor, la versión 3.1.1 de MQTT, el ID del cliente proporcionado en la configuración del servidor, el nivel QoS = 0 y las credenciales de usuario y contraseña anteriormente mencionadas. Todos los topics creados se explican en el apartado 4.1.4. Por último, los pasos para la configuración del thing que permite obtener la información de un topic determinado en OpenHAB, son: (se utilizará como ejemplo el thing llamado Sensor_Temperatura_DHT11) 1. Igual que el paso número 1 del binding HTTP. 2. Establecer la dirección del bróker al que se va a conectar enlazándolo con el thing ‘MQTT Broker’ creado anteriormente en el atributo ‘Parent Bridge’: Figura 3-24: Configuración Bróker MQTT 56 3. Al configurar el channel, se indica el topic ‘Sensor/DHT11/Temperatura’ para recibir la información de la temperatura enviada desde el bróker. 4. Al enlazarlo con el Item ‘SensorTemperatura_DHT11’ se especifica el tipo de valor recibido, en este caso de tipo String. • Xiaomi Wifi devices (Mi IO) Binding: utilizado para establecer la conexión con la bombilla Xiaomi. Los pasos necesarios para configurar el thing ‘Bombilla LED Xiaomi Prueba 1’ son: 1. Mismo paso que en los anteriores. 2. Especificar la dirección IP, el Token y el Device ID de la bombilla. Para conseguir estos datos, es necesario ejecutar en una terminal el programa de extractor de tokens mencionado en el apartado 2.2.8. Figura 3-26: Binding Xiaomi Figura 3-25: Enlace con el thing MQTT Broker Figura 3-27: Ejecución del programa extractor de tokens de dispositivos Xiaomi 57 Para obtener esta información, es necesario disponer de una cuenta en Xiaomi Cloud. 3. Crear un channel por cada Item que representa la funcionalidad de la bombilla, como puede ser su encendido/apagado, cambio de color, cambio de la intensidad luminosa, etc. 3.3.2 Reglas OpenHAB permite automatizar el control de los dispositivos físicos a través de las reglas. Estas se pueden componer con los siguientes bloques: Tabla 3-8: Estructura de una regla en OpenHAB Bloque Objetivo Trigger o evento Cuando ocurre el evento definido, se ejecuta la regla. (Opcional) Condition o condición Condición que debe cumplirse para que se ejecute la regla. (Opcional) Action o acción Sobre qué se debe actuar cuando se ejecute la regla De forma que la definición de una regla en OpenHAB siempre será del tipo: “Cuando ocurra (evento), si (condición), entonces haz (acción)”. 3.3.2.1 Triggers Como se ha mencionado anteriormente, los triggers definen aquellos eventos que, cuando ocurren, hacen que se ejecute la regla. Figura 3-28: Channels para el thing de la bombilla de Xiaomi 58 Las categorías de reglas que se pueden utilizar para activar una regla son: Tabla 3-9: Triggers Para los triggers basados en el tiempo, se pueden usar expresiones predefinidas para los temporizadores o usar expresiones cron [26], que son cadenas que están compuestas por otras seis (siete opcionalmente) subexpresiones para una mayor precisión. Estas subexpresiones se separan por espacios en blanco y representan, en el siguiente orden: Tabla 3-10: Subexpresiones Momento temporal Valores permitidos Segundos Del 0 al 59. Minutos Del 0 al 59. Horas Del 0 al 23. Días del mes Del 1 al 31 (dependiendo del mes). Mes Del 0 al 11 (siendo 0 el mes de enero) o utilizando las cadenas: JAN, FEB, MAR, APR, MAY, JUN, AUG, SEP, OCT, NOV, DEC. Días de la semana Del 1 al 7 (siendo 1 el domingo) o mediante las cadenas: SUN, MON, TUE, WED, THU, FRI, SAT. Año (Opcional) Del 1970 al 2099 Evento Descripción Items Comandos, actualizaciones y cambios en el estado de un item individual. Groups Los grupos son ítems especiales que tienen a otros ítems como miembros. Time Las reglas pueden activarse en momentos específicos. Channel Algunos things tienen canales que pueden desencadenar reglas directamente. Thing Cuando cambian o actualizan su estado. System Cuando ocurren eventos importantes internas de OpenHAB. 59 Las subexpresiones individuales pueden contener rangos y/o listas. Por ejemplo, en el campo día de la semana, se podría poner “MON-FRI”, “MON, WED, FRI” o “MON-WED, SAT”. Existen también una serie de comodines que permiten simplificar la sintaxis. Estos son: • El carácter ‘*’ indica todos los posibles valores para ese campo. Por ejemplo, en el campo día de la semana, un ‘*’ indicaría “todos los días de la semana”. • El carácter ‘/’ se puede utilizar para especificar incrementos en los valores. Por ejemplo, en el campo de los minutos, ‘0/15’ o ‘/15’ indicaría “cada 15 minutos de la hora, comenzando en el minuto 0”. ‘3/20’ indicaría “cada 20 minutos de la hora, comenzando en el minuto 3”. • El carácter ‘?’ se utiliza para los campos del día del mes y día de la semana. Este comodín indica “ningún valor específico”. Resulta útil cuando se necesita especificar algo en uno de los dos campos, pero no en el otro. • El carácter ‘L’ se utiliza también para los campos anteriormente mencionados. Indica “el último”, pero su uso tiene un significado distinto en cada campo. En el día del mes significa “el último día del mes”. En el correspondiente del día de la semana, indica “7” o “SAT” (sábado). Pero si se usa en este campo después de otro valor, indicaría “el último día (día) del mes”. Por ejemplo, “6L” o “FRIL” significaría “el último viernes del mes”. También se puede utilizar este comodín para especificar los días contando desde el último. Por ejemplo, “L-3” quiere decir “el penúltimo día”. • El carácter ‘W’ se utiliza para especificar los días de la semana (de lunes a viernes) más cercano al día dado. Por ejemplo, poner “15W” en el campo del día del mes significaría “el día de la semana más cercano al día 15 del mes”. • El carácter ‘#’ se utiliza para especificar “el enésimo” día de la semana del mes. Por ejemplo, el valor “6#3” o “FRI#3” en el campo del día de la semana indicaría “el tercer viernes del mes”. Para poder utilizar estas expresiones para la planificación temporal, OpenHAB se apoya en el framework Java de Quartz, que destaca por su potencia y sencillez. 3.3.2.2 Conditions Las condiciones pueden limitar cuándo se puede ejecutar una regla agregando una o varias de ellas. Existen distintos tipos de condiciones disponibles, como: • Cuando un item tiene un determinado estado. • Cuando es una hora determinada del día. • Cuando es un día determinado de la semana o es festivo, fin de semana, etc. • Cuando se evalúa la condición de un script. La condición de script permite elegir uno de los muchos lenguajes de script/reglas que dispone OpenHAB para crear cualquier condición, con la restricción de que la última línea de código debe ser el resultado de un valor booleano (true o false). 60 3.3.2.3 Actions Las acciones definen los elementos sobre los que se actúan cuando se cumple una regla. Estas acciones pueden ser: • Habilitar o deshabilitar otras reglas • Ejecutar otras reglas • Enviar comandos • Actualizar el estado de un ítem • Reproducir algún sonido • Ejecutar un script 3.3.2.4 Reglas programadas En el proyecto se han creado diferentes reglas para la automatización del sistema, todas ellas basadas en scripts. Para su programación, se ha utilizado el complemento denominado JavaScript Scripting: Este add-on proporciona soporte para JavaScript (ECMAScript 2022+) y además incluye openhabjs, una biblioteca para apoyar la automatización en OpenHAB. Proporciona un cómodo acceso a la funcionalidad común de OpenHAB dentro de las reglas, incluyendo items, things, acciones, etc. Figura 3-29: Binding JavaScript 61 Existen cuatro reglas utilizadas para el envío de los datos procedentes de los sensores al servidor REST cuando los ítems encargados de almacenarlos cambian de estado. Observando como ejemplo la regla para el envío del nivel de luminosidad, su sintaxis es la siguiente: [Cuando cambie el estado del sensor de la luminosidad, entonces ejecuta el script llamado “EnviarDatoLuminosidad”]. Su configuración se muestra a continuación: Como se puede apreciar, no se establece ninguna condición dentro de la regla. A continuación, se muestra el código del script que se ejecuta cuando se cumple la regla: Este almacena en una variable el estado del item que recoge los datos del sensor y mediante el método ‘sendHttpGetRequest’, los envían a la dirección URL especificada. Otra regla es utilizada para el establecimiento de límites ambientales. Por ejemplo, cuando el usuario establezca un límite de temperatura para el invernadero de unos 21 ºC, y el sensor DHT11 recoja una temperatura ambiental de 25ºC, OpenHAB encenderá automáticamente mediante esta regla el aire acondicionado para que las condiciones se ajusten a las establecidas desde la aplicación móvil. Su configuración es: Figura 3-30: Regla OpenHAB para el envío de la luminosidad Figura 3-31: Código de la primera regla OpenHAB 62 Igual que en la regla anterior no se establecen límites (ya que éstos se comprueban directamente en el script). La configuración del script se muestra a continuación: Figura 3-32: Regla OpenHAB para comprobar la temperatura del invernadero Figura 3-33: Código de la regla OpenHAB encargada de comprobar la temperatura y humedad 63 En este script se recogen en variables los límites de temperatura y humedad establecidos por el usuario y las condiciones ambientales actuales. Posteriormente, se enviará una señal de ON/OFF a los ítems que representan el aire acondicionado y el humidificador, dependiendo de si se cumplen las condiciones previstas para el invernadero. 3.3.3 Persistencia Aunque este módulo de OpenHAB no se va a implementar finalmente en el proyecto, sí que se ha trabajado y estuvo funcionando conjuntamente con la base de datos, y por ello se procederá a explicar brevemente su funcionalidad y configuración. La decisión de no utilizarlo fue porque se consideró que para evitar problemas de inserción y aumentar la eficiencia de las consultas a la base de datos, ésta se gestionará únicamente desde el propio servidor REST y no desde este y el servidor OpenHAB. OpenHAB permite almacenar datos a lo largo del tiempo mediante este módulo. Para ello se puede utilizar el servicio de persistencia que viene por defecto instalado llamado rrd4j ó utilizar otros complementos dedicados a este propósito. Entre todos los posibles servicios adicionales, se escogió el complemento llamado JDBC Persistence PostgreSQL: Este servicio escribe y lee estados de ítems desde y hacia una serie de sistemas de base de datos relacionales que admiten Java Database Connectivity (JDBC). Para configurarlo, es necesario establecer este servicio de persistencia como predeterminado para OpenHAB y editando el fichero services/runtime.cfg, dentro de la carpeta raíz, donde se ha instalado OpenHAB, y añadir la siguiente línea: Posteriormente, en otro fichero llamado services/jdbc.cfg, se indicarán todas las propiedades necesarias para la configuración del complemento para la persistencia, como la dirección URL de la base de datos en la que se almacenarán los datos, su usuario y contraseña si hubiese, etc. Un ejemplo de esta configuración, para una base de datos llamada openhabdb, sería: Figura 3-34: Binding PostgreSQL Figura 3-35: Selección persistencia tipo jdbc Figura 3-36: Configuración acceso base de datos 70 71 4 COMUNICACIÓN MQTT ENTRE OPENHAB Y LA PLACA ESP32 “En principio, la investigación necesita más cabezas que medios” Severo Ochoa En este capítulo se explicará cómo se realiza la gestión y recogida de datos de actuadores y sensores utilizando Arduino. La conexión entre OpenHAB y la placa ESP32 mediante el protocolo MQTT permitirá el envío de estos datos a través de los distintos topics configurados, clasificando el flujo de información que se envíe. 4.1 MQTT MQTT (acrónimo de Message Queuing Telemetry Transport) es un protocolo de red ligero, de tipo publicador/suscriptor, M2M para el servicio de colas de mensajes. Está diseñado para conexiones con ubicaciones remotas que tienen dispositivos con restricciones de recursos o ancho de banda limitado. Se ejecuta sobre un protocolo de transporte que proporcione conexiones ordenadas, sin pérdidas y bidireccionales, generalmente TCP/IP. La versión de uso común es la 3.1.1, aunque existe la versión 5.0. 4.1.1 Componentes básicos de MQTT Los elementos básicos MQTT son: 4.1.1.1 Conexión de red Construcción proporcionada por el protocolo de transporte subyacente que está siendo utilizado por MQTT. Permite la conexión entre el cliente y el servidor a través de un medio apto para enviar un flujo ordenado de datos, sin pérdidas y en ambas direcciones. 4.1.1.2 Mensaje de aplicación Son los datos transportados por el protocolo MQTT a través de la red para la aplicación. Cuando los mensajes de aplicación son transportados por MQTT, estos tienen asociados un QoS (calidad de 72 servicio) y un nombre de topic. 4.1.1.3 Cliente Un programa o dispositivo que utiliza MQTT. Siempre establece la conexión de red con el servidor. Es capaz de: • Publicar mensajes de aplicación que puedan interesar a otros clientes. • Suscribirse a topics para solicitar mensajes de aplicación. • Darse de baja para eliminar una solicitud de mensajes de aplicación. • Desconectarse del servidor. 4.1.1.4 Servidor o Bróker Programa o dispositivo que actúa como intermediario entre aquellos clientes que publican mensajes de aplicación y aquellos que han realizado suscripciones. Es capaz de: • Aceptar conexiones de red de clientes. • Aceptar mensajes de aplicación publicados por clientes. • Procesar las solicitudes de suscripción y cancelación de suscripción de los clientes. • Reenviar los mensajes de aplicación que coinciden con las suscripciones de los clientes. 4.1.1.5 Suscripción Una suscripción comprende un filtro de topics y una QoS máxima. Está asociada a una única sesión y una única sesión puede contener más de una suscripción. 4.1.1.6 Nombre de topic Es una etiqueta adjunta a un mensaje de aplicación que se utiliza para relacionarlo con las suscripciones conocidas por el servidor. El servidor envía una copia del mensaje a cada cliente que tenga una suscripción coincidente. 4.1.1.7 Filtro de topics Se trata de una expresión contenida en una suscripción, para indicar un interés en uno o más topics. 4.1.1.8 Sesión Se establece cuando hay una interacción entre un cliente y un servidor. Estas pueden permanecer en función del tiempo de existencia de una conexión de red o varias si están consecutivamente establecidas entre el cliente y el servidor. 73 4.1.1.9 Paquete de control MQTT Paquete de información que se envía a través de la conexión de red. La especificación MQTT define catorce tipos diferentes de estos paquetes. 4.1.2 Estructura de un Paquete de Control MQTT El protocolo MQTT funciona mediante el intercambio de una serie de paquetes de control. Estos paquetes constan de hasta tres partes, siempre en el siguiente orden: 4.1.2.1 Cabecera fija Cada paquete de control MQTT contiene una cabecera fija. Su formato es: 4.1.2.1.1 Tipos de Paquetes de Control MQTT La identificación del tipo del paquete se encuentra en el primer byte, ocupando los bits 7-4. Se representa como un valor sin signo de 4 bits. Estos tipos son: Figura 4-1: Estructura paquete de control MQTT Figura 4-2: Cabecera fija 74 Tabla 4-1: Tipos de paquetes de control MQTT Nombre Valor Sentido del flujo Descripción Reserved 0 Prohibido Reservado CONNECT 1 C → S Petición del cliente para conectarse con el servidor. CONNACK 2 S → C Acuse de recibo de conexión. PUBLISH 3 C → S, S → C Publicar mensaje. PUBACK 4 C → S, S → C Acuse de recibo de publicación. PUBREC 5 C → S, S → C Publish recibido (parte 1). PUBREL 6 C → S, S → C Publish recibido (parte 2). PUBCOMP 7 C → S, S → C Publish recibido (parte 3). SUBSCRIBE 8 C → S Petición de suscripción del cliente. SUBACK 9 S → C Acuse de recibo de suscripción. UNSUBSCRIBE 10 C → S Petición de desinscripción. UNSUBACK 11 S → C Acuse de recibo de desinscripción. PINGREQ 12 C → S Petición PING. PINGRESP 13 S → C Respuesta PING. DISCONNECT 14 C → S Cliente desconectándose. Reserved 15 Prohibido Reservado 75 Siendo C → S de Cliente a Servidor y C → S de Servidor a Cliente. En el apartado 4.1.2.4 se describen con mayor profundidad estos tipos de mensajes. 4.1.2.1.2 Flags Los bits restantes [3-0] del primer byte en la cabecera fija contienen banderas específicas para cada tipo de paquete de control MQTT. Tabla 4-2: Flags de los paquetes de control MQTT Paquete de control Flags de la cabecera fija Bit 3 Bit 2 Bit 1 Bit 0 CONNECT Reserved 0 0 0 0 CONNACK Reserved 0 0 0 0 PUBLISH Usado en MQTT 3.1.1 DUP QoS QoS RETAIN PUBACK Reserved 0 0 0 0 PUBREC Reserved 0 0 0 0 PUBREL Reserved 0 0 1 0 PUBCOMP Reserved 0 0 0 0 SUBSCRIBE Reserved 0 0 1 0 SUBACK Reserved 0 0 0 0 UNSUBSCRIBE Reserved 0 0 1 0 UNSUBACK Reserved 0 0 0 0 PINGREQ Reserved 0 0 0 0 PINGRESP Reserved 0 0 0 0 DISCONNECT Reserved 0 0 0 0 • DUP → Entrega duplicada de un paquete de control PUBLISH. • RETAIN → Bandera de retención del paquete de control PUBLISH. • QoS → Calidad de servicio del paquete de control PUBLISH. Existen tres niveles de calidad de servicio que permiten elegir entre minimizar la transmisión de datos y maximizar la fiabilidad: 76 • QoS 0: El mensaje se entrega según las capacidades de la red subyacente. El receptor no envía ninguna respuesta y el emisor no realiza ningún reintento, por lo que el mensaje llega al receptor una vez o no llega. Conocida como “Una entrega como máximo”. • QoS 1: Esta calidad de servicio garantiza que el mensaje llegue al receptor al menos una vez. Un paquete con dicho QoS tiene un Identificador de paquete en su cabecera variable y es reconocido por un paquete PUBACK. Conocida como “Entregado al menos una vez”. • QoS 2: Se trata de la calidad de servicio más alta, para cuando no son aceptables ni la pérdida ni la duplicación de mensajes. Conocida como “Entregado exactamente una vez” Figura 4-3: Esquema QoS=0 Figura 4-4: Estructura QoS=1 77 4.1.2.1.3 Longitud restante La longitud restante es el número de bytes que quedan en el paquete, incluidos los datos de la cabecera variable más los de la carga útil. No se tiene en cuenta los bytes utilizados para codificarla. 4.1.2.2 Cabecera variable Como se comentó anteriormente, algunos tipos de paquetes de control MQTT contienen esta cabecera con información adicional, ubicada entre la cabecera fija y la carga útil. Su contenido varía en función del tipo de paquete, aunque el campo ID del paquete puede ser común entre varios de ellos. Este ID se asigna de forma independiente entre cliente y servidor. Los paquetes que tienen este campo son: PUBLISH (si QoS es > 0), PUBACK, PUBREC, PUBREL, PUBCOMP, SUBSCRIBE, SUBACK, UNSUBSCRIBE y UNSUBACK. En los casos en los que el paquete sea de tipo SUBSCRIBE, UNSUBSCRIBE y PUBLISH (con QoS > 0) los paquetes de control deben tener un ID distinto de 0. Cada vez que el cliente envíe uno de estos tipos de mensajes, deberá asignarle un ID nuevo, y en caso de reenvío, se deberá utilizar el mismo identificador en todos los reenvíos posteriores. Los paquetes PUBACK, PUBREC y PUBREL tienen el mismo ID de paquete que el del paquete PUBLISH enviado. De igual forma, los paquetes SUBACK y UNSUBACK deben tener el mismo que los paquetes SUSBCRIBE y UNSUBSCRIBE enviados respectivamente. 4.1.2.3 Carga útil Algunos paquetes de control MQTT contienen una carga útil como parte final de su estructura en el que se transporta el contenido real del mensaje. En el caso del paquete PUBLISH se corresponde con el mensaje de aplicación. Figura 4-5: Esquema QoS=2 78 Tabla 4-3: Carga útil de los paquetes de control MQTT Paquete de control Carga útil CONNECT Requerido CONNACK Ninguno PUBLISH Opcional PUBACK Ninguno PUBREC Ninguno PUBREL Ninguno PUBCOMP Ninguno SUBSCRIBE Requerido SUBACK Requerido UNSUBSCRIBE Requerido UNSUBACK Ninguno PINGREQ Ninguno PINGRESP Ninguno DISCONNECT Ninguno 4.1.3 Paquetes de control A pesar de los catorce tipos diferentes de paquetes que existen, solo cuatro de ellos son necesarios para la conexión/comunicación/desconexión entre cliente y servidor (CONNECT, PUBLISH, SUBSCRIBE y UNSUBSCRIBE). El resto son utilizados principalmente para mecanismos internos y gestión del flujo de mensajes. 4.1.3.1.1 CONNECT Es el primer mensaje enviado desde el cliente al servidor una vez establecida la conexión de red. El cliente puede enviar un solo paquete de este tipo y si el servidor recibe más de uno, considerará que ha ocurrido una violación del protocolo y procederá a la desconexión del cliente. La carga útil contiene uno o varios campos codificados que especifican un ID del cliente, un topic, un mensaje, que define el mensaje de aplicación que se publicará en el topic, un nombre y una contraseña. Todos estos campos, a excepción del ID del cliente son opcionales y se determinan en función de los 79 flags presentes en la cabecera variable. 4.1.3.1.2 CONNACK Es el primer paquete enviado por el servidor en respuesta a un paquete CONNECT recibido por un cliente. Si el cliente no recibe este paquete de respuesta en un tiempo determinado, que dependerá del tipo de aplicación y la infraestructura de comunicación, cerrará la conexión de red. 4.1.3.1.3 PUBLISH El paquete de control PUBLISH se envía de un cliente a un servidor o de un servidor a un cliente para transportar un mensaje de aplicación. 4.1.3.1.4 PUBACK El paquete PUBACK se envía como respuesta a un paquete PUBLISH con QoS de nivel 1. 4.1.3.1.5 PUBREC El paquete PUBREC se envía como respuesta a un paquete PUBLISH con QoS nivel 2. Es el segundo paquete intercambiado cuando QoS = 2. 4.1.3.1.6 PUBREL El paquete PUBREL se envía como respuesta a un paquete PUBREC. Es el tercer paquete intercambiado cuando QoS = 2. 4.1.3.1.7 PUBCOMP El paquete PUBCOMP se envía como respuesta a un paquete PUBREL. Es el cuarto y último paquete intercambiado cuando QoS = 2. 4.1.3.1.8 SUBSCRIBE Este paquete se envía desde el cliente al servidor para crear una o más suscripciones que registran el interés que se tiene por uno o varios topics. El servidor envía paquetes PUBLISH al cliente para reenviar mensajes de aplicación publicados en topics coincidentes. El paquete SUBSCRIBE especifica también la calidad de servicio máxima con la que el servidor puede enviar mensajes de aplicación. 4.1.3.1.9 SUBACK El servidor envía un paquete SUBACK al cliente para confirmar la recepción y el procesamiento de un paquete SUBSCRIBE. 86 de los que se quieren recibir mensajes. • Bloque de operación: este bloque, que se corresponde con la función loop() de Arduino, es el encargado de recoger periódicamente toda la información que envían los sensores a la placa ESP32 y este de enviarlo a los topics del que es el publicador. La recogida de datos está programada de forma que se obtengan de cada sensor en un periodo de tiempo distinto. Para conseguirlo, inicialmente se pensó en que la obtención de estos datos se podría hacer en diferentes hilos, de forma que cada uno se encargara de un sensor distinto y posteriormente enviar la información al bróker MQTT. Sin embargo, tal y como se comentó al principio, la placa ESP32 posee un microcontrolador, el cual posee un único núcleo. Esto implica que solo es capaz de ejecutar una instrucción cada cierto tiempo, por lo que la multitarea, es decir, la ejecución en varios hilos a la vez, no es posible. Tampoco sería posible utilizar la función delay(), ya que ésta retrasaría la ejecución de todo el código. La solución adoptada fue el uso de la función de temporización millis() que cuenta el tiempo y ejecuta una operación cuando el temporizador se dispare. De esta forma se consigue realizar varias tareas a la vez. A modo de ejemplo, se explicará el funcionamiento de la recogida de los datos para el sensor de luminosidad, siendo igual para el resto. Cuando se inicia la ejecución, se crea la variable ‘tiempoInicioLuz’ que almacena el tiempo que lleva en funcionamiento el programa mediante la llamada función millis(). En el momento en el que comienza la ejecución del bloque de operación, se inicia la variable ‘tiempoTrancurridoLuz’ utilizando la misma función. Posteriormente, se comprueba si el tiempo transcurrido ejecutándose el código es superior o no al tiempo desde su inicio más un tiempo concreto (2 segundos), es decir, se verifica si el programa se ha estado ejecutando durante un periodo de tiempo. Cuando este periodo es superado, se procede a: ▪ Inicializar de nuevo la variable ‘tiempoInicioLuz’ para que almacene el nuevo tiempo de ejecución del programa. Figura 4-10: Conexión servidor MQTT y asociación con la función callback() Figura 4-11: Subscripción a los topics 87 ▪ Leer el pin correspondiente, convertir el valor obtenido al tipo de dato char y enviarlo al bróker a través del topic adecuado, utilizando la función ’publish(const char *topic, const char *info)’. El periodo de recogida y envío de los datos obtenidos de cada sensor se muestra en la siguiente tabla: Tabla 4-6: Periodos de muestreo Sensor Periodo (min) Luminosidad 30 Humedad Terrestre 10 Nivel Agua 10 Humedad y Temp. Ambiental (DHT11) 30 Los pasos realizados para la instalación de Arduino, así como de las librerías necesarias para poder configurar la placa ESP32 a través del IDE se explican en el Anexo C. Figura 4-12: Configuración fotorresistencia 88 89 5 DESCRIPCIÓN DEL ALMACENAMIENTO DE DATOS Y APLICACIÓN ANDROID “La conectividad es un derecho humano” Mark Zuckerberg En este capítulo se explicará el desarrollo y la configuración de la base de datos Postgresql utilizada para el almacenamiento de la información, abordando posteriormente la programación del servidor REST, elemento de gran importancia para el funcionamiento del proyecto. Por último, se hará una descripción profunda sobre la aplicación móvil empleada por el usuario final para la gestión de todo el invernadero. 5.1 Base de datos PostgreSQL PostgreSQL es un sistema de base de datos relacional de objetos de código abierto con más de treinta y cinco años de desarrollo. Destaca por su capacidad de adaptación y cumplimiento con el estándar SQL, así como la gestión de grandes volúmenes de datos. Entre sus funciones principales destacan: • Almacenamiento eficiente de datos: organiza y almacena datos garantizando su integridad y consistencia. • Consultas avanzadas: permite realizar consultas complejas y analizar datos para obtener información valiosa con lenguaje SQL. • Backend confiable para aplicaciones: empleado principalmente en el desarrollo de aplicaciones web y móviles, pues ofrece un almacenamiento seguro y escalable para los datos de la aplicación. La base de datos se ha empleado en el proyecto para el almacenamiento de información de interés. Para ello, se creó una base de datos, llamada ‘openhabdb’, localmente en la máquina virtual ejecutándose en el puerto 5432 con usuario y contraseña ‘openhab’. Las tablas creadas y su sintaxis se muestran a continuación: 90 • item0001, item0002, item0010, item0011, item0012: estas tablas almacenan los datos procedentes de los sensores de temperatura, humedad ambiental, luminosidad, nivel de agua y humedad terrestre respectivamente. La sintaxis es similar para todas, con una columna “time” para almacenar la fecha en la que se recibe el dato y la columna “value” que almacena el valor: Como se puede observar, la clave primaria está establecida en la columna “time”. • Limites: esta tabla almacena todos los limites ambientales establecidos en el invernadero por el usuario y que son imprescindibles para la configuración de las reglas en OpenHAB. En ella, los datos relacionados con la temperatura, humedad ambiental y terrestre se almacenan en doble precisión, mientras que los que provienen del sensor de luminosidad se almacenan como enteros. La clave primaria de esta tabla está establecida en la columna “limite_temperatura”, aunque puede ser cualquier otra. • Usuarios: en esta tabla se almacenan las credenciales de los usuarios que tienen acceso a la aplicación del invernadero. Cada columna, de tipo text, almacenará el nombre del usuario y su contraseña respectivamente. Figura 4-1: Tabla item0001 Figura 5-2: Tabla limites Figura 5-3: Tabla usuarios 91 La clave primaria de la tabla se corresponde con la columna usuario. • Motoragua: esta tabla es utilizada para almacenar el tiempo de inicio de funcionamiento del motor y la cantidad de agua consumida en las columnas “time” y “value” respectivamente. Se explicará su uso con más profundidad en el siguiente apartado 5.2: Servidor REST. La configuración de la base de datos se explica en el Anexo D. 5.2 Servidor REST Para poder atender a todas las peticiones que el cliente realice a través de la aplicación móvil y para gestionar la base de datos se ha configurado un servidor REST. Este servidor está implementado mediante el framework de Spring y permite ser la puerta de entrada a la gestión y monitorización de todo el invernadero. El servidor REST tiene como posibles clientes tanto el usuario final, a través de la aplicación móvil, como al servidor OpenHAB y se ejecuta localmente en la máquina virtual con dirección IP 192.168.1.62 y en el puerto 9000. Además, es el responsable de establecer la conexión con la base de datos PostgreSQL, de modo que cualquier operación de inserción, eliminación o modificación de alguna de sus tablas es realizada por él. Junto con el directorio donde se encuentra el archivo que contiene la función ‘main()’ para la ejecución del servidor, existen otros en los que se codifica la funcionalidad del mismo. Estos son: • controller: en esta carpeta se encuentra el archivo ‘FileController’, donde se definen las URLs de las operaciones que podrá realizar el servidor. Además, se encuentran otras clases utilizadas por este archivo las cuales, como se explicará posteriormente, permiten la definición de variables dentro de una petición. • service: se encuentran la interfaz ‘apiRest_ServidorAPI’, donde se definen los métodos de las operaciones que puede atender el servidor y la clase ‘apiRest_ServidorImpl’, donde se implementan estos métodos. Además, aquí se ubica la clase ‘ConexionBBDD’, que es la encargada de establecer la conexión con la base de datos mediante la carga del driver correspondiente, especificando la URL donde se aloja y añadiendo el usuario y la contraseña para el acceso a sus tablas. A continuación, se explicarán todos los tipos de métodos empleados para dar respuesta a las distintas peticiones provenientes de los clientes: - Método “subir”: Este método es llamado por la operación “/audio” de tipo POST y es el encargado de transformar el audio que el cliente envía en formato 3gp a través de la app utilizando la herramienta ffmpeg de línea de comandos y obtener un wav. Posteriormente llama a voice2json para que realice la transcripción. Este método no devuelve nada. El fichero de audio se envía desde la aplicación móvil, con un determinado nombre, a una ubicación específica del servidor. La herramienta ffmpeg es un convertidor de medios universal, pudiendo leer una amplia variedad de entradas, tales como de video, audio, archivos adjuntos, etc, para filtrarlas y transcodificarlas en diferentes formatos a la salida. 92 De forma resumida, el funcionamiento de esta herramienta se muestra en la siguiente figura: Ffmpeg llama a una biblioteca incorporada llamada libavformat (que contiene los multiplexores y demultiplexores) para leer los archivos de entrada y obtener de ellos paquetes que contienen los datos codificados. Estos son pasados por el decodificador produciendo frames sin comprimir (videos en raw, audio PCM, etc). Posteriormente, los frames son pasados por el codificador para convertirlos en paquetes codificados. Por último, se pasan al multiplexor para escribir estos paquetes en el archivo de salida. Para transformar el audio que se recibe de la aplicación móvil, en formato 3gpp, a un formato wav, que es el reconocido por Voice2JSON, se utilizan dos argumentos: ▪ -i: Indica la ubicación del archivo de audio que se quiere convertir. ▪ -acodec: utilizado para especificar el códec de audio, permitiendo codificar y decodificar los datos auditivos en un formato determinado. Entre todos los códecs disponibles de esta herramienta, el utilizado para la conversión es el PCM signed 16-bit Little-endian (pcm_s16le). PCM es un método de codificación sin compresión que permite obtener el formato de audio wav sin perdidas. Esta herramienta es ejecutada desde código Java mediante el uso de la librería ‘Runtime’, que permite a la aplicación interactuar con el entorno en el que se encuentra. Una vez convertido el fichero de audio a formato wav, se procede a realizar la transcripción utilizando Figura 5-4: Esquema ffmpeg Figura 5-5: Configuración de ffmpeg 93 Voice2JSON. Para ello, se utiliza la clase ‘PocessBuilder’, que permite ejecutar nuevos procesos del sistema operativo desde el código Java. El resultado de esta operación se imprime en un archivo de texto que posteriormente será el mensaje enviado a través de Kafka, tal y como se explicó en el apartado 3.2.5. - Método “consulta”: Este método es llamado por la operación “/consutla” de tipo GET y es utilizado para obtener los datos almacenados en la base de datos desde una fecha determinada para su representación en la aplicación móvil en forma de gráfico. Devuelve una lista de tipo String. La consulta a la base de datos para obtener una serie de datos almacenados a partir de una fecha se realiza mediante la consulta: “SELECT * FROM ‘nombre_tabla’ WHERE time > ‘fecha_consulta’”. Tanto ‘nombre_tabla’ como ‘fecha_consulta’ son dos variables que se envían desde la aplicación móvil junto con la URL, ya que la operación es de tipo GET. El parámetro que viaja en la URL identificando la fecha de la consulta puede tomar únicamente tres valores: • A partir de 1 día • A partir de 3 días • A partir de 7 días De esta forma se le permite al usuario la posibilidad de comprobar la evolución del invernadero en distintos tramos temporales. Como se explicó en el apartado de la base de datos PostgreSQL, las tablas almacenan también las fechas en las que se reciben los datos de los sensores. Estas, tienen el formato “YY/MM/DD HH/MM/SS”. Para obtener una variable en este formato y utilizarla en la consulta anterior, se utiliza la clase Calendar de Java. Esta clase permite calcular de forma automática la fecha de hace uno, tres o siete días respecto a la fecha actual, en la que se ejecuta el código. En el código que se muestra a continuación, se calcula la fecha según lo solicitado por el usuario, restando a la fecha actual un día (para el resto de las opciones solo se tendría que cambiar este número): Figura 5-6: Configuración para realizar la transcripción Figura 5-7: Configuración para obtener las fechas 94 Posteriormente, a partir de la fecha resultante, se obtiene el día, el mes y el año almacenándolas en variables para posteriormente, obtener otra variable llamada “f’echaConsulta’ juntando las anteriores y que será utilizada en la consulta. Finalmente se realiza la petición a la base de datos, se obtienen los datos y se almacenan en una lista que será enviada al cliente. Para que el servidor no envíe todos los datos almacenados en la base de datos, se realiza una serie de operaciones para que el resultado sea una lista con nueve datos y nueve fechas. Lo que se busca es que cada punto que se vaya a mostrar en la gráfica de la aplicación Android se corresponda con la media de un conjunto de datos almacenados en una lista. De tal forma, los datos recibidos se organizan en nueve arrays. Dependiendo desde cuando se soliciten los datos, las operaciones realizadas serán distintas: - A partir de 1 día En una lista se almacenan todos los datos almacenados desde este tiempo en la base de datos, y en otra, se almacenan las fechas de cuando fueron guardados ejecutando la sentencia SQL mostrada anteriormente. Una vez terminada la consulta, se cuenta el número total de datos recibidos y se divide entre nueve (ya que nueve son los puntos que se mostrarán en las gráficas de la aplicación). De esta forma, el cociente de esta división, indica el número de elementos que deben tener los nueve arrays y, con ello, realizar la media de cada uno de ellos para que sean representadas en las gráficas. - A partir de 3 días En este caso, se obtienen los datos almacenados correspondientes para uno de los tres días y cada conjunto se divide entre tres, de forma que, se obtendrán tres arrays, y tres medias correspondientes a cada día. - A partir de 7 días Con esta opción, se obtienen dos conjuntos de datos para el sexto y séptimo día (obteniéndose dos medias) y para el resto de días, se obtiene un único conjunto, de forma que se consiguen las cinco medias restantes. - Método “iniciarSesion”: Método llamado por la operación “/sesion” de tipo GET, utilizado para comprobar si el cliente que intenta acceder a la aplicación con el usuario y contraseña introducidos son válidos. Devuelve un booleano, indicando si está permitido o no su acceso. Para ello, se realiza la consulta siguiente a la tabla usuarios donde se almacenan los usuarios y contraseñas de cada uno de los clientes autorizados para acceder a la aplicación: “SELECT * FROM usuarios WHERE usuario = ‘nombre_usuario’”. La variable ‘nombre_usuario’, junto con la contraseña introducida por el usuario, se envía como argumento en la URL. Posteriormente, se comprueba si estos parámetros coinciden con los obtenidos de la base de datos, devolviendo true o false cuando 95 corresponda - Método “ActualizarLimites”: Este método es llamado por la operación “/valoresLimite” de tipo PUT, empleado para actualizar los valores límites establecidos por el usuario. Estos valores son utilizados por OpenHAB en sus reglas, explicadas en el capítulo anterior. Devuelve un booleano, indicando si se han actualizado todos los valores correctamente o no. Para actualizar estos datos, se realiza la siguiente consulta de actualización a la base de datos: “UPDATE limites SET limite_temperatura = ‘valor_temperatura’, limite_humedad = ‘valor_humedad’, limite_luz = ‘valor_luminosidad’, limite_humedadterrestre = ‘valor_humedadTerrestre’”. Todos estos valores personalizados por el usuario son enviados en el cuerpo del paquete que se envía al servidor. Para obtenerlos, en la definición de la operación, se utiliza la clase personalizada ‘Limites’, la cual contiene una serie de getters/setters que permiten adquirir los datos del cuerpo del mensaje. La definición de la clase Limites: Figura 5-8: Comprobación de credenciales Figura 5-9: Definición de /valoresLimite 102 Esta actividad no está pensada para que el usuario gestione el sistema desde ella. Para ello, existe otra llamada “Gestión” que permite, no solo obtener los valores límites ambientales establecidos actualmente en el invernadero, sino que, además, admite la posibilidad de establecer unos límites personalizados según las condiciones óptimas de cada cultivo, entre otras funcionalidades. Para manejarse entre todas las actividades, existe un menú ubicado en la esquina superior izquierda, accesible en cualquier momento de ejecución de la aplicación una vez iniciada sesión con el servidor. Figura 5-18: Actividad Inicio 103 Como se puede observar, desde la actividad “Gestión” se envían las ordenes al servidor para que estas sean atendidas. Estas órdenes se pueden decidir mediante dos formas: • Utilizando una orden por defecto, mediante el campo de escritura situado en la parte superior de la actividad. Cada vez que se empiece a escribir una orden, aparecerá una lista con todos los comandos disponibles que mejor coincidan con las letras/palabras escritas en este campo. Se muestra un ejemplo a continuación: • Utilizando el micrófono del dispositivo móvil, para enviar un comando por voz. Esta forma se realiza pulsando sobre el micrófono de color negro situado en la esquina superior derecha. Al pulsarlo, este icono se coloreará en rojo, indicando el comienzo de la grabación. El micrófono Figura 5-19: Menú desplegable Figura 5-20: Actividad Gestión Figura 5-21: Comandos predefinidos 104 ubicado a la izquierda del anterior, permite escuchar el comando de voz una vez terminada la grabación. El envío tanto del comando de voz como del escrito se realiza a través de una petición POST al servidor para que sean tratados de la manera oportuna, según se explicó en el apartado 5.2. Sin embargo, la forma empleada para enviar el comando de voz es distinta a la utilizada en el resto de las peticiones POST. Para el envío de los audios, se hace uso de la librería ‘Retrofit’. Esta permite crear un cliente REST seguro para Android, que se encarga de la serialización, deserialización, concurrencia y manejo de las respuestas. Se trata de una alternativa a ‘HttpUrlConnection’ y ‘Volley’, utilizada porque permite una gran simplificación de código además de poder realizar el envío de estos archivos de audio de forma sencilla. Para su uso, es necesario implementar las dependencias necesarias en app/build.gradle: Debido a que la aplicación necesita conectarse a internet para su funcionamiento, es necesario concederle este permiso. Para ello, se le solicita al usuario, mediante una ventana emergente, el uso de internet, además de acceso al almacenamiento interno del dispositivo, utilizado para conservar el fichero de audio grabado. Para la construcción del mensaje, se crea un objecto de la clase ‘Retrofit’ al que se le indica la URL del servidor, y a la que se le asocia una interfaz java que contiene la estructura de los posibles tipos de peticiones. En dicha interfaz, debido a que solo se usa esta librería para enviar el comando por voz, solo se específica un método para una operación de tipo POST. En el cuerpo del mensaje se incluye el fichero de audio grabado que ha sido almacenado en el dispositivo y seleccionando el anterior método encargado de la petición, es enviado al servidor. Al mandarlo, esta librería también permite tanto gestionar las respuestas recibidas como tratar los fallos que pudieran aparecer. La otra forma utilizada para el envío de comandos en texto plano se realiza a través de la librería Volley. Los mensajes se construyen indicando la URL del servidor, el tipo de método HTTP (POST) y el cuerpo del mensaje que contiene el comando. Esta librería, al mandar la petición, también permite gestionar tanto las respuestas recibidas como los fallos ocasionados durante la transmisión. Figura 5-22: Implementación librerías Retrofit Figura 5-23: InterfazAPI 105 La actividad “Graficas”, como su propio nombre indica, se utiliza para mostrar visualmente la evolución que ha tenido el invernadero desde distintas condiciones ambientales. En ellas se pueden observar el progreso que ha tenido la temperatura, la humedad ambiental, la luminosidad o la humedad terrestre en diferentes periodos de tiempo. Los tipos de gráficas utilizados han sido: gráfica de barras vertical, para la representación de las humedades y la gráfica de puntos, para la representación de la temperatura y de la luminosidad. En las siguientes figuras se pueden observar a modo de ejemplo las evoluciones de la temperatura y humedad ambiental en el último día. La librería ‘MPAndroidChart’ es la encargada del desarrollo de estas gráficas. Al tratarse de una biblioteca externa, es necesario implementarla en el archivo build:gradle del proyecto: Antes de que la actividad dibuje las gráficas, solicita previamente los datos al servidor, utilizando la URL: http://192.168.1.62:9000/servidor/consulta?seleccion=’tabla’&opcion=’opcionElegida’, siendo ‘tabla’, el nombre de la tabla de la base de datos de la que se quiere obtener la información y ‘opcionElegida’ el rango temporal. Estos datos serán los valores enviados por los sensores y los tiempos en los que han sido almacenados en la base de datos y ambos conjuntos serán representados en el eje OY y en el eje OX respectivamente. Figura 5-24: Gráfica temperatura Figura 5-25: Gráfica humedad ambiental Figura 5-26: Implementación librerías MPAndroidChart 106 En todas las gráficas se muestran nueve puntos, de los cuales se calcula el máximo, el mínimo y la media entre ellos para mostrarlos en la parte inferior de la actividad. La pestaña “Información” del menú lleva a una lista en la que se detalla, a modo de curiosidad, cuáles son las condiciones óptimas de diferentes cultivos en el invernadero. La pestaña “Configuración” permite dirigirse a una actividad en la que se puede cambiar tanto la dirección del servidor como el puerto a la que van dirigidas todas las peticiones usadas en la aplicación. Estas variables son almacenadas en una clase get/set de Java y al pulsar sobre el botón “Guardar Cambios”, son accesibles desde cualquier actividad de la app. Por último, la pestaña “Salir” permite cerrar y abandonar la aplicación. 5.3.2 Conexión entre componentes A continuación, se muestra en un diagrama de secuencia el paso de mensajes que tiene lugar cuando se pretende encender la bomba de agua mediante una orden por voz. Se suponen que todas las conexiones están iniciadas. Este proceso comienza cuando el usuario ejecuta la aplicación móvil. Lo primero que se le pedirá será que se autentifique para poder acceder. Una vez introducido el usuario y la contraseña, la app envía una petición HTTP POST al servidor REST. Este, en función del nombre del usuario introducido, realizará una consulta a la base de datos para obtener, si existe, dicho usuario y la contraseña correspondiente. Si las credenciales introducidas y las almacenadas en la base de datos coinciden, el Figura 5-27: Actividad Información Figura 5-28: Actividad Configuración 107 servidor envía como respuesta una variable booleana con valor true, permitiendo que la aplicación cambie de actividad. La nueva actividad mostrada será la de ‘Inicio’, y mediante el menú, el usuario podrá dirigirse a la actividad ‘Gestión’, en la que se configura el invernadero. Desde ella, el usuario, pulsando sobre el icono del micrófono, grabará una orden que, una vez terminada, recibirá el servidor presionando sobre el botón “Enviar comando”. La grabación será enviada en un archivo de audio de tipo 3gp en el cuerpo de una petición POST. Una vez que el servidor recibe este mensaje, extrae de él el fichero de audio y mediante la herramienta de línea de comandos ffmpeg, realiza la conversión de 3gp a wav. Posteriormente, el servidor, al tener el archivo en formato wav, lo envía al programa Voice2JSON para que realice la transcripción de audio a texto. Al acabar este proceso, el resultado JSON se escribe en un fichero y se almacena en un directorio. Cuando el script “enviar_comando.sh” detecta la existencia de este fichero, lee su contenido para enviarlo, como productor, al bróker de Kafka a través del topic “Thing1” y seguidamente será reenviado al programa Python, configurado como suscriptor de este topic. Por último, el script será el encargado de eliminar el fichero consiguiendo de esta manera estar preparado para la recepción de nuevos comandos. El programa Python, en función de la orden recibida, enviara una petición POST al servidor REST de OpenHAB con el valor data establecido a ON reflejando la voluntad del encendido del motor. Al recibirlo, OpenHAB envía un mensaje que contiene un entero 1, de tipo PUBLISH, al servidor MQTT a través del topic “Entrada0/1”. Esta petición no tiene una respuesta por parte del servidor, al ser enviada con el nivel QoS establecida a 0. Cuando la placa ESP32 recibe el mensaje PUBLISH reenviado por el servidor MQTT, comprueba el topic del cual proviene y el valor que transporta el mensaje. Al proceder de “Entrada0/1” y con un valor 1, enviará la orden LOW al pin conectado al relé, ya que éste funciona a nivel bajo. Cuando el relé se desactive, el motor recibirá la tensión suficiente de las cuatro pilas instaladas y bombeará el agua. 108 Figura 5-29: Diagrama de secuencia 109 6 CONCLUSIONES Y FUTURAS AMPLIACIONES Gracias al internet de las cosas, el mundo tal y como se conoce actualmente, está evolucionando digitalmente. Multitud de avances afirman este cambio, como pueden ser el nacimiento de las ciudades inteligentes, en las que los servicios públicos están controlados a través de internet, o la aparición de las monedas virtuales, utilizadas para realizar cualquier tipo de transacción. Este cambio se ha visto altamente respaldado por la reciente pandemia vivida como consecuencia de la Covid-19. Durante este tiempo, el uso de internet permitió que el mundo siguiera comunicado a pesar de las estrictas restricciones, evitando así una fuerte involución que hubiera afectado, no solo a las actuales generaciones, también a las venideras. La aparición de nuevas tecnologías, como la red 5G o la inteligencia artificial, incentivarán aún más el uso de dispositivos IoT conectados a Internet. Ámbitos como la educación, vigilancia o la sanidad se verán beneficiados por el uso de dichas tecnologías, ya que otorgarán una mayor eficiencia y productividad en el desarrollo de sus procesos y operaciones. Sin embargo, este cambio también puede suponer riesgos para la seguridad o intimidad de las personas, al abrir espacios privados hacia el exterior o la publicación de datos confidenciales en caso de que los sistemas IoT sean vulnerados. En este trabajo se ha conseguido administrar y gestionar un pequeño invernadero simulado y gracias a la gran escalabilidad que ofrecen los programas con los que se ha desarrollado, se podría emplear para el control de invernaderos a gran escala. Se ha diseñado y configurado poniendo de manifiesto su bajo coste, teniendo en cuenta únicamente los distintos tipos de sensores, actuadores utilizados y la placa ESP32. Además, se ha demostrado que la interconexión entre Kafka y OpenHAB es posible, a pesar de no haber en este último bindings específicos que lo faciliten, como puede ser el usado para la comunicación con MQTT. Esto tiene lugar gracias a que OpenHAB dispone de su propia API REST, a través de la cual se han podido controlar todos los elementos físicos existentes en el invernadero. Así mismo, las reglas configuradas en OpenHAB permiten la automatización del invernadero. Por otro lado, el programa Voice2JSON ofrece un software gratuito de código abierto utilizado para la conversión de audio a texto y que ha permitido la interacción entre la voz y el invernadero. Con él, se han resuelto los inconvenientes ocasionados al trabajar en estas instalaciones, como pueden ser la manipulación de herramientas húmedas o el contacto con la tierra. Sin embargo, su uso no garantiza completamente que los procesos de transcripción se realicen correctamente ya que, al ser de código abierto, los conjuntos de datos utilizados para las conversiones no son tan extensos como los de las entidades privadas. Finalmente, la aplicación Android otorga al usuario la posibilidad de poder gestionar el invernadero de acuerdo con sus necesidades. A través de ella, se observan las condiciones ambientales actuales 110 del invernadero, se configuran límites ambientales para accionar los distintos actuadores de forma automática cuando el estado del clima no se adapte a estos valores. Además, mediante toda la información proporcionada de forma gráfica, se puede obtener la máxima rentabilidad del suelo cultivado, aumentar la producción y calidad de las cosechas y optimizar el consumo del agua. Este Trabajo Fin de Grado es un proyecto que empezó con la idea de conectar dos programas, y fue ampliándose hasta llegar al trabajo descrito en la presente memoria. Sin embargo, muchas han sido las ideas que no han sido implementadas para no extender en exceso el contenido del mismo. Por ello, en futuras versiones de este proyecto se podrían incorporar nuevas mejoras en campos como el de la seguridad, el rendimiento o el diseño: 6.1 Líneas futuras: Seguridad • Encriptar los pares usuario:contraseña almacenados en la base de datos para el acceso en la app. Actualmente, el servidor realiza la comprobación de las credenciales sin utilizar ningún tipo de programa que encripte las claves. • Envío de las peticiones a través del protocolo HTTPS. 6.2 Líneas futuras: Diseño • Ofrecer la posibilidad de poder cambiar la frecuencia a la que se recogen los datos. De esta forma, la gestión del invernadero se ajusta a las necesidades propias del usuario. • Ofrecer la capacidad de mostrar los datos que se vayan recogiendo del invernadero en tiempo real, utilizando para ello, por ejemplo, WebSockets. • Instalar todos los servidores y programas en la nube, de forma que sea posible acceder a la gestión del invernadero desde cualquier lugar y no estar necesariamente en la misma red local. • Utilizar sensores que sean más precisos a la hora de recoger datos, con menos margen de error. Por ejemplo, sustituir el sensor de temperatura y humedad DHT11 por el DHT22 o utilizar un sensor de ultrasonidos en lugar de uno resistivo para conocer el nivel del agua. • Utilizar otro bróker MQTT con una mayor disponibilidad, ya que el que se ha usado en el trabajo, a pesar de ser de uso público, en ocasiones no se podía utilizar en ciertos periodos del día. • Mejorar el proceso de conversión audio a texto, de forma que se implementara algún procedimiento para que se verificase si la transcripción realizada se corresponde con la orden por voz. Se podría realizar instalando el programa Voice2JSON en la aplicación Android. 6.3 Líneas futuras: Funcionalidad • Crear un acceso exclusivo a administradores para que puedan cambiar la configuración del sistema, como añadir nuevos usuarios o borrar datos de la base de datos. • Incluir la posibilidad de eliminar los datos que están almacenados en la base de datos, ya sea por decisión del usuario o de forma automática. • Crear notificaciones o alertas de forma que cuando se supere un límite establecido, la aplicación lo muestre, aunque el usuario no la esté ejecutando en primer plano. 111 • Permitir al usuario la posibilidad de cancelar el accionamiento automático de los actuadores. • Permitir al usuario actualizar el contador del agua consumida a través de la app. 118 • Software de Adafruit específico utilizado como controlador para poder conectarse con placas desarrolladas por esta empresa. • Software Genuino USB Driver, la cual se trata de otro controlador que permite la comunicación entre placas y la computadora. Instalación librerías ESP32 Para poder comunicarse y programar la placa ESP32 desde Arduino IDE es necesario instalar unas librerías adicionales. Para ello, desde el menú del IDE de Arduino, en Archivo → Preferencia, en el cuadro de texto llamado “URLs adicionales de gestor de placas” se añade el enlace [29]. Este contiene un fichero JSON con las direcciones de todas las librerías necesarias para poder gestionar la placa ESP32. Figura C-3: Software Adafruit Figura C-4: Software Genuino USB Driver 119 Posteriormente, en Herramientas → Placas → Gestor de placas, se busca la nueva placa descargada y se instala para que pueda ser configurada a través del IDE. En caso de que el controlador encargado de la comunicación USB-UART no se haya instalado automáticamente, será necesario descargarlo a través del enlace [9]. Figura C-5: Configuración del IDE de Arduino Figura C-6: Instalación de la placa ESP32 en el IDE de Arduino 120 Una vez instalado, se podrá comprobar que en el puerto USB del PC se reconoce la placa a través del menú Windows → Administrador de dispositivos: Por último, hay informar que se ha tenido que utilizar un cable USB-micro USB de pequeña longitud para establecer la comunicación entre el host y la placa. De esta forma se asegura que los datos no se atenúan y no hay problemas de conexión. Figura C-7: Software reconocible en el PC 121 ANEXO D: CONFIGURACIÓN DE LA BASE DE DATOS En este anexo se explicará cuáles han sido los pasos necesarios para la configuración de la base de datos utilizada en el proyecto. No ha sido preciso su instalación en la máquina virtual puesto que ya venía implementada. Al instalarse el SGBD PostgreSQL, se crea dentro del sistema operativo de Linux el superusuario ‘postgres’ del servidor de la base de datos. Sin embargo, por motivos académicos, la contraseña se modificó a ‘pgsqlroot’ mediante el siguiente comando con privilegios de superusuario (necesario haber accedido mediante el usuario root): Posteriormente, para crear el nuevo usuario ‘openhab’ dentro del SGBD, hay que ejecutar los siguientes comandos: Con el comando “start-postgresql” iniciamos el servidor PostgreSQL, y con “psql” ejecutamos la base de datos. Para la creación de la base de datos ‘openhabdb’ se utiliza la siguiente orden: Una vez creado el nuevo usuario y la base de datos, es necesario cambiar la configuración correspondiente a la autenticación por contraseñas de los usuarios del SGSB. Para ello, en el fichero pg_hba.conf se incluye la siguiente sentencia: local all openhab md5 De esta forma, se consigue el acceso desde el usuario ‘openhab’ a la base de datos ‘openhabdb’, introduciendo el comando psql -U openhab openhabdb Figura D-1: Comando para cambiar de contraseña al usuario ‘postgres’ Figura D-2: Creación del nuevo usuario 'openhab' Figura D-3: Creación de la base de datos 'openhabdb'