Saltar a contenido

Proyecto OTITIA — Planificación en 8 sesiones de 3 h

1. Decisiones de partida

Aspecto Decisión
Duración 8 sesiones × 3 h = 24 h de trabajo en aula
Stack OTITIA (Docker Compose): Mosquitto, Node-RED, Telegraf, InfluxDB 2.7, InfluxDB 3 Core, MySQL, N8N, Grafana, WordPress
Sensórica Simulada (flujos Node-RED + API Open-Meteo + entradas Telegraf). Sin hardware físico
Broker MQTT Mosquitto local (otitia-mosquitto), no HiveMQ público
TSDB principal InfluxDB 2.7 (Flux). InfluxDB 3 Core como comparativa lateral
Organización / bucket s8a / h2v
LLM Sesión propia (Sesión 7)
Documentos existentes timeseries.md, nodered.md, influxdb.md
Documentos nuevos grafana.md, llm-iot.md

Convenciones del proyecto

Fijar esto el primer día evita el caos de la sesión 4 en adelante.

Jerarquía de topics MQTT

otitia/<zona>/<dispositivo>/<magnitud>

otitia/aula/sensor01/temperatura
otitia/aula/sensor01/humedad
otitia/exterior/meteo/temperatura
otitia/planta/electrolizador/presion
otitia/alertas/<zona>/<nivel>

Modelo InfluxDB

  • Medida: sensores
  • Tags: zona, dispositivo, magnitud (valores estables → indexables)
  • Fields: valor, bateria, rssi (valores cambiantes)
  • Timestamp: UTC, precisión de segundos (s) salvo que se justifique otra

Nomenclatura del alumnado: cada estudiante trabaja bajo otitia/<iniciales>/... para no pisarse en el broker compartido del VPS.


2. Mapa de contenidos: de los apuntes a las sesiones

Sesión Documento fuente Estado del material
1 timeseries.md (§ Introducción → Almacenamiento) + guía de arranque OTITIA Reutilizar, recortar pandas
2 nodered.md (§ Hola Node-RED → Añadiendo lógica de control) Reutilizar casi íntegro
3 nodered.md (§ MQTT con Node-RED) Refactorizar: HiveMQ → Mosquitto
4 influxdb.md (§ Elementos → Ingesta de datos, § Node-RED e InfluxDB) Reutilizar + añadir Telegraf
5 influxdb.md (§ Flux → InfluxDB y Python) Reutilizar
6 — Escribir grafana.md
7 — Escribir llm-iot.md
8 timeseries.md (§ Uso de Pandas, § Proyecto) como reto opcional Reubicar como ampliación

3. Desarrollo de las sesiones

Sesión 1 — Series temporales y arranque del stack

Objetivos * Distinguir una serie temporal de otros tipos de datos y justificar el uso de una TSDB. * Poner en marcha el stack OTITIA y verificar que todos los servicios responden.

Contenidos * Serie temporal: definición, orden cronológico, naturaleza append-only. * Tipos: métrica (regular) vs evento (irregular). * Características de los datos IoT: dependencia temporal, alta frecuencia, tamaño creciente, ruido. * Descomposición: tendencia, estacionalidad, ciclo, ruido. * Opciones de almacenamiento: edge, gateway, TSDB, NoSQL, relacional. Por qué InfluxDB. * Arquitectura OTITIA: redes otitia-ot / otitia-it, papel de cada servicio.

Desarrollo (3 h) | Tiempo | Actividad | | --- | --- | | 45' | Teoría de series temporales con ejemplos gráficos | | 30' | Presentación del proyecto, arquitectura y convenciones | | 60' | Taller: docker compose up, verificación de los 9 servicios, tour por Node-RED (1880), Influx UI (8086), Grafana (3000) | | 45' | Actividad 1 + resolución de incidencias de instalación |

Entregable: capturas de los tres interfaces funcionando + ficha con puertos, credenciales y comandos básicos de Docker.

Actividad 1 (RABDA.1 / 1p) — Documenta el stack: para cada servicio, indica imagen, puerto, red a la que pertenece y su papel en el pipeline. Dibuja el diagrama de flujo de datos de extremo a extremo.

Aviso de aula: reservar margen real para instalaciones fallidas. Windows con WSL2 exige que el proyecto viva en ~/proyectos/, nunca en /mnt/c/.


Sesión 2 — Node-RED y simulación de sensores

Objetivos * Construir flujos con nodos de entrada, proceso y salida. * Programar la lógica de simulación de una red de sensores.

