Piloto de gestión para dispositivos IoT Autor: Vicente Zamora García Tutor: Antonio José Estepa Alonso Dpto. de Ingeniería Telemática Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2025 Trabajo Fin de Grado en Ingeniería de las Tecnologías de Telecomunicación
iii Trabajo Fin de Grado en Ingeniería de las Tecnologías de Telecomunicación Piloto de gestión para dispositivos IoT Autor: Vicente Zamora García Tutor: Antonio José Estepa Alonso Dpto. de Ingeniería Telemática Escuela Técnica Superior de Ingeniería Universidad de Sevilla Sevilla, 2025
v Trabajo Fin de Grado: Piloto de gestión para dispositivos IoT Autor: Vicente Zamora García Tutor: Antonio José Estepa Alonso El tribunal nombrado para juzgar el Proyecto arriba indicado, compuesto por los siguientes miembros: Presidente: Vocales: Secretario: Acuerdan otorgarle la calificación de: Sevilla, 2025 El Secretario del Tribunal
vii A mi familia A mis maestros
ix Agradecimientos Considero realmente complicado describir, en unos pocos párrafos, todo lo que han significado para mí mis padres a lo largo de este camino. Durante estos últimos cuatro años y en general durante toda mi vida, han sido el pilar fundamental para que continuara, día tras día, esforzándome por llegar a convertirme en la persona que quiero ser, por cumplir los sueños que siempre he tenido y por no olvidarme de que lo más importante en esta vida es no arrepentirte de nada. Han estado en todos los momentos en los que dudaba de mí mismo, donde una simple conversación y un abrazo lleno de confianza me empujaban a seguir adelante e intentar dar lo mejor de mí. Este logro también es vuestro, porque sé que mi esfuerzo y perseverancia os la debo a vosotros, pues me la habéis enseñado desde el ejemplo. También me gustaría agradecer a todos los profesores que han formado parte de mi formación durante el Grado. Gracias a ellos he podido conseguir un trabajo que me encanta y me mantiene motivado día tras día, con unas condiciones que ni me planteaba tener antes de entrar en la Universidad. Quiero agradecer de primera mano a mi tutor, Antonio José Estepa Alonso, por haberme brindado la oportunidad de realizar este trabajo y ayudarme ante cualquier duda, permitiéndome continuar el proyecto que más he disfrutado llevar a cabo en todo el Grado. Gracias a todos. Vicente Zamora García Estudiante de Ingeniería de las Tecnologías de Telecomunicación Sevilla, 2025
ÍNDICE DE TABLAS Tabla 2-1 – Tipos de datos SNMP 31 Tabla 2-2 – Tipos de mensajes SNMP 32 Tabla 2-3 – Comparativa Arduino MEGA y ESP32 36 Tabla 3-1 - Presupuesto 41 Tabla 4-1 – Librerías utilizadas en el ESP32 45 Tabla 4-2 – Pines del LCD TFT 56 Tabla 5-1 – Especificaciones TMP36 64 Tabla 5-2 – Especificaciones Vegetronix VH400 65 Tabla 5-3 – Especificaciones KY-38 67 Tabla 5-4 – Pines del display LCD 16x2 70 Tabla 5-5 – Grupo1: informacionDeGestion 73 Tabla 5-6 – Grupo 2: valoresLeidos 73 Tabla 5-7 – Grupo 3: eventos 74 Tabla 5-8 – Grupo 4: servidores 74 Tabla 5-9 – Grupo 5: nodosDeRed 75 Tabla 5-10 – Grupo 6: encaminamiento 75
xvii ÍNDICE DE FIGURAS Figura 1-1 – Esquema general del sistema 24 Figura 2-1 – Modelo de gestión por SNMP 29 Figura 2-2 – Escenario básico MQTT 34 Figura 2-3 – ESP32 35 Figura 2-4 – Arduino MEGA 2560 35 Figura 3-1 – Componentes del sistema 39 Figura 3-2 – Diagrama de conexiones del sistema 42 Figura 3-3: Sistema real: hardware implicado 42 Figura 4-1 – Hardware implicado 44 Figura 4-2 – Diagrama de estados Arduino_SNMP_Manager 46 Figura 4-3 – Estructuras SNMP 46 Figura 4-4 – Callbacks para servidores 47 Figura 4-5 – Callbacks para los routers 47 Figura 4-6 – Callbacks para el grupo BGP 47 Figura 4-7 – Función get1SNMP: envío de peticiones SNMP a los servidores 48 Figura 4-8 – Función inicioWeb() 1 49 Figura 4-9 – Función inicioWeb() 2 49 Figura 4-10 – Función inicioWeb() 3 49 Figura 4-11 – Web de gestión del ESP32 50 Figura 4-12 – Función conexionBroker() 50 Figura 4-13 – Publicación de variables con MQTT 51 Figura 4-14 – Funcionamiento de la comunicación UART 53 Figura 4-15 – Comunicación UART entre el ESP32 y el Arduino 53 Figura 4-16 – Diagrama de flujo de la comunicación UART 54 Figura 4-17 – Función combinarVariables() 55 Figura 4-18 – Función asignarValoresDesdeCadena() 55 Figura 4-19 – Display TFT: pantalla de carga 56 Figura 4-20 – Display TFT: establecimiento WiFi 57 Figura 4-21 – Diagrama de flujo del display TFT 57 Figura 4-22 – Diagrama de flujo del script cliente MQTT 58 Figura 4-23 – Escenario GNS3 59 Figura 5-1 – Hardware implicado 63 Figura 5-2 – Sensor de temperatura TMP36 64 Figura 5-3 – Sensor de humedad del suelo VH400 65
Figura 5-4 – Sensor capacitivo 66 Figura 5-5 – Sensor de sonido KY-38 67 Figura 5-6 – Ethernet Shield W5100 68 Figura 5-7 – Módulo WiFI: ESP8266 69 Figura 5-8 – Display LCD: establecimiento WiFi 70 Figura 5-9 – Diagrama de flujo del display LCD 71 Figura 5-10 - MIBARDUINO 72 Figura 5-11 – Diagrama de flujo del Arduino MEGA 76 Figura 5-12 – Función loop() 77 Figura 5-13 – Función actualiza2() 78 Figura 5-14 – Función evento2() 79 Figura 6-1 – Declaración de OIDs (ejemplo R1) 82 Figura 6-2 – Declaración de variables: nodosDeRed 82 Figura 6-3 – Definición de variables: nodosDeRed 82 Figura 6-4 – Función PduRecibida() 83 Figura 6-5 – Función PduRecibida() - 2 83 Figura 6-6 – Función PduRecibida() - 3 84 Figura 6-7 – Función PduRecibida - 4 85 Figura 6-8 – Función PduRecibida() - 5 85 Figura 6-9 – Array auxiliar de OIDs 86 Figura 6-10 – Array auxiliar de variables locales 87 Figura 6-11 – Función PduRecibida - 6 88 Figura 6-12 – Función PduRespuesta() 89 Figura 6-13 – Función PduRespuesta() - 2 89 Figura 6-14 – Envío de TRAP 89 Figura 6-15 – Función Trap() 90 Figura 6-16 – Función Trap() - 2 91 Figura 6-17 – Función Trap() -3 92 Figura 6-18 – Función Trap() - 4 92 Figura 6-19 – Función Trap() - 5 93 Figura 6-20 – Procesamiento de peticiones GET-NEXT 93 Figura 7-1 – Escenario de pruebas 95 Figura 7-2 – Servidor REST: endpoints disponibles. 97 Figura 7-3 – Diagrama de clases del proyecto Spring Boot 98 Figura 7-4 – Diagrama de clases de la aplicación VManager 99 Figura 7-5 – Página de inicio de sesión 100 Figura 7-6 – Página principal 101 Figura 7-7 – Apartado del grupo informacionDeGestion 102 Figura 7-8 – Apartado del grupo valoresLeidos 103
xix Figura 7-9 – Apartado del grupo eventos 104 Figura 7-10 – Apartado del grupo servidores 105 Figura 7-11 – Apartado del grupo nodosDeRed 106 Figura 7-12 – Apartado del grupo encaminamiento 107 Figura 7-13 – Grafana: Dashboard 1 109 Figura 7-14 – Grafana: Dashboard 2 109 Figura 7-15 – Selección en MIBBrowser 110 Figura 7-16 – Peticiones en MIBBrowser 111 Figura 7-17 – Tablas en MIBBrowser 111
xxi Notación ADC Analog-to-Digital Converter ASN Autonomous System Number BER Basic Encoding Rules BGP Border Gateway Protocol CSS Cascading Style Sheets HTTP HyperText Transfer Protocolo HTML HyperText Markup Language I2C Inter Integrated Circuit IANA Internet Assigned Numbers Authority IDE Integrated Development Environment IETF Internet Engineering Task Force IoT Internet of Things LX106 Arquitectura del microcontrolador ESP8266 MQTT Message Queuing Telemetry Transport OID Object Identifier OS Operating System QoS Quality of Service REST Representational State Transfer RMON Remote Network Monitoring SNMP Simple Network Management Protocol SPI Serial Peripheral Interface SPIFFS SPI Flash File System SQL Structured Query Languaje UART Universal Asynchronous Receiver/Transmitter VWC Volumetric Water Content XTensa Arquitectura de microprocesadores utilizada en los ESP
1 INTRODUCCIÓN Este primer capítulo tiene como objetivo presentar los motivos y antecedentes que justifican la realización de este Trabajo Fin de Grado, enmarcados en un contexto donde la gestión de redes y el Internet de las Cosas desempeñan un papel fundamental en el paradigma de las telecomunicaciones. Además, se describirán desde un alto nivel de abstracción, las características generales de la solución propuesta, así como los objetivos y el alcance de este proyecto. Finalmente, se presentará la estructura general de la memoria. 1.1 Motivación La gestión de redes ha evolucionado significativamente en las últimas décadas, pasando de ser un conjunto de tareas manuales y reactivas a convertirse en un proceso proactivo y automatizado, siendo esencial para garantizar la disponibilidad, el rendimiento y la seguridad de las infraestructuras de telecomunicación [1]. En este contexto, SNMP se ha consolidado como uno de los protocolos más utilizados para la monitorización y gestión de dispositivos de red, gracias a su simplicidad, escalabilidad y amplia adopción en el sector empresarial. Sin embargo, la integración de SNMP en dispositivos IoT, presenta un gran desafío debido a las limitaciones de recursos en estos dispositivos y a la necesidad, por tanto, de adaptar el protocolo a entornos con requisitos específicos de bajo consumo y potencia. Por otro lado, el IoT ha experimentado un crecimiento exponencial en los últimos años, transformando industrias enteras, desde aplicaciones en smart cities hasta sistemas de automatización industrial. Como consecuencia, ha quedado demostrada su capacidad para mejorar la eficiencia, reducir costes y ofrecer servicios personalizados. La motivación de este trabajo radica en el desarrollo de una solución innovadora que permita, no solo gestionar un conjunto de sensores físicos y virtuales, sino también de redes complejas, aprovechando las ventajas de protocolos ampliamente adoptados como SNMP en dispositivos realmente económicos y de muy bajo consumo energético. Este enfoque no solo pretende aportar un nuevo punto de vista para la gestión de redes, sino que también sienta las bases para un futuro proyecto de un sistema de detección de ataques y de gestión por intenciones [2]. Es decir, a largo plazo, se busca integrar en este sistema técnicas de machine learning para la detección temprana de posibles ataques o fallos en la red, así como convertirse en una pieza clave para un sistema de gestión por intenciones, donde la red pueda autogestionarse en función del estado actual del sistema. 1.2 Objetivos y alcance El objetivo principal de este proyecto es desarrollar un sistema de gestión remota que no solo permita la monitorización y control de sensores físicos y virtuales implementados en un dispositivo IoT, sino que también centralice la información de gestión de dispositivos de red reales, como routers, switches y servidores. El sistema está diseñado para ser escalable tanto verticalmente, ampliando el número de sensores y dispositivos gestionados, como horizontalmente, permitiendo la replicación del sistema en distintas zonas geográficas para la gestión distribuida de múltiples dispositivos y redes. "El principio es la mitad de todo." - Pitágoras -
Introducción 1.2.1 Esquema general del sistema Para introducir el sistema desde un alto nivel de abstracción, propongo el siguiente diagrama general que simplifica los principales subsistemas que se detallarán a lo largo de la memoria. En él, se han indicado para cada subsistema sus entradas y salidas asociadas, además de los principales componentes internos de cada uno. Figura 1-1 – Esquema general del sistema • Subsistema de gestión 1: monitoriza equipos de un escenario de red complejo (interconexión de redes de dos sistemas autónomos con routers que utilizan BGP) simulado a través de GNS3. Se simulan routers que implementan un agente SNMP. Por tanto, este subsistema gestor recopila datos de estos equipos a través de SNMP y los envía al sistema de persistencia de datos a través del protocolo MQTT y al subsistema de gestión 2 a través de una UART. Los principales componentes de este subsistema son: un gestor SNMP encargado de realizar las peticiones a los agentes SNMP gestionados, un cliente MQTT encargado de proporcionar toda la información obtenida por SNMP a través de MQTT y un servidor HTTP para mostrar vía Web los dispositivos que están siendo gestionados por este subsistema, además de permitir configurar el tiempo entre envíos de las peticiones a estos. Todos estos componentes son implementa en un único dispositivo IoT.
25 25 Piloto de gestión para dispositivos IoT • Subsistema de gestión 2: recibe del subsistema de gestión 1 las instancias de SNMP de los routers y servidores que este gestiona. Transmite, también mediante SNMP, tanto estas instancias, como otras nuevas para la gestión de sensores físicos y virtuales que este subsistema implementa. Es, por tanto, un agente SNMP complejo que implementa la MIB elaborada para este trabajo (Anexo A). En ella se definen todos los objetos de gestión de este subsistema que pueden ser gestionados mediante SNMP. Por tanto, los principales componentes de este subsistema son: un agente SNMP encargado de recibir y procesar las peticiones de este protocolo y todo el conjunto de sensores virtuales y físicos. • Subsistema de persistencia: lo conforma un conjunto de herramientas y tecnologías que hacen posible el almacenamiento de los datos centralizados en el subsistema de gestión 1, permitiendo la persistencia de estos para tener un historial completo de las instancias de SNMP de los routers y servidores gestionados. Concretamente, este subsistema se compone de un bróker del protocolo MQTT, encargado de recibir las instancias de SNMP gestionadas por el subsistema de gestión 1 y almacenarla en el segundo componente, una base de datos PostgreSQL. Además, implementa un servidor REST ejerciendo de backend cuyo objetivo es proveer la información de la base de datos. • Subsistema de Interfaz de Usuario: se encarga del procesamiento y presentación de los datos obtenidos tanto del subsistema de gestión 1 para las instancias de SNMP de los routers y servidores, como del subsistema de gestión 2, para las instancias del agente SNMP implementado en este subsistema. Se compone de una aplicación móvil desarrollada con Android Studio y de un stack de monitorización estándar conformado por SNMP_Exporter, Prometheus y Grafana. 1.3 Estructura de la memoria La memoria comenzará introduciendo la base teórica necesaria para comprender las características y el rol que ejerce cada herramienta y protocolo utilizado en este trabajo. En un posterior capítulo, se realizará una definición general del sistema completo desde un alto nivel de abstracción, detallando los principales componentes de cada subsistema. Posteriormente, se realizará una definición completa del subsistema de gestion 1, abarcando todo el desarrollo realizado para el ESP32 en relación a las funcionalidades que ofrece: gestión de un display, comunicación UART, publicación sobre MQTT, entre otros. Además, en este apartado, se detallarán las implementaciones realizadas para las herramientas externas que se comunican con este subsistema: GNS3 y Eclipse Mosquitto. En un posterior capítulo, se definirá el subsistema de gestion 2, correspondiente al Arduino MEGA, detallando sus principales características: manejo de sensores, conectividad WiFi y Ethernet, gestión de los eventos y gestión del display. Dejando para un posterior capítulo, debido a los problemas encontrados y a su notoria complejidad, el proceso e implementación del protocolo SNMP en un dispositivo Arduino. Para terminar con la implementación, se detallarán en un único capítulo de pruebas, el subsistema de interfaz de usuario y el subsistema de persistencia, abarcando la aplicación móvil desarrollada con Android Studio y el servidor REST que ejerce de intermediario con la base de datos. También se presentarán soluciones alternativas que puedan utilizarse como el stack conformado por SNMP-Exporter, Prometheus y Grafana o un software gratuíto llamado MIBBrowser. Por último, se presentará en un capítulo las conclusiones de este trabajo y las líneas a futuro, dejando constancia de múltiples mejoras.
Base teórica formato específico. Como se ha mencionado anteriormente, una MIB no es más que un módulo ASN.1 donde se definen los objetos de gestión que implementa el agente SNMP. o Estos módulos utilizan el lenguaje definido por ASN.1 para describir la estructura de los objetos. 4. Reglas de Codificación (BER): o Para codificar los objetos, se utilizan las reglas BER (Basic Encoding Rules) siguiendo una codificación en formato binario. o En estas reglas se definen cómo se empaquetan los datos en mensajes SNMP, debiendo incluir el tipo, la longitud y el valor de cada uno de los objetos de gestión. Es imprescindible conocer de primera mano estas reglas para entender la implementación de la librería de AgenteSNMP desarrollada para el Arduino MEGA. Propongo la consulta de [11], donde se definen las principales reglas BER para los tipos de datos de SNMP vistos anteriormente, tanto primitivos como constructores. 2.1.1.3 Tipos de mensajes SNMP cuenta con varios tipos de mensajes, estando la gran mayoría disponibles en cualquier versión del protocolo, excepto GET-BULK y INFORM que surgieron a partir de la versión 2 de SNMP y, por tanto, no se pueden utilizar en SNMPv1. Tabla 2-2 – Tipos de mensajes SNMP Tipo de mensaje Descripción GET Solicita el valor de un objeto específico en la MIB GET-RESPONSE Respuesta del agente a una solicitud GET, GET-NEXT, GETBULK, SET o INFORM. Contiene los valores solicitados o, en caso contrario, errores. GET-NEXT Obtiene el siguiente objeto en la MIB, según el orden jerárquico. Se utiliza principalmente para recorrer tablas o grupos completos. SET Modifica el valor de una instancia de un objeto. GET-BULK Es similar a GET-NEXT, pero siendo más eficiente al permitir con una única petición obtener un alto número de instancias de SNMP, siendo este número limitado por el tamaño máximo del mensaje. INFORM Es similar a TRAP, pero requiere confirmación del gestor, es decir, una respuesta GET-RESPONSE. TRAP Utilizado para alertar sobre eventos previamente configurados por el gestor. Indica la ocurrencia de dicho evento configurado.
33 33 Piloto de gestión para dispositivos IoT 2.1.2 MQTT En el contexto de este trabajo, es imprescindible conocer este protocolo ya que es utilizado por el subsistema de gestión 1 para enviar las instancias de SNMP, de los routers y servidores que gestiona, hacia el bróker MQTT encargado de introducirlas en la base de datos. MQTT [12] cuenta con varios motivos que lo hace ser el principal protocolo de comunicación en el ámbito de los dispositivos de IoT. Lo que provoca que sus casos de uso abarquen ámbitos muy dispares y que, cada vez, sea más utilizado en entornos profesionales. Entre estos ámbitos podemos destacar sectores como la automoción, con marcas como BWM [13] y Volkswagen [14], que ya implementen sus propios sistemas de IoT basados en el protocolo de MQTT, o el sector de transporte, donde un claro ejemplo en Alemania con DB Railway System. Todos estos casos de uso en ámbitos profesionales se detallan en [15], sin embargo, no debemos dejar atrás el uso general de protocolos de este ámbito por el ciudadano común, ya que, hay una gran comunidad detrás de sistemas Smart Home que utilizan estos protocolos para automatizar y gestionar ciertas actividades en el ámbito privado: sistemas de seguridad en casa, riego automático de plantas, automatización de la iluminación, entre otros. 2.1.2.1 Modelo Publicador-Suscriptor Concretamente, MQTT es un estándar de OASIS [12] y constituye uno de los protocolos más tradicionales y usados de IoT, destinados a la comunicación en dispositivos de bajos recursos. Entre sus principales características, cabe destacar que utiliza un modelo publicación-suscripción. En este modelo nos encontramos los siguientes elementos: • Cliente MQTT: Es una aplicación o dispositivo que se encarga de enviar o recibir información al bróker. Por tanto, no se comunica directamente con el resto de los clientes. Puede ejercer dos roles: publicador, donde el cliente transmite un mensaje a un topic específico; y suscriptor, donde el cliente le comunica al bróker que desea recibir mensajes de un topic específico. • Bróker MQTT: Es, por tanto, un intermediario entre los clientes. Su labor es la gestión de las conexiones con estos y la distribución de la información. En la siguiente Figura 2-2 se detalla el modo de funcionamiento de este protocolo, donde un cliente puede ejercer tanto como suscriptor, publicador o ambos a la vez, y no necesariamente tiene que ser un dispositivo de bajos recursos tales como sensores, cámaras o dispositivos Arduino, sino que podría ser un dispositivo más potente, como un ordenador o teléfono. En este escenario se propone un modelo donde el ordenador y el teléfono móvil gestionan los tiempos de muestreo de un sensor de temperatura, un sensor de humedad, y un sensor genérico conectado a un dispositivo Arduino. Se observa, cómo el bróker lleva a cabo la tarea de distribuir la información entre clientes, de forma que estos no se comunican de forma directa y de gestionar las conexiones con estos. Esto permite además que un cliente tras desconectarse del bróker pueda mantener sus suscripciones y publicaciones cuando vuelva a conectarse sin necesidad de realizar nuevas peticiones.
Base teórica Figura 2-2 – Escenario básico MQTT 2.2 Tecnologías implicadas A continuación, se realizará una breve introducción a todas las tecnologías sobre las que se apoya este Trabajo Fin de Grado. 2.2.1 Arduino Arduino [16] es una plataforma de hardware y software de código abierto diseñada para facilitar la creación de proyectos del ámbito de la electrónica, gracias al uso de microcontroladores eficientes y muy baratos que permiten programar dispositivos de tamaño reducido mediante un lenguaje basado en C/C++. En este trabajo, Arduino supone la principal plataforma de hardware y software utilizada, ya que, el subsistema de gestión 2 está gobernado por un Arduino MEGA 2560, modelo nativo de la marca Arduino, mientras que el subsistema de gestión 1 es conformado por un ESP32 cuyo desarrollo se realiza también desde el mismo IDE de Arduino. Ambos dispositivos comparten similitudes, sin embargo, hay claras diferencias que se explicarán a lo largo de los siguientes apartados. Fue inventado en 2005 por un estudiante de instituto, llamado Massimo Banzi, con el objetivo facilitar a los estudiantes de su instituto un aprendizaje práctico en electrónica y programación, ya que, en aquel momento, las placas de microcontroladores eran muy poco accesibles económicamente. Tras varias versiones y el apoyo de varios colaboradores, Banzi consiguió la variante de Arduino Uno que conocemos hoy en día y que se ha convertido en una herramienta líder en el ámbito de la electrónica DIY (Do It Yourself, hazlo tú mismo). En los siguientes apartados se detallarán las características del hardware y software necesarios para elaborar un proyecto de este ámbito, haciendo énfasis en los dispositivos utilizados en este trabajo. 2.2.1.1 Hardware Además de las placas estándar como las de la marca Arduino, existen otras plataformas de microcontroladores de bajo coste y gran rendimiento que incluyen capacidades de conectividad como Wi-Fi o Bluetooth, lo que las convierte en una excelente opción para proyectos más avanzados que requieren comunicación inalámbrica y conexión a Internet.
35 35 Piloto de gestión para dispositivos IoT A continuación, se describen dos opciones muy populares que constituyen el núcleo a nivel de hardware de este Trabajo Fin de Grado: el ESP32 y el Arduino MEGA. ESP El módulo ESP32 es un sistema en chip (SoC) de bajo coste y consumo energético, diseñado por la empresa Espressif Systems para aplicaciones de IoT. A diferencia de las opciones más económicas de Arduino, integra de forma nativa Wi-Fi y Bluetooth, siendo, además, programable con el IDE de Arduino o el framework ESPIDF. Figura 2-3 – ESP32 Arduino MEGA 2560 El Arduino MEGA 2560 es una placa basada en el microcontrolador ATmega2560, diseñada para proyectos que requieren más pines de E/S y memoria que un Arduino UNO ya que, a diferencia de este, cuenta con 8 KB de SRAM, 54 pines digitales y 16 pines analógicos, números cuatro veces mayor que los de su hermano menor. Además, es el modelo más utilizado de la gama Arduino al ser compatible con la mayoría de las placas de expansión, también llamadas shields, que agregan capacidades adicionales como Ethernet, pantallas LCD, ranuras de memoria micro-SD, entre otras. Figura 2-4 – Arduino MEGA 2560 A nivel de hardware, hay una gran diferencia de potencia entre el Arduino MEGA y el ESP32 que puede verse reflejada en la siguiente tabla comparativa, sin embargo, debido al elevado número de pines y a la facilidad de configuración en Arduino IDE, el Arduino MEGA 2560 sigue siendo la opción más utilizada:
Base teórica Tabla 2-3 – Comparativa Arduino MEGA y ESP32 Característica Arduino MEGA 2560 ESP32 Microcontrolador ATmega2560 (8 bits) Tensilica Xtensa LX6 (32 bits) Frecuencia del procesador 16 MHz 240 Mhz GPIO 54 36 (14 analógicas compartidas) Memoria RAM 8 KB 520 KB Memoria de programa 256 KB 4 MB Voltaje de funcionamiento 5 V 3.3 V 2.2.1.2 IDE de Arduino y programación La programación en Arduino se realiza principalmente a través de su entorno de desarrollo integrado (IDE), que es una herramienta que permite escribir, compilar y cargar código en placas de Arduino, ESP y otras marcas. El lenguaje utilizado está basado en C/C++, lo que significa que comparte la sintaxis y el uso de estructuras y funciones comunes en estos lenguajes. Una de las particularidades de los programas desarrollados en Arduino, también denominados sketchs, es que siempre siguen la siguiente estructura: • Declaración de variables: Al inicio del programa se presenta una parte para declarer variables, funciones, objetos y estructuras e importar las librerías necesarias. • Función setup: Se ejecuta una única vez al encender la placa Arduino o al pulsar la tecla Reset del mismo. En esta función se presenta el código de configuración inicial para la inicializaciónd de perisféricos, comunicaciones, variables, etc. • Función loop: Esta función contiene la lógica principal del programa y su ejecución se repite en bucle hasta que se apague el Arduino. La programación en Arduino no solo se limita a controlar actuadores y leer sensores. A medida que un proyecto crece en complejidad, también lo hace la necesidad de utilizar bibliotecas adicionales que permiten interactuar fácilmente con componentes y módulos más avanzados, como pantallas LCD, motores, dispositivos de alta tension como bombillas y electrodomésticos y módulos de comunicación WiFi o Ethernet. 2.2.2 Eclipse Mosquitto Eclipse Mosquitto [17] es un bróker de código abierto que implementa el protocolo MQTT en sus versiones 5.0, 3.1.1 y 3.1. Está desarrollado por Eclipse Foundation y es compatible con múltiples plataformas y sistemas operativos, ofreciendo características como autenticación, cifrado y soporte para QoS, siendo una herramienta esencial en implementaciones de IoT. Además, proporciona librerías en C para implementar clientes de este protocolo, permitiendo controlarlos por CLI con dos comandos que responden a los roles que pueden tener los clientes de este protocolo: mosquitto_pub y mosquitto_sub. Dentro del subsistema de persistencia, es un elemento clave ya que se encarga de recibir las instancias de gestión de routers y servidores obtenidas por el subsistema de gestión 1 e introducirlas en la base de datos. 2.2.3 GNS3 GNS3 [18] es una plataforma de simulación de redes que permite diseñar, probar y desplegar configuraciones de red complejas en un entorno virtual. Es compatible la mayoría de los sistemas operativos y soporta una amplia gama de dispositivos virtuales (routers, switches, firewalls). Es ampliamente utilizado por profesionales y estudiantes para experimentar con redes sin necesidad de
37 37 Piloto de gestión para dispositivos IoT hardware físico, ya que, permite ejecutar imágenes reales de Cisco IOS utilizando Dynamips en la infraestructura de GNS3. Permitiendo realizar configuraciones como si estuvieras trabajando con routers reales. Utilizar este software para probar diseños de redes complejas es muy recomendable para preparar exámenes de certificaciones muy conocidas de Cisco como CNNA y CCNP. Como alternativa, también existe un programa similar desarrollado por Cisco Networking Academy llamado Cisco Packet Tracer, sin embargo, dada mi experiencia con GNS3 en varios proyectos durante el grado, he decidido seguir utilizando GNS3. Por ello, GNS3 ha sido el software utilizado para desarrollar el escenario principal de gestión de este trabajo que contine los routers gestionados mediante SNMP por el subsistema de gestión 1. 2.2.4 Android Studio Android Studio [19] es el entorno de desarrollo integrado (IDE) oficial para la creación de aplicaciones Android. Es la plataforma preferida por los desarrolladores para diseñar, probar y optimizar aplicaciones móviles para el sistema operativo Android, soporta lenguaje Java y Kotlin. En mi caso, he utilizado este IDE para el desarrollo completo de la aplicación móvil que ejerce como principal interfaz de usuario del sistema. 2.2.5 PostgreSQL PostgreSQL [20] es un sistema de gestión de bases de datos relacionales de código abierto ampliamente utilizado, basado en el lenguaje SQL. Soporta consultas complejas, transacciones ACID, y funciones avanzadas como triggers, vistas y procedimientos almacenados. En este proyecto se utilizará como servicio de base de datos para almacenar las instancias de los objetos de gestión de los routers y servidores gestionados por el subsistema de gestión 1. 2.2.6 Spring Boot Spring Boot [21] es un framework de desarrollo de aplicaciones en Java, diseñado principalmente para simplificar el despliegue y creación de aplicaciones con herramientas que facilitan el acceso y gestión de los datos. En este proyecto se utilizará un servidor REST implementado usando este framework, para ejercer de intermediario entre la aplicación móvil desarrollada y la base de datos.
Base teórica
39 39 Piloto de gestión para dispositivos IoT 3 DEFINICIÓN DEL SISTEMA n este capítulo se llevará a cabo una definición general del sistema, para introducir los principales elementos que componen cada uno de los subsistemas introducidos en el capítulo 1: los subsistemas de gestión 1 y 2, el subsistema de persistencia y el subsistema de interfaz de usuario. En posteriores capítulos, serán descritos detalladamente. 3.1 Componentes del sistema Figura 3-1 – Componentes del sistema E Lo que tengo en mi corazón y en mi alma debe encontrar una salida. Esa es la razón de la música .- Ludwig van Beethoven-
Definición del sistema En la Figura anterior, se muestra el diagrama completo del sistema con las partes que lo componen y la comunicación entre estas. Se han indicado con la misma gama de colores y en forma de recuadro cada uno de los subsistemas vistos anteriormente en la Figura 1-1: Esquema general del sistema. Además, se indican con flechas las entradas y salidas de cada componente y el protocolo que aplica en cada caso. En el subsistema de gestión 1, el ESP32 tiene la función de cliente SNMP, obteniendo mediante este protocolo y de forma periódica, las instancias de gestión de varias de las MIB que implementan los dispositivos que gestiona. Estos serán tanto routers como servidores: • En el caso de los routers, se utilizará un escenario desarrollado con el software GNS3 utilizando imágenes de routers de Cisco, concretamente del modelo C7200. • Para los servidores, utilizaré dos ordenadores con el S.O Windows, aprovechando el servicio de SNMP que viene por defecto. Por otra parte, en el subsistema de gestión 2, El Arduino MEGA es el dispositivo principal encargado de proporcionar, mediante el protocolo SNMP, las instancias de todos los objetos de la MIB elaborada para este Trabajo Fin de Grado (Anexo A), es decir, se trata del agente SNMP principal. Además, es este dispositivo el que implementa, casi en su totalidad, las funcionalidades de gestión que se mencionarán en el siguiente apartado 3.2 Funcionalidades Específicas. Como se observa en el diagrama, hay una interconexión entre el ESP32 y el Arduino MEGA. Esto es así dado que, como se ha mencionado anteriormente, cada vez que el ESP32 obtenga las instancias de SNMP de los routers y servidores gestionados, las enviará al Arduino MEGA mediante comunicación UART. De esta forma, el Arduino MEGA podrá proporcionarlas mediante SNMP, centralizando así toda la información de gestión, no solo de los propios sensores que implementa, sino también de los valores de gestión de routers y servidores. Del mismo modo, el ESP32 también envía estos valores a un bróker del protocolo MQTT que se encarga de almacenarlos en una base de datos relacional implementada con el servicio PostgreSQL. Por último, como se puede apreciar en el propio diagrama, el subsistema de persistencia y el de interfaz de usuario están conformado por las siguientes herramientas: • En primer lugar, tenemos un bróker del protocolo MQTT encargado de recibir del ESP32 las instancias de SNMP que este obtiene de routers y servidores, de forma que almacena en una base de datos toda esta información con el objetivo de tener un historial de cada una de las instancias. • Por otro lado, tenemos una aplicación móvil que he desarrollado como principal interfaz de usuario para interactuar con el sistema. Para ello, obtiene parte de la información de gestión del Ardunio MEGA, mediante peticiones de SNMP, mientras que el resto de la información, correspondiente a las instancias de gestión obtenidas por el ESP32, se obtienen con peticiones HTTP a un servidor REST. Como esta parte se almacena en la base de datos, he desarrollado un backend definido en este caso como servidor REST, que ejerce de intermediario entre la aplicación móvil y la propia base de datos. • Por último, como se mencionó previamente, el Arduino MEGA recibe del ESP32 todas las instancias que este gestiona, de forma que se presenta la posibilidad de obtenerlas mediante peticiones SNMP. La diferencia entre obtener esta información desde la base datos o del Arduino MEGA, es únicamente que en el primer caso tenemos la posibilidad de obtener el historial completo para cada instancia, mientras que, si la obtenemos atacando por SNMP al Arduino MEGA, obtendríamos solo los últimos valores de cada instancia. Sin embargo, dado que se presenta esa posibilidad de obtener toda la información de gestión mediante SNMP, he decidido implementar un escenario auxiliar para la visualización de estos datos con un stack muy utilizado en entornos empresariales, compuesto por: SNMP-Exporter [22], encargado de enviar las peticiones SNMP y proporcionar esta información desde un endpoint que puede ser atacado por HTTP, Prometheus [23] que convierte estos datos en formato de métricas válidas para Grafana [24], que las obtiene y las presenta en múltiples formatos de gráficas personalizables.
41 41 Piloto de gestión para dispositivos IoT 3.2 Funcionalidades del sistema De cara al usuario final, se desglosarán a continuación los casos de uso del sistema: • Consulta y modificación de información de gestión del sistema: nombre identificador, encargado de gestión, dirección IP del Arduino, tiempo de encendido, localización, teléfono y correo de contacto. • Consulta del valor de los sensores físicos y virtuales: temperatura, humedad de suelo, voltímetro, sensor capacitivo, sensores de audio (frecuencia e intensidad del sonido), número de accesos al servidor Web y memoria RAM disponible del Arduino Mega. • Gestión individual de los tiempos entre lecturas de cualquier sensor, físico o virtual. • Consulta del instante de lectura del último valor de cada sensor. • Establecimiento de avisos personalizados para controlar el valor de cualquier sensor, dentro del umbral especificado por el usuario. • Gestión de hasta 2 servidores con el SO Windows (servicio SNMP propietario): tiempo de encendido, carga de la CPU, número de conexiones TCP y espacio en uso del disco principal. • Gestión de hasta 5 dispositivos de red que implementen un agente SNMP (routers, switches): tiempo de encendido, localización, estado y número de bytes recibidos de uno de sus puertos. • Gestión de información de encaminamiento relativa a las conexiones del protocolo BGP de los dispositivos de red gestionados: ASN del vecino, tiempo de la conexión, mensajes de tipo UPDATE y mensajes totales recibidos (UPDATE + KEEP ALIVE). • Acceso autenticado a la aplicación móvil para la gestión remota del sistema. • Acceso al servidor web HTTP del ESP32 para la consulta de los equipos de red (nombre y dirección IP) que están siendo gestionados, descargar la MIB completa del Arduino y configurar el tiempo entre envíos de las peticiones SNMP a routers y servidores. 3.3 Conexiones del sistema: Hardware implicado El presupuesto total del hardware se presenta en la siguiente Tabla: Tabla 3-1 - Presupuesto Referencia Dispositivo Precio 1 Arduino MEGA 2560 20€ 2 ESP32 5€ 3 Display TFT 3.2 ILI9341 9€ 4 Display LCD 2x16 3€ 5 Ethernet Shield W5100 12€ 6 ESP8266 3€ 7 Sensor de humedad de suelo VH400 60€ 8 Sensor de temperatura TMP35 2€ 9 Sensores de audio KY-38 2€ 10 Voltímetro 1€ 11 Sensor capacitivo 1€ 12 Cables 4€ 13 Resistencias 4€ Total: 136€
Subsistema de gestión 1 Figura 4-7 – Función get1SNMP: envío de peticiones SNMP a los servidores 4.2.2 Servidor web HTTP El servidor web, implementado con la biblioteca WebServer, proporciona una interfaz para visualizar algunas de las variables obtenidas por SNMP y configurar el intervalo de sondeo de peticiones. Concretamente, la página principal (inicioWeb()) muestra: • Nombres y direcciones IP de dispositivos gestionados por SNMP. • Detalles de interfaces de red (descripción, y localización). • Un formulario para ajustar el intervalo de sondeo (configura la variable pollInterval). • Botón para descargar el fichero de la MIB. Por otra parte, he configurado los endpoints /modificarTm y /obtenerMIB que permiten, respectivamente, actualizar la frecuencia de sondeo y descargar la MIB desarrollada para este proyecto (Anexo A) que está almacenada en la memoria SPIFFS del ESP32. Para el desarrollo de esta página web, en el caso de la librería WebServer se debe realizar de una forma totalmente manual, creando una variable de tipo String que contenga el HTML con toda la página web, pudiendo hacer concatenaciones para personalizar los distintos campos.
49 49 Piloto de gestión para dispositivos IoT Adjunto algunas capturas de cómo he implementado todo el HTML de forma manual, este incluye al principio un apartado extenso de CSS para modificar los estilos: Figura 4-8 – Función inicioWeb() 1 Figura 4-9 – Función inicioWeb() 2 Y finalmente se define toda la lógica con las cabeceras habituales de HTML (body, main, h1, etc): Figura 4-10 – Función inicioWeb() 3
Subsistema de gestión 1 En la siguiente captura se puede apreciar la web proporcionada por el ESP32 descrita anteriormente. A ella se accede indicando la dirección IP del ESP32 desde cualquier cualquier navegador web. Figura 4-11 – Web de gestión del ESP32 4.2.3 Cliente MQTT El cliente MQTT (PubSubClient) publica las variables recopiladas con SNMP en los tópics definidos para cada dispositivo. Estos tópics los he configurado para que, dinámicamente, contengan el nombre (sysName) obtenido por SNMP de cada dispositivo gestionado. En primer lugar, dado que MQTT es un protocolo orientado a conexión, se debe establecer en primer lugar una conexión con el bróker. Esto se hace en la función conexionBroker, que reintenta la conexión cada 5 segundos en caso de fallar. Figura 4-12 – Función conexionBroker()
51 51 Piloto de gestión para dispositivos IoT Posteriormente, en la función loop() se realiza la publicación de los valores de todas las variables en sus correspondientes tópics de MQTT. En mi caso, he configurado los tópics para que se establezcan de forma dinámica con el nombre de cada dispositivo, que se obtiene con SNMP (variable sysName), de forma que cada tópic queda de la siguiente forma: /arduino1/{sysName}/bgp/{nombreInstancia} Para las variables del grupo bgp /arduino1/{sysName} /{nombreInstancia} Para el resto de variables En la siguiente captura se refleja la publicación de todas las variables en sus correspondientes tópics de MQTT, con un primer bucle para las variables de los routers, y un segundo bucle para las variables de los servidores Windows. Haber almacenado las variables asociadas a un mismo OID en un array facilita la gestión de estas, no solo en este caso de publicación con MQTT, sino también en el envío por UART al Arduino Mega y en las funciones de envío de peticiones de SNMP. Figura 4-13 – Publicación de variables con MQTT 4.2.4 Comunicación serial Recordemos que una de las particularidades de este trabajo es que las funcionalidades de cliente y servidor (agente) del protocolo SNMP se implementan en distintos dispositivos. El ESP32 ejerce de cliente y es el que envía peticiones de tipo GET y GET-NEXT a los servidores y routers gestionados, mientras que el Arduino MEGA es el agente SNMP que responde ante peticiones SNMP (GET, GET-NEXT y SET) para gestionar tanto sensores físicos y virtuales propios, como los dispositivos que gestiona el ESP32, ya que este le envía todos los valores que obtiene de los dispositivos que gestiona. De esta forma, tenemos un dispositivo que centraliza toda la información de gestión.
Subsistema de gestión 1 Los motivos de implementar ambas funcionalidades de forma separada en dos dispositivos muy diferentes son varios, y se enfocan en tres principales características: memoria RAM, número de pines y voltaje de funcionamiento. La idea inicial de este proyecto era implementarlo todo en un único dispositivo que pretendía ser un Arduino MEGA, sin embargo, tras varios meses adaptadando las librerías que ofrecen las funcionalidades de cliente y servidor para que funcionaran simultáneamente, conseguí una versión completamente funcional. No obstante, su limitada memoria RAM de 8 KB únicamente permitía la gestión de hasta 10 instancias de SNMP, lo que equivale a gestionar solo dos servidores. Insatisfecho con esta limitación, decidí separar las funcionalidades de clientes y servidor de SNMP en dos dispositivos distintos, lo que me ha permitido llegar a gestionar hasta 140 instancias de SNMP, teniendo todavía un gran margen para ampliar este número gracias a su memoria RAM de 520 KB. La elección del dispositivo encargado de implementar cada funcionalidad no ha sido aleatoria, sino que surgió como resultado de un análisis de las necesidades del proyecto, las capacidades de los dispositivos existentes en el mercado y el conocimiento adquirido durante todo el trabajo previo de implementación en el Arduino MEGA: • En primer lugar, cabe destacar que la funcionalidad de cliente SNMP demanda mucha mas memoria RAM que la de servidor. Esto es así debido al tratamiento de la memoria en cada caso, teniendo en cuenta que, para ambas funcionalidades, los atributos realmente necesarios de un mensaje SNMP son el OID y la IP origen o destino. Cuando un cliente recibe una petición del protocolo SNMP, comprueba el OID de la petición y envía el valor correspondiente a ese OID en el mensaje de respuesta. Por tanto, el par OID y IP solo permanece en memoria RAM el tiempo que tarde el Arduino en procesar la función de generar la respuesta. Una vez enviada, se eliminan de memoria RAM. En el caso del cliente, cuando envía una petición deberá mantener en memoria RAM el par OID e IP destino hasta que le llegue la respuesta y la procese. Por tanto, para un alto número de dispositivos gestionados, se llega a tener en un mismo instante un alto número de pares OID e IP en memoria RAM, provocando un uso excesivo de la misma. • Por otra parte, necesitaba tener un amplio número de pines digitales y analógicos para poder implementar un alto número de sensores, manteniendo la posibilidad de, en un futuro, implementar otros nuevos. Además, el dispositivo que implementara la funcionalidad de servidor es el que debe tener conectados todos los sensores. Por ello, el Arduino MEGA que cuenta con 54 pines digitales y 16 analógicos ha sido mi opción elegida para ejercer como servidor mientras que el ESP32, que cuenta únicamente con 33 pines digitales, pudiendo usar 12 de estos como analógicos, ejercer como cliente. • Además, una gran parte de los sensores necesitan 5V para funcionar correctamente lo que hace difícil utilizar el ESP32 para la gestión de un alto número de sensores, dado que este dispositivo entrega solo 3.3V en todos sus pines. En cambio, el Arduino MEGA sí proporciona 5V. Por tanto, una vez explicados los motivos por el que el ESP32 ejerce de cliente y el Arduino MEGA de servidor, puedo detallar el método utilizado para el intercambio de información entre ambos dispositivos. En este apartado, tenía varias opciones posibles: • Métodos físicos: I2C, UART (comunicación serial) y SPI. • Métodos inalámbricos: utilizando protocolos como MQTT, HTTP, o Bluetooth. Tras investigar todas las posibilidades y analizar sus ventajas e inconvenientes, decidí utilizar la comunicación serial (UART) al ser la opción más sencilla de implementar y siendo, además, totalmente válida para la necesidad de mi proyecto: enviar periódicamente los valores de 64 instancias de SNMP desde el ESP32 hasta el Arduino MEGA.
53 53 Piloto de gestión para dispositivos IoT La comunicación serial UART permite el intercambio asíncrono de datos entre dispositivos mediante dos cables: uno para transmitir (TX) y otro para recibir (RX). A diferencia de otros métodos de comunicación, el UART no requiere un reloj compartido, ya que ambos dispositivos deben estar configurados a la misma velocidad en baudios (en mi caso, 115200 bps) para interpretar correctamente los datos. El funcionamiento del UART se basa en tramas de datos que viajan, desde un pin de transmisión TX hacia el pin de recepción RX del destino e incluyen bits de inicio, bits de parada y, opcionalmente, bits de paridad para detectar errores. El receptor reconstruye los datos interpretando las señales eléctricas según el baud rate configurado y aunque es un protocolo de comunicación simple y eficaz, su alcance es limitado, ya que es vulnerable a pérdidas de señal, ruido electromagnético y degradación con la distancia, por ello, deben utilizarse cables lo más cortos posibles. Figura 4-14 – Funcionamiento de la comunicación UART Decidí utilizar este método de comunicación ya que tanto el Arduino MEGA como el ESP32 incluyen varios pines de transmisión y recepción (RX y TX) dedicados a UART, e implementan de forma nativa una librería oficial llamada Serial que simplifica el envío y recepción de datos entre estos pines mediante funciones como print() y read(). En la siguiente ilustración podemos ver como he conectado ambos dispositivos, utilizando un único cable que va desde el pin de transmisión del ESP32 al pin de recepción del Arduino MEGA. Como solo necesito este sentido de transmisión, no he utilizado un segundo cable para el sentido contrario. Figura 4-15 – Comunicación UART entre el ESP32 y el Arduino
Subsistema de gestión 1 Para aclarar la implementación de este envío de variables, voy a detallar la parte del código encargada de realizar esto. En primer lugar, se deben configurar en ambos dispositivos el mismo número de baudios con la siguiente llamada a la función begin de la librería Serial: Serial.begin(115200); Esto iniciará la comunicación serial entre ambos dispositivos, permitiendo el envío de cualquier tipo de información desde el ESP32 al Arduino MEGA. En la siguiente Figura se muestra la secuencia del envío de todas las variables que recibe el ESP32 de routers y servidores al Arduino MEGA. En este caso, el trigger que inicia la secuencia es la recepción en el ESP32 de todas las instancias de SNMP gestionadas. Una vez recibidas, se combinan todas ellas en una única variable de tipo String denominada resultado utilizando la función combinarVariables. El Arduino MEGA comprobará en cada iteración de su función loop si hay algún dato disponible en el puerto RX2, en cuyo caso almacenará dicha cadena en la variable SNMPvars. Por último, se realizará el proceso inverso: a partir de la variable obtenida por comunicación serial, se asignará cada valor a su variable correspondiente del Arduino MEGA. Figura 4-16 – Diagrama de flujo de la comunicación UART Para aclarar este proceso, quiero dejar constancia de la implementación de las funciones combinarVariables y asignarValoresDesdeCadena. Debido a la gran extensión de ambas funciones, voy a proporcionar la parte de código que se refiere únicamente a los valores de las instancias SNMP de los servidores, ya que la parte de los routers sigue el mismo procedimiento. En la función combinarVariables del ESP32 se consigue crear la variable resultado que será un String que contiene las 64 instancias de SNMP que gestiona el ESP32, siguiendo el formato: variable1*variable2*variable3*variable4*… Esto se consigue gracias a la función snprintf utilizando un buffer temporal que se concatenará finalmente a la variable resultado. Se aprecian hasta dos iteraciones, una por servidor, dado que todas las variables que
55 55 Piloto de gestión para dispositivos IoT coinciden con un mismo OID de SNMP se almacenan en un mismo Array (sysName1, nombreDisco1, time1…). Figura 4-17 – Función combinarVariables() Cuando el Arduino MEGA reciba dicha cadena, llamará a la función asignarValoresDesdeCadena que se encargará de guardar cada valor en su variable correspondiente. Gracias a separar cada variable con el signo *, esta tarea es realmente sencilla utilizando la función indexOf propia de las variables de tipo String. Cabe destacar que es crucial el orden en el que se concatenaron las variables en el ESP32 dado que es lo que se tiene en cuenta para hacer la asignación de las variables en el Arduino MEGA: Figura 4-18 – Función asignarValoresDesdeCadena()
Subsistema de gestión 1 Este proceso se realiza periódicamente, siempre que el ESP32 recibe nuevos datos de tipo SNMP de los routers y servidores. De esta forma, una vez finalizado todo este proceso el Arduino MEGA puede proporcionar mediante el protocolo SNMP los valores de todas estas instancias como si fueran propias. 4.2.5 Gestión del display TFT Uno de los principales problemas de desarrollar código en la plataforma Arduino es que esta no cuenta con un sistema integrado de depuración de código que facilite el análisis del flujo del programa para identificar y corregir errores. Para ello, se suele optar por el método tradicional de depuración con instrucciones de impresión (Serial.print) utilizando el Serial Monitor de Arduino IDE, sin embargo, es una práctica poco adecuada ya que no permite realizar análisis detallados y requiere un mantenimiento manual. Debido a esto y a mi deseo de tener un proyecto completamente portátil, decidí implementar dos displays para facilitar esta tarea y utilizarlos junto con el Serial Monitor. De esta forma, también se podrá consultar en tiempo real la secuencia de ejecución de ambos dispositivos sin necesidad de tenerlos conectados a un ordenador para utilizar el Serial Monitor, herramienta de visualización de logs del propio IDE de Arduino. Los displays utilizados han sido elegidos teniendo en cuenta las capacidades del Arduino MEGA y del ESP32, de esta forma, el Arduino gestiona un display LCD de tipo alfanumérico que cuenta con 2 filas de 16 caracteres cada una, mientras que en el ESP tenemos un display TFT LCD de 3,2 pulgadas con resolución de 240x320. En cuanto al display que gestiona el ESP32, tenemos un display con la tecnología TFT LCD que permite visualizar imágenes y personalizar pixel a pixel lo que se muestra en cada momento. Esto conlleva que, a su vez, sea más difícil de utilizar que el display de caracteres. En cuanto a la conexión, este utiliza únicamente comunicación I2C y necesita tener conectados los siguientes pines: Tabla 4-2 – Pines del LCD TFT Pin Nombre Función 1 CS Chip Select, habilita la comunicación en la línea de datos 2 DC Equivalente al registro de selección 3 RST Reinicia el módulo TFT 4 SDA Línea de datos SPI 5 SCL Línea de reloj SPI que sincroniza la transferencia de datos 6 VCC Alimentación de 5V 7 GND Tierra Figura 4-19 – Display TFT: pantalla de carga
57 57 Piloto de gestión para dispositivos IoT Figura 4-20 – Display TFT: establecimiento WiFi Para la gestión de este display, existen varias librerías de terceros muy utilizadas, como Adafruit o TFT_eSPI. En mi caso, he utilizado esta última y no me ha dado ningún problema ya que es compatible con el driver de mi display: el ILI9341. El driver de este tipo de displays TFT para Arduino es un elemento crucial ya que se encarga de la gestión total del mismo. Cada fabricante o modelo de display suele utilizar controladores, de forma que son gestionados con distintos comandos, por eso se necesita una librería compatible con el controlador específico. La información que muestra durante la ejecución del programa es la siguiente: Figura 4-21 – Diagrama de flujo del display TFT
Subsistema de gestión 2 5.1.1 Sensores físicos En este apartado, se detallarán los sensores físicos utilizados, proporcionando una descripción de cada uno, así como sus conexiones físicas y las librerías utilizadas para realizar las lecturas. 5.1.1.1 Sensor de temperatura Para el sensor de temperatura, se ha utilizado el modelo TMP36, un sensor ampliamente conocido por su bajo coste y simplicidad. Este cuenta con 3 pines: alimentación, tierra y salida analógica, tal como se muestra en la siguiente ilustración: Figura 5-2 – Sensor de temperatura TMP36 Las principales características de este sensor se pueden obtener en su datasheet [29], no obstante, las resumo a continuación en la siguiente tabla: Tabla 5-1 – Especificaciones TMP36 Parámetro Valor Rango de temperatura –40 °C a +125 °C Precisión típica ±1 °C Voltaje de alimentación (Vcc) 2.7 V a 5.5 V Corriente de operación < 50 µA Voltaje de salida a 25 °C 750 mV Escala de salida 10 mV/°C Offset de temperatura 500 mV a 0 °C La lectura del TMP36 se realiza midiendo el voltaje de salida en su pin VOUT, el cual varía linealmente con la temperatura. Para ello, este pin se debe conectar a una entrada analógica del Arduino (en mi caso, A0), que convierte la señal analógica en un valor digital utilizando su conversor ADC de 10 bits. El valor leído se transforma en voltaje, y luego se convierte a grados Celsius aplicando la fórmula: 𝐓𝐞𝐦𝐩𝐞𝐫𝐚𝐭𝐮𝐫𝐚 (°𝐂) = (𝐕𝐨𝐥𝐭𝐚𝐣𝐞 𝐞𝐧 𝐦𝐕 – 𝟓𝟎𝟎𝐦𝐕) 𝟏𝟎𝐦𝐕
65 65 Piloto de gestión para dispositivos IoT Esto se debe a que el TMP36 tiene un offset de 500 mV y una sensibilidad de 10 mV por grado. De esta forma, el microcontrolador puede interpretar fácilmente la temperatura ambiente. 5.1.1.2 Sensor de humedad de suelo Como sensor de humedad de suelo he utilizado una opción profesional, de la marca estadounidense Vegetronix, concretamente, el modelo VH400. Tuve la oportunidad de obtenerlo a buen precio en el mercado de segunda mano y puedo asegurar que la calidad de materiales y precisión de lectura es acorde a su precio. Al igual que el TMP36, se trata de un sensor analógico de 3 pines (alimentación, tierra y salida analógica): Figura 5-3 – Sensor de humedad del suelo VH400 Su datasheet [30] presenta las siguientes características principales: Tabla 5-2 – Especificaciones Vegetronix VH400 Parámetro Valor Tipo de medición Contenido volumétrico de agua (VWC) Precisión (a 25 °C) ±2% VWC Resolución 0–50% VWC Rango de temperatura de operación –40 °C a +85 °C Señal de salida Proporcional al nivel de humedad: 0–3 V Tiempo de arranque 400 ms Consumo de corriente <13 mA Alimentación 3.5 V a 20 V DC La lectura del sensor Vegetronix VH400 se realiza midiendo el voltaje de salida en su pin VOUT, el cual varía en función del contenido volumétrico de agua (VWC) presente en el suelo. Este pin se debe conectar a una entrada analógica del Arduino (en mi caso, A1), que convierte la señal analógica en un valor digital utilizando su conversor ADC de 10 bits.
Subsistema de gestión 2 El resultado se presenta en porcentaje de humedad de suelo, utilizando la siguiente formula: 𝐇𝐮𝐦𝐞𝐝𝐚𝐝 𝐝𝐞𝐥 𝐬𝐮𝐞𝐥𝐨 (%) = (𝟏𝟎𝟐𝟑 × 𝟏𝟎𝟎 ) 𝐕𝐨𝐥𝐭𝐚𝐣𝐞 (𝐦𝐕) 5.1.1.3 Sensor capacitivo Un sensor capacitivo permite detectar la presencia o proximidad de objetos o personas, sin necesidad de contacto físico. Su funcionamiento se basa en el comportamiento de un condensador: dos placas metálicas separadas por un dieléctrico, cuya capacidad dependerá de la superficie de las placas, la distancia entre ellas y el tipo de material dieléctrico. En el caso de este sensor, el cuerpo humano actúa como una de esas placas cuando se acerca, lo que modifica la capacidad del sistema. En Arduino, este sensor recibe una implementación y un tratamiento muy distinto a los sensores introducidos hasta ahora. Para construir este sensor se necesita: una resistencia de mínimo 100kΩ , un par de cables y un material conductor como el papel de aluminio. La conexión se realiza de la siguiente forma: cada pin de la resistencia se conectará a un pin digital distinto del Arduino y la lámina de aluminio se conectará a cualquiera de ellas. Cada conexión recibe un nombre distinto: aquella que tenga conectada la lámina de aluminio recibe el nombre de receptor (receiver) mientras que la otra se denomina emisor (sender): El pin emisor (sender) se encarga de generar una señal eléctrica que se transmite hacia el receptor (receiver) a través del circuito R (pordría añadirse un condesador obteniendo un circuito RC). Cuando se produce esta transmisión, el tiempo que tarda la señal en llegar al pin receptor dependerá de la capacitancia total del circuito. Figura 5-4 – Sensor capacitivo Al acercar la mano u otro objeto conductor al papel de aluminio, se altera la capacitancia y, por tanto, también el tiempo de transmisión. Es decir, la lectura de un sensor capacitivo consiste en medir el tiempo que tarda la señal en alcanzar el receptor, y ese tiempo varía según la capacitancia del entorno del sensor. De esta forma, la librería CapacitiveSensor [31] se encarga de medir este retardo de propagación.
67 67 Piloto de gestión para dispositivos IoT 5.1.1.4 Voltímetro Un voltímetro es un instrumento esencial en electrónica que mide la diferencia de potencial (voltaje) entre dos puntos de un circuito. Los voltímetros modernos suelen ser digitales, con amplias configuraciones y una pantalla donde se muestra el valor leído. Sin embargo, en el caso de Arduino, medir voltaje es muy sencillo: basta conectar un único cable desde el punto a medir a una de sus entradas analógicas (en mi caso A2). El ADC de 10 bits del Arduino convertirá la señal analógica (que varía entre 0V y 5V por defecto) en un valor digital (de 0 a 1023), el cual puede leerse en el código y convertirse a voltios con la siguiente fórmula de conversión: 𝐕𝐨𝐥𝐭𝐚𝐣𝐞 (𝐕) = 𝐕𝐚𝐥𝐨𝐫 𝐝𝐢𝐠𝐢𝐭𝐚𝐥 𝐀𝐃𝐂 (𝟎 − 𝟏𝟎𝟐𝟑) × 𝐕𝐨𝐥𝐭𝐚𝐣𝐞 𝐝𝐞 𝐫𝐞𝐟𝐞𝐫𝐞𝐧𝐜𝐢𝐚 (𝐕𝐫𝐞𝐟) 𝟏𝟎𝟐𝟑 Vref = 5V (Arduino Mega) 5.1.1.5 Sensores de audio El sensor de audio KY-38 permite medir la intensidad del sonido que capta mediante un micrófono electret. No obstante, no proporciona un valor en escala calibrada, como por ejemplo en dB, sino que detecta variaciones del voltaje provocadas por la presión de las ondas acústicas captadas por dicho micrófono. No mide, por tanto, una magnitud física de forma precisa, sino que realiza una estimación de la intensidad del sonido. Sus principales características pueden ser consultadas en su datasheet [32]. A continuación, se presenta un cuadro resumen de algunas de ellas: Tabla 5-3 – Especificaciones KY-38 Parámetro Valor Rango de frecuencias 20 Hz – 20 kHz (Sin calibración) Sensibilidad Ajustable con potenciómetro Voltaje de alimentación (Vcc) 3.73V a 5.5 V Corriente de operación 10 mA La lectura de este sensor se realiza midiendo el voltaje de salida en su pin analógico, que deberá estar conectado a una entrada analógica del Arduino (en mi caso, A3). Para realizar la lectura se utilizará directamente la salida del convertidor ADC del Arduino MEGA: Figura 5-5 – Sensor de sonido KY-38
Subsistema de gestión 2 𝐈𝐧𝐭𝐞𝐧𝐬𝐢𝐝𝐚𝐝 𝐝𝐞𝐥 𝐬𝐨𝐧𝐢𝐝𝐨 (−)= 𝐕𝐚𝐥𝐨𝐫 𝐝𝐢𝐠𝐢𝐭𝐚𝐥 𝐀𝐃𝐂 (𝟎 − 𝟏𝟎𝟐𝟑) 5.1.2 Sensores virtuales En este apartado se detallarán los sensores virtuales utilizados, en qué consiste cada uno de ellos y cómo se implementan. 5.1.2.1 Memoria RAM libre Uno de los principales problemas que tuve en el desarrollo de todo el código para el Arduino MEGA fueron los errores provocados por el desbordamiento de memoria RAM, provocando un reset automático del Arduino. Como consecuencia, decidí durante todo el desarrollo tener implementado este sensor como una variable mas gestionada mediante SNMP, teniendo configurado un evento para avisar si la memoria RAM libre bajaba de un cierto umbral. Para conseguir esto, no existen librerías oficiales para consultar la memoria RAM libre, sin embargo, MemoryFree [33] es una librería desarrollada por terceros que cumple a la perfección esta labor. Con una simple llamada a la función freeMemory de esta librería, obtenemos un entero indicando el número de bytes de memoria RAM disponibles en tiempo real. 5.1.2.2 Número de accesos Web Como vimos en apartados anteriores, una de las particularidades del programa desarrollado para el ESP32, es que, además de ejercer como cliente SNMP y cliente MQTT, implementa un servidor web que responde a peticiones HTTP proporcionando una web con información de los dispositivos que gestiona por SNMP el ESP32, tanto nombres como direcciones IP. Esta web permite, además, configurar el tiempo de muestreo del envío de peticiones SNMP y descargar el fichero completo de la MIB del proyecto. Por ello, me pareció interesante implementar una variable que contabilice el número de accesos a esta página web, pudiendo configurar eventos personalizados también para esta variable. Puesto que este dato es contabilizado por el ESP32, lo enviará junto con el resto de las variables obtenidas por SNMP al Arduino MEGA por comunicación serial (UART), para que este permita que su gestión por SNMP como si fuera una variable propia. 5.1.3 Interfaces de red: conexión Wifi y Ethernet Para las primeras versiones de este proyecto, utilicé como único adaptador de red para el Arduino MEGA, un Ethernet Shield W5100, que es una expansión muy utilizada para este dispositivo dada la facilidad de conexión e implementación y su bajo coste, alrededor de los 10 euros. Prácticamente cualquier librería o proyecto de la comunidad de Arduino que requiera conexión a Internet está preparada para utilizarlo. Figura 5-6 – Ethernet Shield W5100
69 69 Piloto de gestión para dispositivos IoT Este adaptador de red está basado en el chip Wiznet W5100, un controlador de red que implementa la pila de protocolos TCP/IP. Por tanto, contiene un microprocesador independiente que libera al microprocesador del Arduino MEGA, el ATmega2560, de procesar cualquier tarea relacionadas con estos protocolos y que debe comunicarse con él, utilizando para ello, el método de comunicación por interfaz SPI. Como se aprecia en la ilustración, cuenta con un conector RJ45 que admite velocidades de hasta 100 Mbps, además de incluir un adaptador para tarjetas microSD. Por otro lado, se alimenta directamente desde el Arduino y dispone de pines de expansión que replican los del microcontrolador base, de forma que no se pierden pines digitales y analógicos. Desde un punto de vista técnico, el W5100 puede manejar hasta cuatro conexiones simultáneas (sockets) TCP, y su configuración y gestión se realiza fácilmente con la librería Ethernet.h, que es una librería nativa de la plataforma Arduino para trabajar con este dispositivo. Trabajar con este adaptador ha sido realmente sencillo, no me ha dado ningún tipo de problema y responde muy rápido ante cualquier tipo de petición. Sin embargo, dado que yo deseaba que este proyecto fuera completamente portátil y que para trabajar con él no siempre he tenido disponible conectividad por Ethernet, decidí dotar al Arduino MEGA de conectividad WiFi utilizando un dispositivo ESP8266, ya a diferencia del ESP32, el Arduino no cuenta con un adaptador de red WiFi nativo. Figura 5-7 – Módulo WiFI: ESP8266 El ESP8266-01 no es mas que otro dispositivo muy utilizado de IoT, al igual que el Arduino MEGA o el ESP32, pero siendo una opción muy compacta. Implementa el chip ESP8266EX, un microcontrolador con WiFi integrado que a pesar de su tamaño cuenta con más memoria RAM, y frecuencia de reloj que el ATmega2560. Concretamente implementa una arquitectura Xtensa LX106 de 32 bits, con una frecuencia de reloj de hasta 80 MHz y 96 KB de memoria RAM. Lo que nos interesa de este dispositivo es que soporta distintos modos de operación: modo estación (STA) en el que se conecta a una red Wi-Fi existente como si fuera un cliente; modo AP (Access Point), en el que actúa como punto de acceso, generando su propia red inalámbrica para que otros dispositivos se conecten a él; y modo mixto (AP + STA), que permite usar ambas funciones de manera simultánea. En resumen, es capaz de funcionar como un adaptador de red independiente que dote de capacidad inalámbrica a otros dispositivos Arduino. Para ello, en vez de utilizar SPI como el W5100, este incorpora una interfaz UART (TX/RX) para comunicarse con otros dispositivos. El problema de utilizar este dispositivo es su poca compatibilidad con proyectos y librerías existentes ya que para realizar toda la gestión de conexiones, configuración y envío/recepción de datos se utiliza una librería desarrollada por terceros denominada WiFiEsp. Esta librería está destinada exclusivamente para facilitar la comunicación entre el ESP8266 y el Arduino MEGA, sin embargo, adaptar la librería de SNMP del Arduino MEGA para poder utilizar el ESP8266 ha sido todo un reto debido a las importantes diferencias de implementación con el W5100.
Subsistema de gestión 2 5.1.4 Gestión del display El display LCD que gestiona el Arduino cuenta con los siguientes pines: Tabla 5-4 – Pines del display LCD 16x2 Pin Nombre Función 1 VSS GND 2 VDD +5V 3 VO Contraste del display 4 RS Registro de selección 5 RW Modo lectura/escritura 6 E Enable (activa la lectura de datos) 7-14 D0-D7 Líneas de datos 15 LED+ Retroiluminación positiva 16 LEDRetroiluminación negativa A pesar de tener 16 pines, no es necesario utilizar todos ellos para gestionar este display gracias a que permite hasta tres modos de conexión: • Modo de 8 bits: Este modo requiere conectar todas las líneas de datos (D0-D7) a pines digitales del Arduino, permitiendo una comunicación más rápida, pero necesitando demasiados pines. • Modo de 4 bits: Este modo es el más utilizado, dado que solo se necesitan conectar las cuatro líneas de datos que comprenden entre D4 y D7. Aunque es más lento que el modo de 8 bits, es suficiente para la mayoría de aplicaciones. • I2C: Este modo de conexión requiere un módulo extra I2C, sin embargo, permite utilizar únicamente dos pines: SDA para datos y SCL para la sincronización (reloj). En mi caso, he decidido utilizar el modo de 4 bits tal como se aprecia en la siguiente Figura, dejando libres los cuatro primeros pines de datos: Figura 5-8 – Display LCD: establecimiento WiFi
71 71 Piloto de gestión para dispositivos IoT Para la gestión de este display, utilizo una librería nativa del IDE Arduino, llamada LiquidCrystal, realmente sencilla de utilizar y que consume muy pocos recursos. La información que muestra durante la ejecución del programa es la siguiente: Figura 5-9 – Diagrama de flujo del display LCD 5.2 Software: Implementación En este apartado se detallará de forma general y precisa todos los códigos que conforma el programa desarrollado para el Arduino MEGA, apoyándome en diagramas y proporcionando capturas de las partes de código que considero más relevantes para su comprensión. En primer lugar, se detallará la MIB elaborada para el agente SNMP de este subsistema, el Arduino MEGA. 5.2.1 Arduino MEGA 5.2.1.1 MIB Antes de presentar la estructura general del programa, considero necesario introducir en primer lugar todas las instancias del Arduino MEGA que podrán ser gestionadas por SNMP. A continuación, se presenta un diagrama con la MIB completa elaborada para el Arduino MEGA.
Subsistema de gestión 2 Figura 5-10 - MIBARDUINO Aunque la MIB completa puede ser consultada en el Anexo A, voy a realizar un resumen de la misma, así como de los grupos e instancias que la componen: El OID raíz utilizado (.1.3.6.1.4.1.36582) ha sido para utilizar el prefijo de enterprises, que no está preasignado puediendo ser adoptado por empresas. A partir de es OID se definen los 6 grupos de gestión que conforman la MIB elaborada para este proyecto, bajo el nombre de MIBARDUINO. En el primer grupo, informaciónDeGestión, tenemos instancias de tipo escalar relacionadas con la gestión general del Arduino, que son equivalentes a los escalares conocidos de la MIB-II [34] como sysName, sysUpTime, sysContact y sysLocation. En la siguiente tabla se definen todos los escalares que conforman este grupo, indicando el nombre, tipo, nivel de acceso (read-write o read-only) y una breve descripción:
73 73 Piloto de gestión para dispositivos IoT Tabla 5-5 – Grupo1: informacionDeGestion Nombre Tipo Acceso Descripción identificador DisplayString R/W Nombre identificador del Arduino localización DisplayString R/W Ubicación del Arduino direcciónIP IpAddress R/O Dirección IP asignada tiempoEncendido TimeTicks R/O Tiempo desde el encendido encargadoDeGestión DisplayString R/W Nombre de la persona responsable teléfonoContacto DisplayString R/W Teléfono de contacto correoContacto DisplayString R/W Correo del responsable En el segundo grupo, valoresLeidos, tenemos toda la información relacionada con la gestión de los sensores físicos y virtuales que implementa el Arduino MEGA. Toda esta información está representada en una tabla de SNMP que contiene las siguientes instancias: Tabla 5-6 – Grupo 2: valoresLeidos Nombre Tipo Acceso Descripción indice Integer R/O Índice de fila descrSensor DisplayString R/O Descripción del sensor a leer valorLeido Integer R/O Valor leído del sensor instanteLectura TimeTicks R/O Instante del valor leído tiempoMuestreo Integer R/W Intervalo en segundos entre lecturas del sensor (configurable) El tercer grupo, eventos, contiene las instancias necesarias para configurar eventos personalizados a cada uno de los sensores del grupo anterior, para enviar alertas a modo de mensajes TRAP de SNMP en caso de superar o. bajar de un cierto umbral establecido, pudiendo establecer la dirección IP del dispositivo que recibirá dicha alerta. Toda esta información está representada en una tabla de SNMP que surge como inspiración de la RMONMIB [35] y que contiene las siguientes instancias:
Subsistema de gestión 2
81 81 Piloto de gestión para dispositivos IoT 6 LIBRERÍA DE AGENTE SNMP PARA ARDUINO La civilización no suprime la barbarie, la perfecciona. .- Voltaire - n este capítulo, se abordará el desafío que ha supuesto la implementación de un agente SNMP en dispositivos Arduino, tanto los problemas técnicos encontrados como su proceso desarrollo e implementación. Cabe destacar que este proceso requirió la creación de una librería propia, desarrollada desde cero, pero tomando como referencia una librería desarrollada por un particular, cuyo repositorio original ya no se encuentra disponible, denominada Agentuino [37]. Partiendo de esta librería como base, he ampliado significativamente sus capacidades con las siguientes mejoras: incorporación de nuevos tipos de datos SNMP, soporte para mensajes GET-NEXT, la adaptación al módulo WiFi del ESP8266 y optimizaciones para manejar un alto número de instancias. 6.1 Diseño general En los siguientes apartados se detallarán, respectivamente, los ficheros que componen el programa completo desarrollado para el Arduino MEGA 2560, y el flujo de ejecución especificado para la librería personal que he denominado agenteSNMP. 6.1.1 Estructura El código está dividido en 6 ficheros distintos: • Agente.ino: Es el fichero principal o sketch que contiene las funciones setup y loop con la comprobación periódica de peticiones SNMP, la lectura de sensores y la comprobación de eventos. • AgenteSNMP.cpp: Fichero principal de la librería agenteSNMP desarollada, contiene todas las funciones que implementan la lógica del protocolo SNMP: recepción y parseo de peticiones, envío de respuestas y envío de TRAPs. • AgenteSNMP.h: Fichero de cabecera que define todas las estructuras necesarias para el funcionamiento de la librería: clase principal AgenteSNMP, estructura PDU que será utilizada en todas y cada una de las funciones de la librería, códigos de error, tipos de mensajes, tipos ASN, todo con la codificación BER. Además, en este fichero se definen las funciones auxiliares de codificación y decodificación de los distintos tipos de variables posibles: INTEGER, IPADDRESS, OCTET STRING, TIME TICKS, COUNTER, entre otros. Estas funciones estaban muy bien implementadas en la librería original Agentuino, excepto aquellas correspondientes al tipo IPADDRESS que tuve que implementar. • MIB.h: Contiene la declaración de los 140 OIDs de la MIB correspondientes a cada uno de los objetos. Todos presentan el siguiente formato: E
Librería de agente SNMP para Arduino Figura 6-1 – Declaración de OIDs (ejemplo R1) Este ejemplo presenta los OIDs del grupo nodosDeRed especificando los correspondientes al router R1 para todos sus objetos: índice, nombre, tiempo de encendido, localización, puerto gestionado, estado y bytes recibidos en dicho puerto. • Variables.cpp: Contiene la declaración de las 140 instancias que almacenan los valores de cada objeto de la MIB. En este caso realizo las declaraciones de cada grupo utilizando arrays cuyos nombres coinciden con los objetos SNMP definidos realmente en las MIBs estándar. Especifico en el siguiente ejemplo las variables que definen el grupo nodosDeRed: Figura 6-2 – Declaración de variables: nodosDeRed • Variables.h: Contiene la definición de las 140 instancias que almacenan los valores de cada objeto de la MIB. Siguen el mismo formato de array y se exportan para ser utilizadas en cualquier parte del programa: Figura 6-3 – Definición de variables: nodosDeRed 6.2 Análisis de la interfaz En los siguientes apartados se detallarán las principales funciones de la librería, encargadas de implementar toda la lógica del protocolo SNMP: recepción de peticiones, envío de respuestas, envío de mensajes de tipo TRAP e implementación soporte a peticiones de tipo GET-NEXT. Cada una de estas funciones ha sido codificada desde 0, tomando como referencia, excepto para la función de GET-NEXT, la librería original Agentuino, adaptando tanto el algoritmo utilizado para facilitar el trabajo con un alto número de objetos SNMP, como la interfaz del objeto udp utilizado, para permitir la conexión por WiFi utilizando el ESP8266.
83 83 Piloto de gestión para dispositivos IoT 6.2.1 Peticiones SNMP Cada vez que se detecta en el buffer del ESP8266 nuevos datos recibidos, se llama a la función PduRecibida(), que se encarga de crear el objeto de tipo pdu donde se almacenarán de forma ordenada todos los datos del paquete recibido. Además, tras conseguir el objeto pdu con todos sus campos completos, se comprobará la variable local correspondiente al OID de la petición, para obtener el valor a enviar en la respuesta. Sin embargo, esta función no es la encargada de parsear el paquete recibido, para ello, llama a otra función con la que comparte nombre, pasándole un puntero al objeto pdu creado: Figura 6-4 – Función PduRecibida() Esta función será la encargada de parsear el paquete recibido completando los campos del objeto pdu inicialmente vacío. Como se observa en el siguiente fragmento de código correspondiente al inicio de esta función, se declaran las principales variables que definen los campos recibidos de un paquete SNMP: comunidad, secuencia, versión, tipo de pdu, varbind, OID y valor. Posteriormente se obtienen los datos del buffer en la variable _paquete, comprobando que el tamaño del paquete no excede el máximo configurado y que efectivamente se trata de un paquete SNMP, verificando que el primer byte es 30: Figura 6-5 – Función PduRecibida() - 2 A continuación, se asignan por orden todas las variables anteriores, teniendo en cuenta el orden de los bytes que definen un paquete de petición de SNMP: tamaño del sequence, versión, comunidad, tipo, etc. Como se aprecia, para cada uno de los campos existe el par valor y tamaño asociado:
Librería de agente SNMP para Arduino Figura 6-6 – Función PduRecibida() - 3 Por último, se definen numerosas comprobaciones para los siguientes tipos de errores posibles: • Tamaño de la comunidad: Se comprueba si el tamaño de la comunidad (comTam) excede la longitud máxima permitida definida. Esta validación se hace para garantizar que la comunidad no desborde los buffers internos. Si se detecta que el tamaño es demasiado grande, se devuelve un estado de error propio del sistema que impide enviar una respuesta a esta petición. • Comunidad en función del tipo de PDU: Se compara byte a byte la comunidad recibida con las cadenas esperadas (_getComunidad y _setComunidad) en función del tipo de PDU: GET, SET o GETNEXT. Si la comunidad no coincide exactamente con la esperada para ese tipo de PDU, se asigna el error estándar de SNMP conocido como noSuchName. En este caso sí se envía una respuesta para enviar ese error. • Tamaño del OID: Se verifica verifica si la longitud del OID de la petición (obiTam) supera el valor máximo definido. Nuevamente, esta comprobación es importante para evitar desbordamientos de memoria. • Tamaño del valor de la instancia: Finalmente, se realiza una comprobación similar para el tamaño del valor asociado al OID, asegurándose de que no supere el máximo permitido para no poder desbordar la memoria del Arduino A continuación, se presenta la comprobación del tamaño y valor de la comunidad recibida. Como se aprecia, hay dos comprobaciones, una para cada tipo de PDU, ya que existen dos comunidades distintas para los tipos SET y GET (incluyendo GET-NEXT).
85 85 Piloto de gestión para dispositivos IoT Figura 6-7 – Función PduRecibida - 4 Por último, se obtiene el valor recibido, en cuyo caso será útil y no nulo para las peticiones de tipo SET, ya que en las de tipo GET este campo estará vacío. La función devolverá éxito o error dependiendo de las comprobaciones previas: Figura 6-8 – Función PduRecibida() - 5 Volviendo con la función inicial PduRecibida, tras tener el objeto pdu completo, se deberá comprobar finalmente a qué variable local corresponde el OID recibido en la petición. Para ello, he utilizado la siguiente lógica para trabajar con un alto número de instancias: un único bucle for cuyo único objetivo consisten en obtener el índice, dentro de los 140 objetos posibles de la MIB, que indica la posición exacta de la varariable asociada al OID recibido. Una vez obtenido ese número, realizo comprobaciones de este índice, agrupando aquellas variables que son de un mismo tipo para minimizar el número de comparaciones. Una vez obtenida la variable exacta cuyo valor deberá contener el mensaje de respuesta, en caso de haber recibido una petición GET o GET-NEXT, o cuyo
Librería de agente SNMP para Arduino valor deberá ser modificado, en caso de haber recibido un SET, se llamarán a las funciones de decodificación o codificación, respectivamente, utilizando los siguientes arrays que juegan un papel imprescindible en esta lógica: Defino, en primer lugar, un array que contiene punteros a todas las variables locales que contienen los OID de toda la MIB (declaradas en MIB.h), en el orden léxicográfico exacto: Figura 6-9 – Array auxiliar de OIDs Posteriormente, defino un array de punteros a todas las variables locales que contiene las instancias de todos los objetos de la MIB (declaradas en Variables.cpp), siguiendo nuevamente el orden lexicográfico:
87 87 Piloto de gestión para dispositivos IoT Figura 6-10 – Array auxiliar de variables locales Gracias a esto, podemos definir para las 140 variables locales posibles, las correspondientes comprobaciones agrupando aquellas que, siguiendo el orden establecido, son del mismo tipo. De esta forma, cuando el OID recibido coincida con alguno de los definidos localmente, se obtiene el índice ‘i’ exacto en el rango de 1 a 140 que determinará la variable local a la que le corresponde dicho OID. Una vez determinada dicha variable, en función del tipo de la PDU recibida, se llamará a la función de decodificar si es tipo SET o codificar si es tipo GET o GET-NEXT. En ambos casos, se pasará la referencia a dicha variable, utilizando el array mibV que contiene punteros a todas las variables locales, y además, se incluirá el tamaño de esta. Como se puede observar, para aquellas variables que se definen en la MIB como read-only, es posible configurarlas para que no se pueda cambiarles su valor mediante una petición SET, para ello, en la parte correspondiente donde se llamaría a la función de decodificar, en vez de esto, se incluye en el campo error del objeto pdu el error read-only.
Librería de agente SNMP para Arduino Figura 6-11 – Función PduRecibida - 6 Finalmente, se llamará a la función PduRespuesta que se encargará de crear la PDU de respuesta a partir del objeto pdu. 6.2.2 Respuestas SNMP En la función de respuesta, se creará todo el paquete a enviar en la variable _paquete, teniendo nuevamente en cuenta el orden de los bytes que definen un paquete SNMP. Dada la gran similitud con la función de PduRecibida donde se realiza el proceso inverso, incluyo únicamente el inicio de esta función. Podemos ver que la variable _paquete que posteriormente será la que se envíe, se va construyendo con sus primeros bytes: el byte 30 que indica que es un sequence (SNMP_SINTAXIS_SEQUENCE), el tamaño del mismo, etc.
89 89 Piloto de gestión para dispositivos IoT Figura 6-12 – Función PduRespuesta() Finalmente, se utilizan las funciones del objeto udp cuyo tipo dependerá del tipo de conexión que se haya configurado: WifiEspUdp en caso de configurar el Arduino con WiFi y EthernetUDP en caso de utilizar conexión física: Figura 6-13 – Función PduRespuesta() - 2 6.2.3 Traps SNMP La función de envío de mensajes de tipo TRAP se llama de la siguiente forma, indicando el mensaje descriptivo a enviar, la IP destino que recibirá el mensaje, el tiempo que el Arduino llevaba encendido cuando se envió dicho mensaje, y los OID asociados: Figura 6-14 – Envío de TRAP
Subsistemas de interfaz de usuario y persistencia: pruebas finales respectivamente por el ESP32 y el Arduino MEGA 2560, un ordenador portátil que ejecuta todas las herramientas y servicios externos correspondientes a los subsistemas de persistencia e interfaz de usuario: el escenario de GNS3 con los routers que ejercerán de agentes de SNMP, el bróker MQTT para recibir los datos obtenidos por el ESP32 e insertarlos en la BBDD, el servicio Maven para el servidor REST y el servicio PostgreSQL como base de datos relacional. La aplicación móvil detallada en este capítulo la estaré ejecutando en mi teléfono personal que tiene el SO Android. Todos estos dispositivos estárán conectados para realizar estas pruebas al router de mi casa mediante conexión inalámbrila WiFi, si bien es cierto que, para el desarrollo del subsistema de gestión definido por el Arduino MEGA, he utilizado mucho la conexión por cable Ethernet al ser más estable. 7.1 Servidor REST: Spring Boot Para introducir el desarrollo del backend REST en Spring Boot, voy a presentar las principales dependencias del proyecto que determinan las principales características de este. Para ello, es de notable importancia el fichero de configuración de un proyecto Java que utiliza el sistema de construcción Maven: pom.xml (Project Object Model): • spring-boot-starter-data-jpa: Permite trabajar con bases de datos relacionales utilizando JPA, así como el mapeo de las relaciones de una base de datos a las propias entidades de Java gracias al uso de anotaciones como @Entity, @Table y @Id. De esta forma, se evita declarar sentencias SQL en el propio código para realizar las operaciones CRUD. • spring-boot-starter-web: Es una de las dependencias más comunes en proyectos de Spring Boot ya que permite crear un servidor web basado en la API REST. • postgresql: Es el driver necesario para que la aplicación Java pueda conectarse a una base de datos PostgreSQL y ejecutar operaciones CRUD sobre ella. • spring-boot-maven-plugin: Es un plugin de Maven que permite el embaquetado, ejecución y despliegue de aplicaciones de Spring Boot. Facilita la configuración y ejecución de la aplicación evitando un despliegue manual de la misma. • springdoc-openapi: Permite integrar Swagger de forma automática en aplicaciones Spring Boot, una herramienta que genera documentación del proyecto y provee de una interfaz para consultarla vía web. Aprovechando esta última dependencia, voy a utilizar la herramienta Swagger para detallar los controladores de mi proyecto Spring Boot que son aquellas clases que reciben, procesan y responde a las peticiones HTTP recibidas por el servidor REST. Dado que decidí añadir persistencia para los grupos servidores, nodosDeRed y informacionBGP de mi MIB, estos controladores responderán ante las URL asociadas a estos grupos:
97 97 Piloto de gestión para dispositivos IoT Figura 7-2 – Servidor REST: endpoints disponibles. Los tres controladores presentan la misma lógica implementada para cada endpoint respondiendo a mis necesidades personales para el desarrollo de determinados apartados de la aplicación móvil que requieren los siguientes métodos: obtener todos los históricos de todos los servidores, obtener solo los últimos valores de todos los servidores y obtener el historial de un único servidor. Dado que en mi caso no necesito implementar ningún tipo de operación o algoritmo sobre los datos obtenidos de la base de datos, no he utilizado la capa Service que es tan utilizada en proyectos de Spring Boot para ejecer de intermediario entre la capa de controladores y la de acceso a datos (normalmente, capa de repositorios), implementando todo lo referente a la lógica de negocio, operando sobre los datos obtenidos de la BBDD. De esta forma, presento a continuación un diagrama de clases que representa las principales entidades o POJOs, controladores y repositorios que componen mi aplicación de Spring Boot. Es importante destacar que, las clases pertenecientes a los repositorios son generadas en tiempo de ejecución por el componente de Spring Boot denominado Spring Data JPA. Estas clases implementan las interfaces que de estos repositorios que sí he tenido que generar manualmente y utilizan atributos genéricos como EntityManager que es el encargado de realizar las operaciones en la base de datos. El diagrama de clases queda representado, por tanto, por clases asociadas a los controladores, que procesan las peticiones y envían las respuestas, clases asociadas a las entidades o POJOs, que representan los datos de cada entidad, y clases que implementan las interfaces definidas para los repositorios, que se encargan de la interacción con la base de datos:
Subsistemas de interfaz de usuario y persistencia: pruebas finales Figura 7-3 – Diagrama de clases del proyecto Spring Boot
99 99 Piloto de gestión para dispositivos IoT 7.2 Aplicación Móvil: VManager Figura 7-4 – Diagrama de clases de la aplicación VManager Para desarrollar la aplicación móvil con Android Studio, he utilizado para el diseño de la misma un modelo modular con el uso de fragmentos y una única actividad para iniciar sesión. De esta forma, temenos 6 fragmentos distintos, uno para cada grupo de la MIB, y un fragmento extra que ejerce de apartado de inicio de la aplicación.
Subsistemas de interfaz de usuario y persistencia: pruebas finales 7.2.1 Inicio de Sesión El apartado de inicio de sesión, tal como he descrito anteriormente, corresponde a la única actividad de la aplicación Android, cuyo objetivo es el acceso al resto de apartados de la propia aplicación. Como no he querido poner el foco de atención en el desarrollo de una aplicación robusta y profesional, sino crear una interfaz fácil de usar y personalizada que permitiera la gestión de todo el sistema, a la vez que ponía en práctica los conocimientos adquiridos de una asignatura que he cursado durante la realización de este trabajo, he decidido que el inicio de sesión sea tan simple como comprobar en local el par usuario y clave, sin implementar un medio de control de acceso vía token, ni almacenar los usuarios en la misma BBDD. De esta forma, se presenta en la siguiente captura el apartado de inicio de sesión, cuyo layout asociado, activity_main.xml, presenta el siguiente formato visual, con una imagen de fondo personalizada que será visible en todos los apartados de la aplicación: Figura 7-5 – Página de inicio de sesión
101 101 Piloto de gestión para dispositivos IoT 7.2.2 Apartado de inicio Una vez accedemos a la aplicación, se nos presenta un apartado de inicio correspondiente al fragmento InicioFragment, donde podemos encontrar: un diagrama de conexiones de toda la implementación física del sistema (Arduino MEGA y ESP32), apartados de configuración de direcciones IP, tanto del Arduino MEGA como del servidor REST que hace de backend, y una imagen con forma de campana que nos permite acceder a los mensajes de tipo TRAP que se han recibido en el propio teléfono móvil. En el apartado técnico, para la recepción de los mensajes de tipo TRAP, se utiliza la librería SNMP4j que permite implementar, entre varias otras funcionalidades de SNMP que veremos en apartados posteriores, un servidor SNMP que escucha peticiones en el puerto 162 de forma ininterrunpida. Cada vez que se recibe un mensaje de tipo TRAP, se presenta un Toast avisando de ello y se reproduce un sonido predeterminado de Android, gracias a la clase RingtoneManager. En cuanto a las direcciones IP configurables, se utilizan las SharedPreferences para tenerlas accesibles durante cualquier otro fragmento de la aplicación. Respecto al apartado visual, su layout asociado, fragment_inicio.xml presenta la siguiente visual: Figura 7-6 – Página principal Por otra parte, desde este y cualquier otro apartado de la aplicación, tendremos accesible un Navigation Drawer que consiste en el siguiente menú lateral deslizable que permite visualizar todos los apartados de la aplicación y acceder a cualquiera de ellos.
Subsistemas de interfaz de usuario y persistencia: pruebas finales 7.2.3 Grupo 1: Información El primer grupo de la MIB del Arduino, informacionDeGestion, se presenta en un formato de tabla con todos los escalares de este grupo, de forma que, cuando se accede a este apartado, se envía una petición al Arduino MEGA para obtener cada uno de ellos. Para el envío de peticiones de SNMP, volvemos a utilizar la librería SNMP4j, en este caso a través de una clase auxiliar, SnmpHelper, creada con los métodos necesarios para enviar mensajes de tipo GET y SET, llamados sendSnmpRequest y setSnmpValue, respectivamente. Ambos métodos requieren la dirección IP destino, en este caso será siempre la del Arduino MEGA, y el OID que irá en la petición, que serán los correspondientes a los escalares de este grupo. Visualmente, queda representado tal como se aprecia en la siguiente captura: Figura 7-7 – Apartado del grupo informacionDeGestion
103 103 Piloto de gestión para dispositivos IoT 7.2.4 Grupo 2: valoresLeídos Para el grupo 2 de la MIB, valoresLeidos, volvemos a tener la misma lógica implementada para las consultas de SNMP los métodos de la clase SnmpHelper. No obstante, el apartado visual cambia notablemente a un formato de tipo lista, mediante el uso de un Recycler View que permite definir cada elemento de la lista, utilizando el fichero de layout auxiliar item_valoresleidos. Encontraremos, por tanto, un ítem para cada uno de los 8 sensores, con todos sus campos definidos para este grupo de la MIB (índice, descripción, valor, instante de lectura y tiempo de muestreo), y permitiendo cambiar el tiempo de muestreo de cada sensor utilizando el botón “CambiarTM”. Al pulsarlo, se abrirá una ventana de diálogo que permite introducir el valor deseado, mostrando el actual configurado. Al aceptar, se enviará el mensaje de tipo SET correspondiente a la dirección IP del Arduino MEGA, configurada en el apartado de inicio de la aplicación: Figura 7-8 – Apartado del grupo valoresLeidos
Subsistemas de interfaz de usuario y persistencia: pruebas finales 7.2.5 Grupo 3: Eventos En el grupo 3, eventos, volvemos a tener un importante cambio visual en el layout asociado, fragment_eventos.xml, a este nuevo fragmento (EventosFragment), utilizando nuevamente la misma lógica con el uso de SNMPJ4, enviando las consultas directamente al Arduino MEGA. Como se aprecia en la siguiente captura, tenemos un formato de formulario que nos permite configurar cualquiera de los 5 eventos disponibles en el Arduino MEGA, rellenando uno a uno, los campos correspondientes: el evento a configurar, el índice del sensor para el que se configura dicho evento, la descripción que se enviará al generarse la alerta, los umbrales a controlar y la dirección IP destino que recibirá la alerta (mensaje de tipo TRAP): Figura 7-9 – Apartado del grupo eventos Además, al seleccionar en el checkbox cada uno de los eventos configurables, se cargarán los valores actualmente configurados en el Arduino MEGA para cada uno de sus correspondientes campos, es decir, se envían consultas SNMP al Arduino MEGA, para obtener los campos del evento seleccionado.
105 105 Piloto de gestión para dispositivos IoT 7.2.6 Grupo 4: Servidores A partir de este grupo 4, servidores, hay un cambio relevante en la lógica implementada ya que estos últimos 3 grupos no se gestionan mediante consultas de SNMP directamente al Arduino MEGA, sino que se ataca al servidor REST que corre el backend Spring-Boot. Es decir, se hacen peticiones de tipo HTTP a los endpoints detallados en el anterior apartado 4.2.3.3 (Servidor REST: Spring Boot). Por tanto, no se utiliza la librería SNMP4J, sino OkHttp para realizar peticiones HTTP al servidor REST cuya dirección IP se configura, como se vio anteriormente, desde el apartado de inicio de la aplicación. Además, se utiliza la librería JSON para procesar las respuestas fácilmente. En este caso, los endpoints del servidor REST que se consultan son /servidores/últimos, para obtener los últimos valores de los dos servidores gestionados y /servidores/historial/, para obtener todos los valores de cada uno de ellos. De esta forma, tenemos la siguiente visual, definida en el layout fragment_servidores.xml, que nos muestra en formato de lista los últimos valores obtenidos de todos los campos de cada servidor, pudiendo acceder, mediante el botón Historial, a todos los valores anteriores, ordenados temporalmente: Figura 7-10 – Apartado del grupo servidores
Subsistemas de interfaz de usuario y persistencia: pruebas finales
113 113 Piloto de gestión para dispositivos IoT 8 CONCLUSIONES Y LÍNEAS FUTURAS La paciencia es amarga, pero sus frutos son dulces. - Proverbio persa En los últimos años, el Internet de las Cosas ha permitido la transformación digital de múltiples sectores, desde la industria y la sanidad, hasta el transporte, el hogar e incluso las ciudades. Sin embargo, su aplicación en el ámbito de la gestión de redes de telecomunicación ha sido limitada y poco explorada. En este sentido, este trabajo ha demostrado la posibilidad de implementar protocolos como SNMP, que originalmente no están diseñados para dispositivos con recursos tan limitados, permitiendo una versión completa de este protocolo y llegando a gestionar hasta 140 instancias, con margen de memoria para aumentar aún más este número. Además, se han llevado a la práctica tecnologías muy utilizadas como: Eclipse Mosquitto como bróker del protocolo MQTT para la comunicación de dispositivos IoT, PostgreSQL como base de datos para la persistencia de la información de gestión, Spring Boot como framework para el desarrollo de un backend que integrara la base de datos con la aplicación Android, desarrollada en Android Studio. Asimismo, se ha utilizado GNS3 como simulador de redes, que ha permitido la creación de un escenario con routers gestionables sin necesidad de hardware físico. Finalmente, se han puesto a prueba las funcionalidades del sistema con el stack conformado por SNMP-Exporter, Prometheus y Grafana como interfaz de monitorización estándar, siendo ampliamente utilizada en la gestión profesional de infraestructuras empresariales. Gracias a todas estas tecnologías y protocolos, el sistema desarrollado e implementado en este trabajo consigue administrar, tanto sensores físicos y virtuales, como dispositivos de red, concretamente routers y servidores. Desde el marco clásico de gestión definido por la ISO como FCAPS (Fault, Configuration, Accounting, Performance, Security) podemos clasificar varias de las funcionalidades de mi sistema: • Gestión de fallos (F): dentro de la gestión de fallos, podemos destacar el conjunto de alertas configurables que permite detectar eventos críticos en cualquiera de los sensores implementados y notificarlos a través de mensajes de TRAP de SNMP a cualquier dispositivo destino. • Gestión de la configuración (C): centrándonos en la gestión de la configuración, destaca la posibilidad de modificar mediante mensajes de tipo SET de SNMP, tanto información general de gestión como la localización y el gestor asociado, así como su teléfono y correo de contacto, como los tiempos de lectura de cada uno de los sensores implementados. Además, también permite modificar vía Web el tiempo entre envíos de las peticiones SNMP del ESP32 a los routers implementados en GNS3. • Gestión de la contabilidad (A): si bien es cierto que el control del número de accesos a la Web del ESP32 o el control del número de paquetes recibidos en las interfaces gestionadas de los routers podrían ser considerados funciones de este apartado, considero que ambos destacan, desde el punto de vista de mi trabajo, en el marco de gestión de la seguridad. • Gestión del rendimiento (P): en este caso destaca la propia gestión de los sensores físicos y virtuales implementados, con especial mención a la gestión de la memoria RAM libre en el Arduino MEGA. • Gestión de la seguridad (S): tal como se mencionó anteriormente, considero dentro de este marco tanto el control del número de accesos a la Web proporcionada por el ESP32, como el control del número de paquetes recibidos en las interfaces de los routers gestionados. También podría incluirse en este marco de gestión el control del número de mensajes de las sesiones BGP.
Conclusiones y Líneas futuras Sin embargo, son muchas las funcionalidades y mejoras que deseo implementar en un futuro, tanto a nivel de hardware como de software: • Permitir la gestión de un número dinámico y configurable de objetos gestionados de SNMP. • Permitir modificar, para cada uno de los objetos gestionados, tanto el OID como la dirección IP del agente SNMP destino, en vez de tener que definir ambos parámetros en el propio código. • Permitir la configuración de alertas para cualquiera de los objetos gestionados, no sólo para el grupo de sensores. De esta forma, se liberaría de carga a los dispositivos de red gestionados dado que la comprobación continua de los umbrales la realizaría el Arduino MEGA. • Mejorar la interfaz Web proporcionada por el ESP32 para incluir, además del formulario ya existente para modificar el tiempo entre envíos de las peticiones SNMP, nuevos formularios para poder configurar las mejoras comentadas anteriormente. • Desde el punto de vista de diseño, me gustaría diseñar mi propia PCB para construir un dispositivo más compacto y con una apariencia más profesional y trabajada, que implemente los microcontroladores del Arduino MEGA, ESP32 y ESP826, e incluso los sensores en la propia PCB para evitar el elevado número de cables y espacio consumido por todos los componentes del sistema. • Por último, desearía implementar para ambas librerías de SNMP la capacidad de cifrado permitiendo, por tanto, la actualización de ambas librerías de SNMP a la versión 3 de este protocolo. Debo destacar que todos los códigos desarrollados para este Trabajo de Fin de Grado se encuentran disponibles en un repositorio de Github, disponible en el Anexo C, que creado para que cualquier persona pueda replicarlo o reutilizar cualquiera de sus partes. Este incluye las implementaciones realizadas para el Arduino MEGA y el ESP32, incluyendo la librería desarrollada AgenteSNMP, el escenario completo de GNS3, el script de cliente MQTT, todos los códigos del backend de Spring Boot y de la aplicación de Android y los ficheros de configuración para poder realizar la instalación completa del stack conformado por SNMP-Exporter, Prometheus y Grafana. Por otro lado, quiero dejar constancia de dos futuras mejoras que planeo realizar en base a algunas investigaciones que he llevado a cabo para expandir el uso de mi sistema en dos ámbitos de gran interés: la detección de ataques y la gestión por intenciones. En [39] y [40], Gayathri y Boyar, respectivamente, demuestran cómo el protocolo SNMP puede ser utilizado para detectar ataques DDoS mediante el análisis de los datos obtenidos a través de este protocolo utilizando diferentes técnicas estadísticas y del ámbito del Machine Learning. En primer lugar, Gayathri realiza numerosas pruebas con la distancia de Kullback–Leibler (KLD) y el uso de Moving Target Defense (MTD) para la detección automática de amenazas. Por otro lado, Boyar utiliza los principales objetos de SNMP que definen el tamaño y la frecuencia de los paquetes recibidos para alimentar modelos de machine learning de redes neuronales, KNN y C5.0, logrando una alta tasa de detección con un bajo número de falsos positivos De esta forma, mi sistema pretende ser un punto de partida que proporcione de forma centralizada todas las instancias de SNMP necesarias para la detección de ataques en una red compleja. Para ello, se debería implementar otro subsistema de gestión que implementara alguna de las técnicas de Machine Learning mencionadas previamente. Por otro lado, hay un tema innovador del ámbito de la gestión de redes que despierta en mí un gran interés: la gestión por intenciones, o Intent-Based Networking (IBN). Concretamente, se propone un cambio de paradigma en el que el gestor no se ocupa de configurar directamente los dispositivos, sino de expresar lo que desea que la red haga, por ejemplo, garantizar baja latencia para cierto tipo de tráfico o priorizar determinados servicios, y el sistema se encarga automáticamente de traducir esas intenciones en configuraciones concretas, aplicarlas y supervisar su cumplimiento. En este sentido, me gustaría profundizar en el desarrollo de un sistema con estas características que utilice los datos obtenidos mediante SNMP por mi sistema actual, a la hora de realizar el análisis de las acciones necesarias a llevar a cabo para cumplir las peticiones del gestor.
115 115 Piloto de gestión para dispositivos IoT A pesar de haber dejado estas mejoras como líneas futuras, me siento enormemente satisfecho por el trabajo realizado, ya que me ha permitido reforzar mis conocimientos técnicos y prácticos en muchas de las áreas vistas durante el Grado. Es mi constante ambición y deseo de mejora lo que me impulsa a seguir mejorando este trabajo, con la misma ilusión y motivación con los que empecé el primer día.
Conclusiones y Líneas futuras
117 117 Piloto de gestión para dispositivos IoT REFERENCIAS [1] Clemm, A., & Cisco Systems, I. C. P. (2007). Network management fundamentals (1st edition). Cisco Press. [2] Gestión por intenciones. https://www.juniper.net/mx/es/research-topics/what-is-intent-basednetworking.html [3] Blog oficial de proyectos de Arduino. https://projecthub.arduino.cc/ [4] RFC 1067 – A Simple Network Management Protocol (SNMP) . https://datatracker.ietf.org/doc/html/rfc1067 [5] RFC 1901 – Introduction to Community-based SNMPv2. https://datatracker.ietf.org/doc/html/rfc1901 [6] RFC 1905 – Protocol Operations for Version 2 of the Simple Network Management. https://datatracker.ietf.org/doc/html/rfc1905 [7] RFC 1906 – Transport Mappings for Version 2 of the Simple Network Management Protocol (SNMPv2. https://datatracker.ietf.org/doc/html/rfc1906 [8] RFC 3411 – An Architecture for Describing SNMP Management Frameworks. https://datatracker.ietf.org/doc/html/rfc3411 [9] Lightweight Machine to Machine Technical Specification https://www.openmobilealliance.org/release/LightweightM2M/V1_2-20201110-A/OMA-TSLightweightM2M_Core-V1_2-20201110-A.pdf [10] CoAP Management Interface (CORECONF). https://datatracker.ietf.org/doc/draft-ietf-core-comi/ [11] Basic encoding rules (ASN.1). https://www.oss.com/asn1/resources/asn1-made-simple/asn1-quickreference/basic-encoding-rules.html [12] Estándar MQTT 5.0. https://docs.oasis-open.org/mqtt/mqtt/v5.0/mqtt-v5.0.pdf [13] Aplicación de BMW que utiliza MQTT (de HiveMQ) https://www.hivemq.com/case-studies/bmw-mobility-services/ [14] Sistema de Volkswagen basado en MQTT (utilizando EMQ). https://www.emqx.com/en/customers/saicvolkswagen [15] Casos de uso de MQTT. https://mqtt.org/use-cases/ [16] Arduino. https://www.arduino.cc/ [17] Eclipse Mosquitto. https://mosquitto.org/ [18] GNS3. https://www.gns3.com/ [19] Android Studio. https://developer.android.com/studio?hl=es-419 [20] PostgreSQL. https://www.postgresql.org/ [21] SpringBoot. https://spring.io/projects/spring-boot [22] SNMP_Exporter . https://github.com/prometheus/snmp_exporter [23] Prometheus. https://prometheus.io/ [24] Grafana. https://grafana.com/
Referencias [25] Librería gestor SNMP: Arduino_SNMP_Manager https://github.com/shortbloke/Arduino_SNMP_Manager [26] Librería cliente MQTT: Pub_sub_client: https://github.com/knolleary/pubsubclient [27] Librería de gestión del display TFT: TFT_eSPI: https://github.com/Bodmer/TFT_eSPI [28] Prototipo de un sistema de monitorizacióndomótico basado en la plataforma Arduino usando protocolos estándar de gestión de red: https://repositorio.unican.es/xmlui/bitstream/handle/10902/29588/447021.pdf?sequence=1&isAllowed=y [29] Datasheet TMP36. https://www.alldatasheet.com/datasheet-pdf/view/49108/AD/TMP36.html [30] Datasheet Vegetronix VH400. https://solem-irrigation.com/wp-content/uploads/2022/06/20200326__Soil_moisture_sensor_VEGETRONIX_VH400_-_V2_-_EN-1.pdf [31] Librería sensor capacitivo: CapacitiveSensor. https://github.com/PaulStoffregen/CapacitiveSensor [32] Datasheet KY-38. https://www.alldatasheet.es/datasheet-pdf/view/1138845/ETC2/KY-038.html [33] Librería memoria RAM Arduino. https://github.com/mpflaga/Arduino-MemoryFree [34] MIB-II. https://www.ietf.org/rfc/rfc1213.txt [35] RMON-MIB. https://datatracker.ietf.org/doc/html/rfc3577 [36] BGP4-MIB. https://datatracker.ietf.org/doc/html/rfc4273 [37] Agentuino. https://github.com/carlosdelfino/agentuino [38] MIB Browser. https://www.ireasoning.com/mibbrowser.shtml [39]Gayathri, R.; Usharani, S.; Mahdal, M.; Vezhavendhan, R.; Vincent, R.; Rajesh, M.; Elangovan, M. Detection and Mitigation of IoT-Based Attacks Using SNMP and Moving Target Defense Techniques. Sensors 2023, 23, 1708. [40]O. Boyar, M. E. Özen and B. Metin, "Detection of Denial-of-Service Attacks with SNMP/RMON," 2018 IEEE 22nd International Conference on Intelligent Engineering Systems (INES), Las Palmas de Gran Canaria, Spain, 2018. [41] Video de demostración: fase de pruebas. https://www.youtube.com/watch?v=mIFPKZTALaY [42] RFC2578. https://datatracker.ietf.org/doc/html/rfc2578
119 119 Piloto de gestión para dispositivos IoT
Anexo A: MIBARDUINO ANEXO A: MIBARDUINO MIBARDUINO DEFINITIONS ::= BEGIN IMPORTS MODULE-IDENTITY, enterprises FROM RFC1155-SMI OBJECT-TYPE FROM RFC-1212 IpAddress FROM SNMPv2-SMI; arduinoMIB MODULE-IDENTITY LAST-UPDATED "202404120000Z" ORGANIZATION "Universidad de Sevilla" CONTACT-INFO "
[email protected]" DESCRIPTION " Piloto de gestión para dispositivos IoT" ::= { enterprises 36582 } informacionDeGestion OBJECT IDENTIFIER ::= { arduinoMIB 1 } valoresLeidos OBJECT IDENTIFIER ::= { arduinoMIB 2 } eventos OBJECT IDENTIFIER ::= { arduinoMIB 3 } servidores OBJECT IDENTIFIER ::= { arduinoMIB 4 } nodosDeRed OBJECT IDENTIFIER ::= { arduinoMIB 5 } encaminamiento OBJECT IDENTIFIER ::= { arduinoMIB 6 } -- Definicion del string utilizado en toda la MIB DisplayString ::= OCTET STRING (SIZE (0..255)) -- Definicion de los escalares del grupo de informacion acerca de gestion del prototipo arduino, -- se presenta informacion tanto del dispositivo como de la persona y equipo de contacto.
121 121 Piloto de gestión para dispositivos IoT identificador OBJECT-TYPE SYNTAX DisplayString ACCESS read-write STATUS mandatory DESCRIPTION " Numero identificador del prototipo arduino gestionado " ::= { informacionDeGestion 1 } localizacion OBJECT-TYPE SYNTAX DisplayString ACCESS read-write STATUS mandatory DESCRIPTION " Localizacion del prototipo Arduino " ::= { informacionDeGestion 2 } direccionIP OBJECT-TYPE SYNTAX IpAddress ACCESS read-only STATUS mandatory DESCRIPTION " Direccion IP asignada al arduino " ::= { informacionDeGestion 3 } tiempoEncendido OBJECT-TYPE SYNTAX TimeTicks ACCESS read-only STATUS mandatory DESCRIPTION " Tiempo desde el encendido del prototipo " ::= { informacionDeGestion 4 } encargadoDeGestion OBJECT-TYPE SYNTAX DisplayString ACCESS read-write STATUS mandatory DESCRIPTION " Nombre de la persona encargada de gestionar la placa "
Anexo A: MIBARDUINO "Nombre del servidor" ::= { pcEntry 2 } tiempoActivo OBJECT-TYPE SYNTAX TimeTicks ACCESS read-only STATUS mandatory DESCRIPTION "Tiempo activo del servidor" ::= { pcEntry 3 } cargaCPU OBJECT-TYPE SYNTAX INTEGER ACCESS read-only STATUS mandatory DESCRIPTION "Carga de la CPU del servidor" ::= { pcEntry 4 } TCPConn OBJECT-TYPE SYNTAX INTEGER ACCESS read-only STATUS mandatory DESCRIPTION "Conexiones TCP activas del servidor" ::= { pcEntry 5 } nombreDisco OBJECT-TYPE SYNTAX DisplayString ACCESS read-only STATUS mandatory DESCRIPTION " Nombre del disco duro principal" ::= { pcEntry 6 }
129 129 Piloto de gestión para dispositivos IoT espacioLibre OBJECT-TYPE SYNTAX INTEGER ACCESS read-only STATUS mandatory DESCRIPTION "Espacio libre en el disco duro principal" ::= { pcEntry 7 } -- Grupo nodosDeRed swTable OBJECT-TYPE SYNTAX SEQUENCE OF FilasSW ACCESS not-accessible STATUS mandatory DESCRIPTION " Tabla que contiene los nodos de red gestionados por el ESP32" ::= { nodosDeRed 1 } swEntry OBJECT-TYPE SYNTAX FilasSW ACCESS not-accessible STATUS mandatory DESCRIPTION " Filas de la tabla" INDEX { indiceSW } ::= { swTable 1 } FilasSW::= SEQUENCE { indiceSW INTEGER, nombreSW DisplayString, tiempoUp TimeTicks, location
Anexo A: MIBARDUINO DisplayString, puertoGestionado DisplayString, estadoPuerto INTEGER, byteRecibidos INTEGER } indiceSW OBJECT-TYPE SYNTAX INTEGER ACCESS read-only STATUS mandatory DESCRIPTION " Índice de la fila " ::= { swEntry 1 } nombreSW OBJECT-TYPE SYNTAX DisplayString ACCESS read-only STATUS mandatory DESCRIPTION "Nombre del nodo " ::= { swEntry 2 } tiempoUp OBJECT-TYPE SYNTAX TimeTicks ACCESS read-only STATUS mandatory DESCRIPTION " Tiempo activo del nodo" ::= { swEntry 3 } location OBJECT-TYPE SYNTAX DisplayString ACCESS read-only STATUS mandatory
131 131 Piloto de gestión para dispositivos IoT DESCRIPTION "Localización física del nodo " ::= { swEntry 4 } puertoGestionado OBJECT-TYPE SYNTAX DisplayString ACCESS read-only STATUS mandatory DESCRIPTION " Puerto gestionado " ::= { swEntry 5 } estadoPuerto OBJECT-TYPE SYNTAX INTEGER ACCESS read-only STATUS mandatory DESCRIPTION " Estado del puerto (1=Activo, 0=Inactivo) " ::= { swEntry 6 } bytesRecibidos OBJECT-TYPE SYNTAX INTEGER ACCESS read-only STATUS mandatory DESCRIPTION " Bytes recibidos en el puerto gestionado " ::= { swEntry 7 } -- Grupo encaminamiento bgpTable OBJECT-TYPE SYNTAX SEQUENCE OF FilasBGP ACCESS not-accessible STATUS mandatory DESCRIPTION "Tabla que contiene la información de las conexiones BGP de los nodos de red gestionados"
Anexo A: MIBARDUINO ::= { encaminamiento 1 } bgpEntry OBJECT-TYPE SYNTAX FilasBGP ACCESS not-accessible STATUS mandatory DESCRIPTION "Fila de la tabla de conexiones BGP" INDEX { indiceBGP } ::= { bgpTable 1 } FilasBGP ::= SEQUENCE { indiceBGP INTEGER, nodoOrigen DisplayString, vecinoASN INTEGER, tiempoConexion Gauge32, mensajesUpdate INTEGER, mensajesTotales INTEGER } indiceBGP OBJECT-TYPE SYNTAX INTEGER ACCESS read-only STATUS mandatory DESCRIPTION "Índice único de la entrada BGP" ::= { bgpEntry 1 } nodoOrigen OBJECT-TYPE SYNTAX DisplayString ACCESS read-only STATUS mandatory DESCRIPTION "Nombre o dirección del nodo origen BGP" ::= { bgpEntry 2 }
133 133 Piloto de gestión para dispositivos IoT vecinoASN OBJECT-TYPE SYNTAX INTEGER ACCESS read-only STATUS mandatory DESCRIPTION "ASN (Autonomous System Number) del vecino BGP" ::= { bgpEntry 3 } tiempoConexion OBJECT-TYPE SYNTAX Gauge32 ACCESS read-only STATUS mandatory DESCRIPTION "Tiempo de conexión BGP en segundos" ::= { bgpEntry 4 } mensajesUpdate OBJECT-TYPE SYNTAX INTEGER ACCESS read-only STATUS mandatory DESCRIPTION "Número de mensajes BGP Update enviados" ::= { bgpEntry 5 } mensajesTotales OBJECT-TYPE SYNTAX INTEGER ACCESS read-only STATUS mandatory DESCRIPTION "Número total de mensajes BGP enviados" ::= { bgpEntry 6 } END
Anexo B: Instalación y configuración de prometheus, snmp-exporter y grafana ANEXO B: INSTALACIÓN Y CONFIGURACIÓN DE PROMETHEUS, SNMP-EXPORTER Y GRAFANA La instalación y configuración del stack prometheus, snmp-exporter y grafana, he decidido hacerla utilizando Docker, ya que facilita enormemente todo este proceso. Por ello, el primer paso será instalar Docker, en mi caso, en Ubuntu 22: sudo apt update sudo apt install -y docker.io En primer lugar, crearemos una red compartida que utilizarán los tres servicios, permitiendo la comunicación entre ellos. Para ello, ejecutaremos el siguiente comando: docker network create monitoring El proceso de instalación y configuración seguirá el siguiente orden: snmp-exporter, prometheus y grafana. Esto permite depurar errores desde el punto de comunicación directo con el Arduino, snmp-exporter, pasando por el recolector de métricas, prometheus, llegando hasta la interfaz gráfica que recoge las métricas obtenidas en prometheus. Para levantar un Docker de snmp_exporter que permita obtener objetos de la MIB de Arduino, crearemos el siguiente fichero (snmp.yml) en cualquier directorio: dit@linux-U-L1:~/prometheus-grafana-snmp_exporter$ cat snmp.yml auths: public_v2: community: public version: 1 modules: arduino: walk: - 1.3.6.1.4.1.36582.2.1.1.3.1 # temperatura - 1.3.6.1.4.1.36582.2.1.1.3.2 # capacitivo - 1.3.6.1.4.1.36582.2.1.1.3.3 # sensor 3 valor - 1.3.6.1.4.1.36582.2.1.1.3.4 # accesos web - 1.3.6.1.4.1.36582.2.1.1.3.5 # humedad suelo - 1.3.6.1.4.1.36582.2.1.1.3.6 # ram disponible - 1.3.6.1.4.1.36582.2.1.1.3.7 # sonido 1 - 1.3.6.1.4.1.36582.2.1.1.3.8 # sonido 2 - 1.3.6.1.4.1.36582.4.1.1.4.1 # carga cpu s1
135 135 Piloto de gestión para dispositivos IoT - 1.3.6.1.4.1.36582.4.1.1.4.2 # carga cpu s2 - 1.3.6.1.4.1.36582.4.1.1.5.1 # conexiones tcp s1 - 1.3.6.1.4.1.36582.4.1.1.5.2 # conexiones tcp s2 - 1.3.6.1.4.1.36582.5.1.1.7.1 # R1 bytes entrantes - 1.3.6.1.4.1.36582.5.1.1.7.2 # R2 bytes entrantes - 1.3.6.1.4.1.36582.5.1.1.7.3 # R3 bytes entrantes - 1.3.6.1.4.1.36582.5.1.1.7.4 # R4 bytes entrantes - 1.3.6.1.4.1.36582.5.1.1.7.5 # R5 bytes entrantes metrics: - name: temperatura oid: 1.3.6.1.4.1.36582.2.1.1.3.1 type: gauge - name: capacitivo oid: 1.3.6.1.4.1.36582.2.1.1.3.2 type: gauge - name: valor_sensor_3 oid: 1.3.6.1.4.1.36582.2.1.1.3.3 type: gauge - name: accesos_web oid: 1.3.6.1.4.1.36582.2.1.1.3.4 type: counter - name: humedad_suelo oid: 1.3.6.1.4.1.36582.2.1.1.3.5 type: gauge - name: ram_disponible oid: 1.3.6.1.4.1.36582.2.1.1.3.6 type: gauge - name: sonido_1 oid: 1.3.6.1.4.1.36582.2.1.1.3.7 type: gauge - name: sonido_2 oid: 1.3.6.1.4.1.36582.2.1.1.3.8 type: gauge - name: carga_cpu_servidor_1 oid: 1.3.6.1.4.1.36582.4.1.1.4.1 type: gauge - name: carga_cpu_servidor_2 oid: 1.3.6.1.4.1.36582.4.1.1.4.2
Anexo B: Instalación y configuración de prometheus, snmp-exporter y grafana type: gauge - name: conexiones_tcp_servidor_1 oid: 1.3.6.1.4.1.36582.4.1.1.5.1 type: gauge - name: conexiones_tcp_servidor_2 oid: 1.3.6.1.4.1.36582.4.1.1.5.2 type: gauge - name: bytes_entrantes_R1 oid: 1.3.6.1.4.1.36582.5.1.1.7.1 type: counter - name: bytes_entrantes_R2 oid: 1.3.6.1.4.1.36582.5.1.1.7.2 type: counter - name: bytes_entrantes_R3 oid: 1.3.6.1.4.1.36582.5.1.1.7.3 type: counter - name: bytes_entrantes_R4 oid: 1.3.6.1.4.1.36582.5.1.1.7.4 type: counter - name: bytes_entrantes_R5 oid: 1.3.6.1.4.1.36582.5.1.1.7.5 type: counter Gracias a este fichero, podremos levantar un contenedor de snmp-exporter con el siguiente comando: docker run -d --name snmp_exporter --network monitoring -p 9116:9116 -v /home/dit/prometheus-grafanasnmp_exporter/snmp.yml:/etc/snmp_exporter/snmp.yml prom/snmp-exporter Una vez levantado, comprobaremos que funciona correctamente accediendo a la siguiente URL en el navegador, modificando las direcciones IP por las correspondientes: http://192.168.48.213:9116/snmp?module=arduino&target=192.168.48.82
137 137 Piloto de gestión para dispositivos IoT Como se puede observar, se reciben correctamente los valores de los objetos de la MIB de Arduino que definimos anteriormente en snmp.yml. Por tanto, pasaremos a levantar un contenedor de prometheus que se encargará de recoger estas métricas de forma periódica, cada 30 segundos. Para ello, creamos el fichero prometheus.yml con el siguiente contenido: global: scrape_interval: 30s evaluation_interval: 30s scrape_timeout: 30s # Aumenta el timeout global por defecto (opcional) # rule_files: # - "first.rules" # - "second.rules" scrape_configs:
Anexo C: Script Broker MQTT f"/arduino1/{sysName}/diskName", f"/arduino1/{sysName}/tcpConnections", f"/arduino1/{sysName}/cpuLoad", f"/arduino1/{sysName}/diskSize" ] for topic in topics: client.subscribe(topic) print(f" Suscrito a: {topic}") # Configuración del cliente MQTT client = mqtt.Client() client.on_connect = on_connect client.on_message = on_message # Conexión con el broker client.connect("localhost", 1883) client.loop_forever()
145 145 Piloto de gestión para dispositivos IoT ANEXO D: REPOSITORIO DE GITHUB He creado un repositorio de Github accesible desde mi perfil o desde la URL https://github.com/viczamgar/Piloto_de_gestion_para_dispositivos_IoT con todos los códigos desarrollados para este trabajo, organizados en los siguientes directorios: • Spring Boot: códigos del backend de Spring Boot desarrollados para el servidor REST. • SubsistemaG1/gestorSNMP: códigos del ESP32. • SubsistemaG2/agenteSNMP: códigos del Arduino MEGA, incluyendo la librería desarrollada AgenteSNMP. • escenarioGNS3: ficheros necesarios para replicar el escenario de GNS3 con los routers gestionados. • prometheus-grafana-snmp_exporter: ficheros de configuración de prometheus y snmp_exporter para replicar la instalación de ambos componentes. • bbdd.sql: fichero con la definición de las tablas de la base de datos de PostgreSQL. • clienteMQTT.py: script de Python del cliente MQTT que recibe la información de gestión del ESP32 y la inserta en la BBDD.
ANEXO E: VIDEO DE DEMOSTRACIÓN: PRUEBAS He subido a la plataforma Youtube un video de demostración enseñando todos los componentes del sistema, tanto hardware como software, haciendo pruebas con la aplicación móvil y Grafana. Este puede ser consultado en el siguiente enlace: https://www.youtube.com/watch?v=mIFPKZTALaY o en mi canal Vicente Z de esta plataforma.