¿Qué aprenderá?
¿Qué hará?
¿Cuáles son los prerrequisitos?
En este tutorial verá cómo se implementa la capa de aplicación del sistema IoT REMA a través de microservicios y cómo se implementa el procesamiento de eventos. Las palabras "servicios" y "microservicios" se usarán de forma intercambiable a lo largo del documento. La tabla que sigue muestra los servicios, clasificados por tipo (ya sea de lógica o de interfaz), y los requisitos que implementa cada servicio. Este tutorial profundiza los servicios de lógica y habla muy brevemente de la interfaz ya que esto se abordará en otro recurso.
Tipo de servicio | Nombre del servicio | Requisitos que implementa |
Lógica | Registro de dispositivos | Dar de alta el dispositivo IoT de un usuario que se conecta al sistema por primera vez |
Recepción de datos | Recibir y almacenar los datos recolectados por dispositivos registrados | |
Control de actuadores | Enviar órdenes a los dispositivos IoT a partir del procesamiento de eventos, el envío se hace a través del Bróker MQTT | |
Interfaz gráfica y seguridad | Visualización de datos |
|
Autenticación | Validar las credenciales de los usuarios para permitir el acceso a la interfaz gráfica |
Tabla 1. Servicios y requisitos que implementan
La figura que sigue muestra el despliegue de los servicios anteriormente mencionados y del resto de la arquitectura en AWS. Note que cada servicio tendrá acceso a una única base de datos (patrón Single Database). La comunicación entre los dispositivos y la capa de aplicación es posible a través de un Bróker MQTT. Esta comunicación se da en dos sentidos (llamada "lazo cerrado"): 1) de los dispositivos al servicio de recepción de datos para enviarle muestras de datos; y 2) del servicio de control a los dispositivos para enviarle órdenes que los actuadores deben ejecutar. Adicionalmente, están los servicios de visualización y autenticación que reciben peticiones desde el navegador del usuario.
Figura 1. Diagrama de despliegue de la arquitectura de microservicios
Los servicios están implementados en Django, un framework web basado en Python. Tanto el framework como el lenguaje de programación poseen varias herramientas útiles que se usan en el desarrollo de los servicios. Django permite crear un proyecto que contiene las configuraciones clave del sistema y dentro del proyecto permite crear aplicaciones que son los submódulos del proyecto. En este tutorial cada aplicación en Django va a implementar uno o más servicios y gracias a las configuraciones globales del proyecto estas aplicaciones acceden a la misma base de datos. Le recomendamos ver este video que explica la estructura interna de una aplicación Django.
Para más información sobre el framework Django puede visitar el sitio web de Django.
A lo largo de este tutorial, a partir del código explorado del proyecto de Django, se despliegan los diferentes servicios en servidores de AWS y se integran con el resto de capas del sistema IoT. Para esto se necesita desplegar la base de datos primero y luego el servidor del bróker MQTT, con sus respectivas configuraciones. Después, se configuran y se despliegan los servicios, uno en cada servidor de AWS, esto incluye la creación de usuarios al sistema. Por último, con la estación de medición IoT se realizan pruebas de funcionamiento de envío de información, visualización de los datos y recepción de alertas. Se cierra el tutorial motivando algunas reflexiones.
El código del proyecto de Django está en este este repositorio de Github. Puede clonar o descargar el proyecto para que examine el programa a fondo.
Al descargarlo y abrirlo en el ambiente de desarrollo puede observar la estructura del proyecto (figura XX), en el que se encuentran las siguientes carpetas: 1) IOTMonitoringServer
(configuración global del proyecto); 2) control
(que encapsula el microservicio de control de actuadores), 3) receiver
(que encapsula los servicios de registro de dispositivos y recepción de datos); y 4) viewer
(que encapsula los servicios de visualización y autenticación).
IOTMonitoringServer/
▶ IOTMonitoringServer
▶ control
▶ receiver
▶ viewer
┗ manage.py
A continuación se explica el código de la configuración global del proyecto así como de las funcionalidades más importantes implementadas en el resto de carpetas (estas son Aplicaciones Django).
Dentro de la carpeta IOTMonitoringServer
se encuentra el archivo settings.py
como se muestra a continuación. En este archivo están las credenciales y datos de acceso a la base de datos. Adicionalmente, existen otras constantes donde se pueden definir las aplicaciones Django que usará el proyecto, los hosts permitidos, el modo debug, el idioma del proyecto, entre otros. En la Tabla 2 puede leer una breve descripción de las constantes del archivo.
IOTMonitoringServer/ ▶ ┃ ▶ ┃ ▶ ┃ ▶ ┃ ▶ ┃ ▶ ┃ ┗ |
|
|
Constante | Descripción |
| Ruta de la carpeta base del proyecto. |
| Booleano que indica si el proyecto está corriendo en desarrollo o producción. |
| Lista de hosts habilitados para el consumo de los endpoints del proyecto. |
| Aplicaciones internas y externas que se usan dentro del proyecto. |
| Configuraciones de las plantillas, entre ellas, rutas donde se ubican y los procesadores de contexto de las plantillas. |
| Configuración de los datos de conexión a la base de datos. EJ: { "default": { "ENGINE": "django.db.backends.postgresql_psycopg2", "NAME": "iot_data", "USER": "dbadmin", "PASSWORD": "uniandesIOT1234*", "HOST": "localhost", "PORT": "", } } |
| Especifica el idioma y la región del proyecto. Para español en Colombia el valor sería: "es-co". |
| Ruta de la carpeta base de las configuraciones globales del proyecto. |
| URL donde se van a servir los archivos estáticos del proyecto como css, js e imágenes. |
| URL donde se encuentra el login del proyecto. |
| Especifica la URL a donde se enviará al usuario cuando se autentique exitosamente. |
| Especifica la URL a donde se enviará al usuario cuando salga de la sesión. |
| Dirección del bróker MQTT. |
| Puerto del bróker MQTT. |
| Nombre del usuario administrador suscriptor del bróker MQTT. |
| Contraseña del usuario administrador suscriptor del bróker. |
| Nombre del usuario administrador publicador del bróker MQTT. |
| Contraseña del usuario administrador publicador del bróker. |
| Tópico a suscribir que estará atento a todos los mensajes que se manden al bróker. |
| Ruta del archivo ca.crt para la comunicación por TLS con el bróker MQTT. |
Tabla 2. Listado de las constantes en settings.py, junto con su descripción
En esta aplicación se implementan los servicios de registro de dispositivos y recepción de datos. Para esto en su archivo models.py
se declaran los modelos que se usarán en dichos servicios. Dentro de este archivo están las entidades que se definieron en el modelo de datos del tutorial "Capa de Datos". Las entidades implementadas son City
, State
, Country
, Location
, Measurement
, Station
y Data
. En particular, User
está definida en el módulo nativo de autenticación (Auth
) de Django. Las otras aplicaciones, Viewer
y Control
, usan los modelos que se definen en esta aplicación Django.
IOTMonitoringServer/ ▶ ▶ ▶ ┃ ▶ ┃ ▶ ┃ ▶ ┃ ▶ ┃ ▶ ┃ ▶ ┃ ┗ ▶ ┗ |
|
|
Por otro lado, el servicio que se muestra a continuación es el que está atento de la llegada de cualquier medición recolectada por los dispositivos IoT. Para esto necesita conectarse al bróker MQTT y suscribirse a todos los tópicos de datos medidos. Dentro de la carpeta receiver
se ubica el archivo mqtt.py
. En este archivo se realizan las operaciones necesarias para la conexión al bróker. El programa intenta primero conectarse al bróker con las constantes establecidas en la configuración global.
IOTMonitoringServer/ ▶ ▶ ▶ ┃ ▶ ┃ ▶ ┃ ▶ ┃ ▶ ┃ ▶ ┃ ▶ ┃ ┗ ▶ ┗ |
|
|
Luego, si la conexión fue exitosa, se suscribe a todos los tópicos que contienen muestras de datos
. Para esto usa el patrón +/+/+/+/out
que corresponde a
donde:
,
y
hacen referencia a la localización geográfica donde está el dispositivo que envía datos
se refiere a la persona que conecta el dispositivo a internet out
indica que el tópico contiene los mensajes salientes del dispositivoEl patrón es especificado en la constante TOPIC
se declara en la función on_connect()
del archivo settings.py
como se muestra a continuación:
|
|
A continuación se observa el código de la función on_message()
del archivo mqtt.py,
cuya responsabilidad es procesar los mensajes recibidos. Esta función primero descodifica el mensaje MQTT que contiene tanto el payload (datos de temperatura y humedad) como el URI del tópico,
. A partir del URI se extrae el usuario y la localización del dispositivo. Se valida que exista el usuario y el dispositivo, si este último no existe se registra. Luego, el programa crea la medición según el tipo de variable que esté en el payload. Esta creación es la misma que está en el tutorial "Capa de Datos"; puede dirigirse a ese tutorial para una explicación más detallada.
|
|
Por su parte, la función on_disconnect
busca reconectar la aplicación al bróker MQTT si se perdió la conexión, como se muestra a continuación.
|
|
Por último, en el archivo utils.py
(dentro de la carpeta receiver
) están las funciones de las que hace uso la función on_message
del archivo mqtt.py
. Una breve descripción de las funciones que están en utils.py
puede ser encontrada en la Tabla 3.
Nombre de la función | Descripción |
| Utiliza un API público (https://geocode.xyz/) para obtener la latitud y longitud de una ciudad ubicada en un estado y país. |
| Extrae el país, estado, ciudad y usuario del tópico que le llega por parámetro. Retorna esos valores por separado. |
| Trae el usuario indicado de la base de datos. Retorna el objeto del usuario. |
| Intenta traer la locación geográfica a partir de los nombres de una ciudad, estado y país. Si no existe, calcula las coordenadas de esa ubicación, la crea y la retorna. |
| Intenta traer la estación con el usuario y la locación especificados. Si no existe la crea y la retorna. |
| Intenta traer la variable con el nombre y la unidad especificados. Si no existe la crea y la retorna. |
| Crea un nuevo dato con el valor, la estación y la variable especificados. Hace las operaciones necesarias para insertarlo en la base de datos con el patrón Blob. Calcula el promedio, el mínimo y el máximo de los datos anteriores. |
Tabla 3. Listado de las funciones en utils.py, junto con su descripción
Este servicio es el encargado de procesar eventos y de acuerdo con unas reglas definidas comandar acciones en los actuadores de los dispositivos IoT. En el caso de este tutorial, la regla se especifica como sigue:
Condición | Acción |
Si el promedio de las mediciones de una variable (por ej., temperatura, humedad), colectadas en la última hora, está por fuera de los límites definidos para esa variable | Mostrar un mensaje en la pantalla del dispositivo informando que los límites se han excedido |
Tabla 4. Reglas para las acciones comandadas sobre los actuadores
Para enviar mensajes a los dispositivos, este servicio publica mensajes en un tópico al que un dispositivo está suscrito. El URI que escogimos para el tópico es muy parecido a la del tópico que contiene las mediciones,
, la única diferencia es que tiene el sufijo /in
para indicar que a ese tópico llegan las órdenes para comandar los actuadores de los dispositivos.
El comportamiento específico de este servicio es el siguiente: un "cron" (servicio que se ejecuta de forma autónoma cada cierto tiempo, por ej., cada 10 minutos) calcula el promedio de las variables medidas en la última hora. Si el promedio se sale de los límites establecidos para esa variable específica, el servicio envía un mensaje al dispositivo para alertar a los usuarios sobre la situación. El mensaje enviado contiene la variable y los límites de esta.
En la implementación del servicio, bajo la carpeta control
del proyecto de Django, se puede encontrar el archivo monitor.py
cuyas funciones más importantes son:
setup_mqtt
: se encarga de conectar y reconectar el servicio al Bróker MQTT a través de las funciones on_connect
y on_disconnect
. También configura la comunicación segura por TLS si así está especificado en la configuración global. Es imperativo que esta función sea la primera que se ejecute ya que ella configura la conexión entre el servicio y el Bróker MQTT.IOTMonitoringServer/ ▶ ┃ ▶ ┃ ▶ ┃ ▶ ┃ ▶ ┃ ┗ ▶ ▶ ▶ ┗ |
|
|
start_cron
: se encarga de ejecutar el "cron" por medio de la librería schedule
.
|
|
analyze_data
: realiza los cálculos necesarios para identificar si hay o no que enviar mensajes de alerta a los dispositivos, en caso que sí las envía. Primero, consulta y promedia los datos medidos en la última hora. Si el valor promedio calculado (check_value
) se encuentra por fuera de los límites (min_value
y max_value
), se envía un mensaje al dispositivo, vía un tópico con el URI ////in
. El mensaje tiene la forma: ALERT
.
|
|
La aplicación de visualización sigue la arquitectura de un proyecto web común en Django. Es decir, en el archivo urls.py
se encuentran las direcciones (paths) que aceptará el servidor web. En el proyecto están las siguientes:
/
: dirección de inicio./login
: dirección de autenticación. Incluída en el sistema de autenticación de Django django.contrib.auth.urls
./realtime-data
/: dirección donde el usuario observará los datos en tiempo real de su estación./map
/: dirección que incluye el mapa con las gráficas de las mediciones de todos los usuarios./historic/
: dirección donde se podrán descargar las mediciones del sistema./users/
: dirección donde se listan los usuarios del sistema. Sólo se accede por el administrador./users/delete//
: dirección para eliminar un usuario. Sólo se accede por el administrador./users/register/
: dirección que envía el formulario de registro de un usuario nuevo al sistema. Sólo se accede por el administrador. IOTMonitoringServer/ ▶ ▶ ▶ ▶ ┃ ▶ ┃ ▶ ┃ ▶ ┃ ▶ ┃ ▶ ┃ ▶ ┃ ▶ ┃ ▶ ┃ ▶ ┃ ▶ ┃ ▶ ┃ ┗ ┗ |
|
|
Cada una de estas URL ejecuta una vista del archivo views.py
. En este archivo cada función retorna una plantilla de la carpeta viewer/templates/
con su respectiva información cargada (por ej., la plantilla home.html
, que se muestra a continuación). La información para cada plantilla se carga con funciones del archivo utils.py
, por ejemplo, la función get_last_week_data
trae los últimos datos de temperatura y humedad y los preprocesa para que alimenten fácilmente la plantilla de HTML. Las vistas están anotadas con decoradores de Django que aseguran la autenticación del usuario y si es administrador o no (@login_required
y @user_passes_test(lambda u: u.is_superuser)
).
IOTMonitoringServer/ ▶ ▶ ▶ ▶ ┃ ▶ ┃ ▶ ┃ ┃ ▶ ┃ ┃ ▶ ┃ ┃ ▶ ┃ ┃ ▶ ┃ ┃ ▶ ┃ ┃ ▶ ┃ ┃ ▶ ┃ ┃ ▶ ┃ ┃ ▶ ┃ ┃ ▶ ┃ ┃ ▶ ┃ ┃ ▶ ... |
|
|
En esta sección se despliegan los servidores necesarios en AWS por medio del script de CloudFormation. En el script se especifica la creación de la VPC (Red Privada Virtual), los permisos de seguridad y la creación de las máquinas EC2. Adicionalmente, para las máquinas se especifican los comandos que deben ejecutarse para equiparlas con los ambientes de ejecución que necesitan. Por ejemplo, para la creación de la base de datos se ejecutan comandos que descargan Postgres y la extensión de Timescale y los configuran con valores predeterminados.
En el script de este tutorial se crearán máquinas para la base de datos, el bróker MQTT y los servicios (receptor, visualizador, control de actuadores). Para desplegar la infraestructura siga estos pasos:
Figura 2. Interfaz de inicio del laboratorio
~$ wget -O template https://raw.githubusercontent.com/SELF-Software-Evolution-Lab/Realtime-Monitoring-webApp/main/tutoriales/Capa%20de%20Aplicaci%C3%B3n/IOT-System.template.json
aws cloudformation create-stack --stack-name iot-system --template-body file://template --capabilities "CAPABILITY_IAM"
La terminal debe mostrar el ID del stack creado.
Figura 3. Inicio de la consola de AWS
En "CloudFormation" podrá ver el estado del stack, a saber: creación en proceso, fallo o creación completada.
Figura 4. Stacks creados en CloudFormation
Espere a que el estado del stack iot-system
sea "CREATE_COMPLETE".
Figura 4. Listado de máquinas aprovisionadas para el tutorial
Luego de haber desplegado la infraestructura se necesita configurarla. La base de datos no necesita configuraciones adicionales, pero el bróker MQTT sí. Como se trabajó en el tutorial de capa de comunicación ("Instalación y configuración de Mosquitto") la configuración que se realizará será agregar los usuarios que usted permitirá en su sistema. Las demás configuraciones como especificación del puerto, reglas de tópicos permitidos y modificación del archivo mosquitto.conf
ya están hechas. Si quiere cambiar los valores configurados puede seguir las instrucciones dadas en el tutorial de capa de sesión. Para agregar los usuarios deseados siga estas instrucciones.
Figura 5. Listado de máquinas aprovisionadas para el tutorial
Figura 6. Detalle de una máquina EC2
Figura 9. Ventana de conexión a una máquina EC2
/etc/mosquitto/users.txt
. Puede usar el siguiente comando para abrirlo:~$ sudo nano /etc/mosquitto/users.txt
En el editor agregue los usuarios con su contraseña correspondiente en líneas separadas. Ejemplo:
admin:admin
admin2:admin2
user1:123456
pe.perez:abc123
ironman:jarvis123
jfkennedy:apolo11
Es importante que tenga dos usuarios administradores, uno que sea capaz de leer y otro de escribir en cualquier tópico del bróker MQTT. Por defecto encuentra admin1
y admin2
en el archivo. Si cambia estos usuarios asegúrese de cambiar las reglas de acceso en el archivo /etc/mosquitto/acl.txt.
~$ sudo mosquitto_passwd -U /etc/mosquitto/users.txt
~$ sudo systemctl restart mosquitto.service
~$ sudo systemctl status mosquitto.service
La terminal debe mostrar que el servicio está activo y corriendo (active). Para salir del resumen del servicio oprima "Ctrl+C".
Según la arquitectura establecida, hay tres servicios que se deben ejecutar, uno en cada máquina. Los servicios son el receptor de datos, el visualizador y el control de actuadores. Todos estos servicios se encuentran en el mismo proyecto de Django, pero se inician de maneras diferentes. Para configurar los servicios basta con editar las configuraciones del proyecto una vez y luego descargarlo en cada máquina. A continuación se explica el procedimiento.
https://github.com/SELF-Software-Evolution-Lab/IOTMonitoringServer
IOTMonitoringServer/settings.py
, con el programa de su elección.ALLOWED_HOSTS
, DATABASES
y MQTT_HOST
.ALLOWED_HOSTS
debe cambiar "ip.maquina.visualizador" por la IP pública de la EC2 del servicio de visualización. Ej.: "ip.maquina.visualizador" pasa a ser "192.168.0.5".DATABASES
debe cambiar el valor del atributo HOST
que es "ip.maquina.db" por la IP pública de la EC2 que contiene la base de datos. Ej.: "ip.maquina.db" pasa a ser "157.253.0.40".MQTT_HOST
debe cambiar "ip.maquina.mqtt" por la IP pública de la EC2 que ejecuta el bróker MQTT. Ej.: "ip.maquina.mqtt" pasa a ser "30.44.127.23".~$ git add –all
~$ git commit -m "mensaje commit"
~$ git push
~$ git clone <enlace_repositorio>
Ingrese sus credenciales de github a continuación: usuario, contraseña (token de acceso).
~$ cd IOTMonitoringServer
~$ python3 manage.py migrate
~$ nohup python3 IOTMonitoringServer/manage.py start_mqtt &
Deberá observar en la consola los mensajes de conexión con el servidor MQTT y suscripción. Con esto ya tiene el receptor iniciado.
~$ python3 IOTMonitoringServer/manage.py start_control &
Deberá observar en la consola los mensajes de inicio del servicio. Con esto ya tiene el servicio de control de actuadores iniciado.
~$ nohup sudo python3 IOTMonitoringServer/manage.py runserver 0.0.0.0:80 &
Deberá observar en la consola los mensajes de inicio del servidor Django. Con esto ya tiene el servicio de visualización iniciado.
Los servicios ya están arriba para empezar a tratar peticiones. Sin embargo, para usar el servicio "IoT Viewer app" se deben crear un administrador y usuarios. El registro lo puede hacer siguiendo los pasos a continuación:
~$ python3 IOTMonitoringServer/manage.py createsuperuser
El programa le pedirá una serie de datos como usuario, correo y contraseña. Ingrese los datos que desee.
Figura 10. Interfaz de inicio de sesión de la aplicación "Viewer"
Nota: la contraseña debe cumplir con los requisitos que se muestran en el formulario de creación.
Figura 11. Interfaz de creación de usuario de la aplicación "Viewer"
Figura 12. Vista de usuarios creados
Ahora, probaremos el funcionamiento de los servicios desplegados conectando un dispositivo a las capas superiores.
Nota: el código para el NodeMCU está programado para utilizar una pantalla OLED 128×64. Si usted cuenta con otro tipo de pantalla, debe asegurarse de:
displayHeader()
, displayMeasures()
y displayMessage(message)
si hay necesidadPara el tutorial de capa de aplicación Interfaz se requiere del despliegue que realizó durante esta práctica. No elimine la infraestructura que desplegó, de lo contrario deberá realizar todo el proceso de nuevo.
Las siguientes preguntas lo invitan a reflexionar sobre lo que observó en el desarrollo de los pasos inmediatamente anteriores:
Para responder las preguntas considere lo visto a lo largo del tutorial y los videos conceptuales de la semana. ¡Traiga sus reflexiones a las sesiones sincrónicas!
Este tutorial le permitió experimentar la comunicación en lazo cerrado entre dispositivos de la capa física y servicios genéricos de la capa de aplicación de un sistema IoT.
Juan Avelino, Kelly Garcés | Autores |
Rocío Héndez, Andrés Bayona | Revisores |
En este tutorial se le guiará en la configuración de la tarjeta NodeMCU con la pantalla OLED 128x64 I2C. Se muestran las conexiones necesarias entre los dos módulos y se aprovechan las librerías de ejemplo para probar el correcto funcionamiento del NodeMCU con la pantalla.
Imagen 1. Diagrama de conexión entre NodeMCU y pantalla OLED.
Pantalla OLED | NodeMCU | Color cable |
GND | GND | Negro |
VCC | 3V3 | Rojo |
SCL | D1 | Verde |
SDA | D2 | Azul |
Tabla 1. Tabla de conexiones NodeMCU - OLED.
Nota: Algunos modelos de la pantalla OLED tienen los pines GND y VCC intercambiados en comparación al diagrama. Es importante que revise en el módulo físico el nombre de los pines que está conectando.
Imagen 2. Ruta de opciones para "Manage Libraries" en Arduino.
Imagen 3. Librería "Adafruit SSD1306" en el gestor de librerías Arduino.
Imagen 4. Ruta de opciones para ejemplo de programa de pantalla OLED.
Imagen 5. Script de ejemplo y ubicación de línea a modificar.
Para compilar y subir el código oprima el botón "Upload" como en la imagen 6.
Imagen 6. Botón "Upload".
Al darle clic, podrá observar el progreso de carga en la parte inferior de Arduino como se muestra en la imagen 7.
Imagen 7. Progreso de carga de script en la consola de Arduino.
Imagen 8,9,10,11. Ejemplos de animaciones que muestra la pantalla OLED.