Contenidos * Entorno: paleta, área de trabajo, panel lateral, despliegue. * Nodos inject, debug, function. Estructura del mensaje (msg.payload). * JavaScript en nodos función: múltiples salidas, node.send(), node.log(). * Contexto de nodo, de flujo y global. * Consumo de API REST con http request (Open-Meteo). * Lógica de control: switch y change con JSONata. * Exportación / importación de flujos en JSON.

Desarrollo (3 h) | Tiempo | Actividad | | --- | --- | | 30' | Hola Node-RED: inject → debug, primer despliegue | | 45' | Nodos función, logger y contexto | | 45' | Taller: simulador de sensores (temperatura interior/exterior, humedad, alarma) cada 30 s en un único JSON | | 40' | Taller: datos reales de Open-Meteo + limpieza del JSON en nodo función | | 20' | Alertas con switch + change |

Entregable: flujo 01-simulador.json exportado, con nodos comentados.

Actividad 2 (RAMIA.4 / CEMIA.4d / 2p) — Simulador de planta: genera cada 30 s las lecturas de al menos tres dispositivos distintos, cada uno con su zona y magnitudes. Sustituye la temperatura exterior por el valor real de Open-Meteo para Elche. Añade lógica que marque cada lectura con un nivel de severidad (ok, aviso, critico).


Sesión 3 — MQTT con Mosquitto

Objetivos * Explicar el modelo publish/subscribe y sus garantías. * Desacoplar el simulador del consumidor mediante el broker.

Contenidos * MQTT: origen, posición en la pila TCP/IP, comparación con HTTP. * Broker, publisher, subscriber. Mosquitto en el stack OTITIA. * Diseño de topics jerárquicos. Comodines + y #. * QoS 0/1/2 y mensajes retenidos: cuándo usar cada uno. * Nodos mqtt in / mqtt out. Configuración del servidor otitia-mosquitto:1883. * Conversión de tipos al recibir (fechas como cadena → Date). * Herramientas de línea: mosquitto_pub / mosquitto_sub desde el contenedor.

Desarrollo (3 h) | Tiempo | Actividad | | --- | --- | | 40' | Teoría MQTT y diseño de la jerarquía de topics del proyecto | | 30' | Taller: mosquitto_sub -t 'otitia/#' -v para "ver" el bus en directo | | 60' | Taller: publicar el simulador de la sesión 2 en MQTT (flujo publisher) | | 40' | Taller: flujo subscriber independiente que consume otitia/# | | 10' | Prueba de QoS y retained: parar y arrancar el subscriber |

Entregable: flujos 02-publisher.json y 03-subscriber.json, más la tabla de topics del proyecto.

Actividad 3 (RAMIA.4 / CEMIA.4d / 2p) — Publica cada lectura en su topic según la jerarquía acordada. Crea un segundo flujo que se suscriba con comodines y que reenvíe a otitia/alertas/<zona>/<nivel> únicamente las lecturas cuya severidad no sea ok. Documenta qué QoS has elegido para cada caso y por qué.

Refactorización pendiente: en nodered.md, sustituir broker.hivemq.com:1883 por otitia-mosquitto:1883 (o localhost:1883 desde el host) y los topics /s8a/h2v/nodered por la jerarquía otitia/.... Conservar la mención a brokers públicos como nota informativa.


Sesión 4 — InfluxDB: modelo de datos e ingesta

Objetivos * Modelar correctamente medidas, tags y fields evitando problemas de cardinalidad. * Ingerir datos por tres vías distintas y compararlas.

Contenidos * Elementos: organización, bucket, medida, tag, field, punto, serie, timestamp. * Retención y tokens. Equivalencias con el modelo relacional. * Protocolo de línea. Cardinalidad de series: por qué la temperatura nunca es un tag. * Influx UI y Influx CLI (influx org, influx bucket, influx write). * El arco de la ingesta en tres fases, cada una motivada por la limitación de la anterior:

1. **Node-RED → InfluxDB.** Nodo `influxdb out` directo. Funciona enseguida y el dato llega. *Límite*: un único consumidor, Node-RED conoce credenciales y esquema de la base de datos, y si InfluxDB no responde el dato se pierde.
2. **Node-RED → MQTT → Node-RED → InfluxDB.** Aparece el bus. El productor ya no sabe quién consume, y podemos añadir suscriptores (alertas, N8N, MySQL) sin tocarlo. *Límite*: la lógica de ingesta sigue siendo código propio, con sus reintentos, lotes y conversiones de tipo.
3. **Node-RED → MQTT → Telegraf → InfluxDB.** La ingesta pasa a ser declarativa: `metric_buffer_limit`, escritura por lotes y reintentos sin escribir una línea de código.
  • Comparativa final: acoplamiento, fan-out, resistencia a caídas y coste de mantenimiento.

Desarrollo (3 h) | Tiempo | Actividad | | --- | --- | | 45' | Modelo de datos y protocolo de línea. Ejercicio en papel de diseño de esquema | | 25' | Influx UI y CLI: crear organización, bucket y token | | 30' | Fase 1: Node-RED escribe directo con influxdb out | | 35' | Fase 2: se interpone el bus. Flujo publicador + flujo suscriptor que inserta | | 40' | Fase 3: Telegraf con mqtt_consumer sustituye al suscriptor | | 5' | Cierre: tabla comparativa de las tres fases |

Insistir en clase: en la fase 3, Telegraf no sustituye a Node-RED, sustituye al consumidor de la fase 2. Node-RED sigue siendo el sensor de principio a fin. Sin esta aclaración, media clase entiende que Telegraf viene a reemplazarlo.

Enlace con la sesión 8: la fase 1 es el único punto donde Node-RED toca la red IT. Cuando al final del proyecto retiremos esa red, la fase 1 dejará de funcionar y el resto del sistema seguirá vivo. Ese es exactamente el resultado esperado.

Entregable: los tres flujos del arco (04a-directo.json, 04b-mqtt.json, 04c-telegraf.json), el telegraf.conf comentado y los datos visibles en el Data Explorer.

Actividad 4 (RABDA.1 / CEBDA.1d / 2p) — Diseña y justifica el esquema de la medida sensores: qué va en tags, qué en fields y qué pasaría si lo invirtieras. Implementa las tres fases del arco de ingesta y documenta, para cada una, qué problema resuelve y qué limitación arrastra. Prueba a parar InfluxDB (docker compose stop influxdb) durante un minuto con las fases 1 y 3 activas, vuelve a arrancarlo y compara cuántos puntos se han perdido en cada caso.

Comparativa opcional: escribir el mismo line protocol contra InfluxDB 3 Core (puerto 8181, /api/v3/write_lp) y observar que el dato entra igual pero el concepto pasa de bucket a database.


Sesión 5 — Consultas Flux, tareas y Python

Objetivos * Consultar y agregar series temporales con Flux. * Automatizar el downsampling con tareas y acceder a los datos desde Python.

Contenidos * Flux: sintaxis, tuberías (|>), variables, funciones. * Flujos de tablas: from, range, filter, group, keep, pivot. * Transformación: map, rename, sort, limit. * Agregación: mean, max, min, count, aggregateWindow. * Remuestreo y downsampling: de datos cada 30 s a medias cada 5 min. * Tareas programadas: crear un bucket h2v_agregado y alimentarlo automáticamente. * Cliente Python influxdb-client: escritura y consulta.

Desarrollo (3 h) | Tiempo | Actividad | | --- | --- | | 50' | Flux paso a paso sobre los datos ya cargados | | 40' | Agregaciones y aggregateWindow | | 40' | Taller: tarea de downsampling a bucket agregado | | 40' | Taller: script Python de escritura y lectura | | 10' | Políticas de retención: bucket crudo 7 d, agregado 1 año |

Entregable: colección de consultas Flux comentadas, tarea activa y consulta.py.

Actividad 5 (RABDA.1 / CEBDA.1d / 2p) — Escribe las consultas Flux que resuelvan: (a) últimas lecturas de cada dispositivo, (b) temperatura media por hora y zona del último día, (c) máximo y mínimo por dispositivo, (d) número de alertas por zona. Crea una tarea que consolide medias de 15 min en el bucket agregado, y desde Python recupera y muestra el resultado.


Sesión 6 — Visualización con Grafana

Objetivos * Conectar Grafana a InfluxDB y construir un cuadro de mandos operativo. * Configurar alertas visuales sobre umbrales.

Contenidos * Grafana en el stack: puerto 3000, credenciales, organización. * Datasources: InfluxDB v2 con Flux (token, org, bucket) e InfluxDB 3 Core con SQL. * Tipos de panel: time series, stat, gauge, table, bar chart, state timeline. * Variables de dashboard (zona, dispositivo) y consultas parametrizadas con ${var}. * Rango temporal, auto-refresco y modo TV. * Umbrales, unidades y transformaciones. * Alertas de Grafana: regla, condición, punto de contacto. * Comparativa: Influx UI vs FlowFuse Dashboard vs Grafana. * Exportación del dashboard a JSON y aprovisionamiento.

Desarrollo (3 h) | Tiempo | Actividad | | --- | --- | | 30' | Grafana: conceptos y creación del datasource Flux | | 45' | Taller: primeros paneles (evolución de temperatura, stat con valor actual) | | 45' | Taller: variables de dashboard y paneles parametrizados por zona | | 40' | Taller: umbrales y regla de alerta | | 20' | Exportar el dashboard a JSON y versionarlo |

Entregable: dashboard-otitia.json exportado y funcionando tras reimportar.

Actividad 6 (RABDA.1 / CEBDA.1d / 2p) — Construye un cuadro de mandos con: valor actual de cada magnitud por zona, evolución de las últimas 6 h, tabla de alertas recientes y un indicador con el porcentaje de tiempo fuera de umbral. Debe incluir al menos una variable de dashboard y una regla de alerta.


Sesión 7 — Integración de un LLM vía API

Objetivos * Invocar una API de LLM desde el pipeline IoT y tratar su respuesta como un dato más. * Valorar críticamente qué aporta y qué riesgos introduce un LLM en un sistema de monitorización.

Contenidos * Qué es una API de LLM: petición, mensajes, max_tokens, respuesta JSON. * Gestión de credenciales: variable de entorno en el contenedor, nunca la clave dentro del flujo exportado. * Llamada desde Node-RED con http request: cabeceras, cuerpo JSON, parseo de la respuesta. * Llamada desde Python como alternativa. * Construcción del prompt en un nodo función a partir de datos reales de InfluxDB. * Salida estructurada: exigir JSON puro en el system prompt y parsear con JSON.parse(); manejar el fallo de parseo. * Casos de uso del proyecto: 1. Resumen en lenguaje natural del estado de la planta en las últimas 24 h. 2. Explicación de una alerta: dado un pico anómalo y su contexto, redactar causa probable y acción recomendada. 3. Clasificación de severidad de eventos con salida {"nivel": "...", "motivo": "..."}. 4. Traducción de lenguaje natural a Flux (uso exploratorio, siempre con revisión humana). * Realimentación: escribir la respuesta del LLM de vuelta en InfluxDB o MySQL y mostrarla en un panel de texto de Grafana. * Consideraciones críticas: latencia, coste por token, límites de tasa, alucinación, no invocar el LLM por cada punto de dato (agregar primero), y por qué el LLM nunca debe ser el único disparador de una acción física.

Desarrollo (3 h) | Tiempo | Actividad | | --- | --- | | 30' | Teoría: APIs de LLM, credenciales y coste | | 40' | Taller: primera llamada desde Node-RED y parseo de la respuesta | | 50' | Taller: prompt construido con datos reales de Influx → resumen diario | | 40' | Taller: salida estructurada JSON y escritura del resultado en la base de datos | | 20' | Debate: límites, riesgos y responsabilidad. ¿Dónde no pondrías un LLM? |

Entregable: flujo 05-llm.json (sin la clave de API dentro) y ejemplos de respuestas obtenidas.

Actividad 7 (RAMIA.4 / CEMIA.4d / 2p) — Implementa un flujo que, una vez al día, consulte a InfluxDB el resumen de las últimas 24 h, lo envíe al LLM y almacene el informe generado. El prompt debe forzar salida JSON con los campos resumen, anomalias y recomendacion. Muestra el resultado en Grafana. Documenta en media página qué acertó y qué falló el modelo, con un ejemplo concreto de cada.


Sesión 8 — Proyecto integrador, análisis avanzado y evaluación

Objetivos * Integrar todas las piezas en un sistema funcional de extremo a extremo. * Defender las decisiones técnicas tomadas.

Contenidos * Revisión del pipeline completo: simulación → MQTT → Telegraf → InfluxDB → Flux/tareas → Grafana → LLM. * Endurecimiento de la frontera IT/OT: retirar la red it de Node-RED y comprobar que solo cae la rama de escritura directa. Si se cae algo más, hay acoplamiento oculto que documentar. * Resiliencia: qué pasa si cae el broker, si Influx no responde, si la API del LLM da error. * Documentación del proyecto y reproducibilidad (arrancar desde cero con los JSON exportados). * Ampliación opcional: análisis con pandas — DatetimeIndex, resample, rolling, shift, interpolate y descomposición de la serie. Aquí encaja el proyecto de predicción de consumo energético de timeseries.md. * Presentaciones y evaluación.

Desarrollo (3 h) | Tiempo | Actividad | | --- | --- | | 75' | Taller final: cierre de la integración y pruebas de fallo | | 30' | Ampliación pandas (para quien haya terminado) | | 60' | Presentaciones (8-10 min por equipo) | | 15' | Cierre y autoevaluación |

Entrega final: repositorio con docker-compose.yml, flujos Node-RED, telegraf.conf, consultas Flux, scripts Python, dashboard.json y un README.md que permita a otra persona reproducir el sistema.


4. Plan de refactorización de los apuntes existentes

timeseries.md

  • Mantener: introducción, tipos, características, descomposición, tabla resumen, almacenamiento. → Sesión 1.
  • Mover: todo el bloque de pandas (resample, rolling, shift, interpolate) y el proyecto de energía → Sesión 8 como ampliación.
  • Añadir: párrafo puente que anticipe el pipeline del proyecto.
  • Limpiar: los bloques comentados de estacionariedad, o bien rescatarlos como cuadro informativo en la ampliación.

nodered.md

  • Mantener: de "Hola Node-RED" hasta "Añadiendo lógica de control". → Sesión 2.
  • Refactorizar (prioritario): sección MQTT, cambiando HiveMQ por Mosquitto local y los topics a la jerarquía otitia/.... → Sesión 3.
  • Reducir: la sección de MongoDB Atlas. En este proyecto el destino es InfluxDB. Dejarla como nota comparativa de una página o desplazarla a otro módulo.
  • Decidir: FlowFuse Dashboard. Propuesta: conservarlo en la sesión 2 como visualización rápida de desarrollo, y contrastarlo con Grafana en la sesión 6.
  • Reescribir actividades: las cuatro actuales apuntan a MongoDB y HiveMQ; sustituirlas por las actividades 2 y 3 de este plan.

influxdb.md

  • Dividir en dos por volumen (1300 líneas es demasiado para una sesión):
  • influxdb-modelo.md — elementos, line protocol, primeros pasos, UI, CLI, ingesta. → Sesión 4.
  • influxdb-flux.md — Flux, tareas, Python. → Sesión 5.
  • Añadir a la parte de ingesta: sección de Telegraf con mqtt_consumer (ahora está muy breve y es clave en el stack).
  • Eliminar: la sección vacía y comentada de Grafana; pasa a documento propio.
  • Actualizar: la actividad final ya menciona s8a / h2v — alinearla con la medida sensores y los tags acordados.

Documentos nuevos

grafana.md — índice propuesto: 1. Qué es Grafana y su papel en el stack 2. Datasources: InfluxDB v2 (Flux) e InfluxDB 3 Core (SQL) 3. Anatomía de un dashboard: paneles, filas, rango temporal 4. Tipos de panel y cuándo usar cada uno 5. Consultas Flux desde Grafana y transformaciones 6. Variables de dashboard 7. Umbrales, unidades y formato 8. Alertas: reglas y puntos de contacto 9. Exportación, versionado y aprovisionamiento 10. Comparativa: Influx UI vs FlowFuse Dashboard vs Grafana 11. Referencias y actividades

llm-iot.md — índice propuesto: 1. Qué aporta un LLM a un sistema IoT (y qué no) 2. Anatomía de una llamada a la API: mensajes, parámetros, respuesta 3. Gestión segura de credenciales en Docker y Node-RED 4. Llamada desde Node-RED con http request 5. Llamada desde Python 6. Construcción del prompt a partir de datos de InfluxDB 7. Salida estructurada en JSON y control de errores de parseo 8. Casos de uso: resumen, explicación de anomalías, clasificación, consulta en lenguaje natural 9. Realimentación: almacenar la respuesta y mostrarla en Grafana 10. Coste, latencia, límites de tasa y estrategias de agregación previa 11. Límites y responsabilidad: por qué el LLM no decide sobre actuadores 12. Referencias y actividades


5. Evaluación

Reparto por actividades

Sesión Actividad RA / CE Puntos
1 Documentación del stack RABDA.1 / CEBDA.1d 1
2 Simulador de sensores RAMIA.4 / CEMIA.4d 2
3 Publicación y consumo MQTT RAMIA.4 / CEMIA.4d 2
4 Modelado e ingesta en InfluxDB RABDA.1 / CEBDA.1d 2
5 Consultas Flux, tarea y Python RABDA.1 / CEBDA.1d 2
6 Cuadro de mandos en Grafana RABDA.1 / CEBDA.1d 2
7 Integración del LLM RAMIA.4 / CEMIA.4d 2
8 Proyecto integrador y defensa Ambos 3
— Ampliación pandas (opcional) RABDA.1 +1

Criterios transversales

Aplicables a todas las entregas, en la línea de lo que ya indicas en nodered.md:

  • Corrección funcional del flujo o consulta.
  • Calidad del código JavaScript, Flux y Python.
  • Documentación mediante comentarios en los propios flujos.
  • Justificación de las decisiones de diseño (esquema, QoS, tags vs fields).
  • Reproducibilidad: el trabajo debe arrancar desde cero en otra máquina.