Grafana para Dashboards y Visualización IoT

Grafana es la herramienta de visualización de referencia para entornos IoT, permitiendo crear dashboards en tiempo real con datos de sensores procedentes de InfluxDB, TimescaleDB, MQTT y otras fuentes. Con Grafana puedes visualizar temperaturas, consumo eléctrico, humedad y cualquier métrica de dispositivos conectados, configurar alertas automáticas cuando se superan umbrales y compartir dashboards públicos con clientes o equipos. Esta guía cubre la instalación, la conexión a fuentes de datos IoT y la creación de dashboards efectivos.

Requisitos Previos

  • Ubuntu 20.04/22.04 o CentOS/Rocky 8/9
  • Mínimo 2 GB RAM y 2 vCPU
  • Una base de datos de series temporales: InfluxDB 2.x o TimescaleDB (PostgreSQL)
  • Datos de sensores disponibles (MQTT + Telegraf o ingesta directa)
  • Puerto 3000 disponible

Instalación de Grafana

Ubuntu/Debian

# Añadir el repositorio oficial de Grafana
sudo apt-get install -y apt-transport-https software-properties-common wget
wget -q -O - https://apt.grafana.com/gpg.key | sudo gpg --dearmor -o /usr/share/keyrings/grafana.gpg

echo "deb [signed-by=/usr/share/keyrings/grafana.gpg] https://apt.grafana.com stable main" | \
  sudo tee /etc/apt/sources.list.d/grafana.list

# Instalar Grafana Enterprise (incluye todas las funciones OSS)
sudo apt-get update
sudo apt-get install -y grafana

# Habilitar e iniciar Grafana
sudo systemctl enable grafana-server
sudo systemctl start grafana-server

# Verificar el estado del servicio
sudo systemctl status grafana-server

CentOS/Rocky Linux

# Añadir el repositorio de Grafana para RPM
sudo tee /etc/yum.repos.d/grafana.repo << 'EOF'
[grafana]
name=grafana
baseurl=https://rpm.grafana.com
repo_gpgcheck=1
enabled=1
gpgcheck=1
gpgkey=https://rpm.grafana.com/gpg.key
sslverify=1
sslcacert=/etc/pki/tls/certs/ca-bundle.crt
EOF

sudo dnf install -y grafana
sudo systemctl enable --now grafana-server

Con Docker

# Ejecutar Grafana con Docker, montando volumen para persistencia
docker run -d \
  --name grafana \
  -p 3000:3000 \
  -v grafana-data:/var/lib/grafana \
  -e GF_SECURITY_ADMIN_PASSWORD=TuPasswordSeguro \
  -e GF_USERS_ALLOW_SIGN_UP=false \
  grafana/grafana:latest

# Verificar que está corriendo
docker logs grafana --tail=20

Configuración inicial

# Editar la configuración principal de Grafana
sudo tee /etc/grafana/grafana.ini << 'EOF'
[server]
# Dominio público de Grafana
domain = grafana.tudominio.com
root_url = https://grafana.tudominio.com/

[security]
# Cambiar siempre la contraseña por defecto en producción
admin_user = admin
admin_password = TuPasswordMuySeguro
secret_key = clave-secreta-aleatoria-de-al-menos-32-caracteres

[users]
# Deshabilitar el registro de nuevos usuarios
allow_sign_up = false
default_theme = dark

[auth.anonymous]
# Deshabilitar acceso anónimo (habilitarlo solo para dashboards públicos)
enabled = false

[smtp]
# Configurar SMTP para alertas por correo
enabled = true
host = smtp.tudominio.com:587
user = [email protected]
password = TuPasswordSMTP
from_address = [email protected]
from_name = Grafana IoT
EOF

sudo systemctl restart grafana-server

Configuración de Fuentes de Datos IoT

Accede a Grafana en http://tu-servidor:3000 (usuario: admin, contraseña: la configurada).

Conexión con InfluxDB

InfluxDB 2.x es la base de datos de series temporales más usada con Grafana para IoT:

# Primero, verificar que InfluxDB está accesible
curl -I http://localhost:8086/health

# Crear un token de API en InfluxDB para Grafana (si no tienes uno)
# Usando la CLI de InfluxDB
influx auth create \
  --org mi-organizacion \
  --description "Token para Grafana" \
  --read-buckets \
  --write-buckets

Configuración desde la interfaz de Grafana:

  1. Ve a Configuración → Data Sources → Add data source
  2. Selecciona InfluxDB
  3. Configura:
    • Query Language: Flux
    • URL: http://localhost:8086
    • Organization: tu organización en InfluxDB
    • Token: el token de API generado
    • Default Bucket: el bucket con los datos IoT

Ejemplo de query Flux para visualizar temperaturas:

// Query Flux: obtener lecturas de temperatura de las últimas 24 horas
from(bucket: "sensores-iot")
  |> range(start: -24h)
  |> filter(fn: (r) => r._measurement == "temperatura")
  |> filter(fn: (r) => r._field == "valor")
  |> filter(fn: (r) => r.ubicacion == "sala")
  |> aggregateWindow(every: 5m, fn: mean, createEmpty: false)
  |> yield(name: "media-5min")

Conexión con TimescaleDB

TimescaleDB extiende PostgreSQL con funciones de series temporales, ideal si ya usas PostgreSQL:

# Verificar la conexión a TimescaleDB
psql -h localhost -U grafana_user -d sensores_iot -c "\dt"

Configuración en Grafana:

  1. Data Sources → Add data source → PostgreSQL
  2. Configura:
    • Host: localhost:5432
    • Database: sensores_iot
    • User: grafana_user (usuario de solo lectura)
    • Password: tu contraseña
    • SSL Mode: require (recomendado)
    • TimescaleDB: activar el toggle

Ejemplo de query SQL para TimescaleDB:

-- Lecturas de temperatura con agregación por ventana de tiempo
SELECT
  time_bucket('5 minutes', timestamp) AS time,
  sensor_id,
  AVG(valor) AS temperatura_media,
  MIN(valor) AS temperatura_min,
  MAX(valor) AS temperatura_max
FROM lecturas_sensores
WHERE
  $__timeFilter(timestamp)
  AND tipo_sensor = 'temperatura'
  AND sensor_id = '$sensor'
GROUP BY time, sensor_id
ORDER BY time ASC

Visualización de Datos de Sensores

Panel de temperatura en tiempo real

Configuración recomendada para un panel de temperatura:

{
  "type": "timeseries",
  "title": "Temperatura en Tiempo Real",
  "fieldConfig": {
    "defaults": {
      "unit": "celsius",
      "min": -10,
      "max": 50,
      "thresholds": {
        "mode": "absolute",
        "steps": [
          {"color": "blue", "value": null},
          {"color": "green", "value": 18},
          {"color": "yellow", "value": 28},
          {"color": "red", "value": 35}
        ]
      },
      "custom": {
        "lineWidth": 2,
        "fillOpacity": 10,
        "spanNulls": true
      }
    }
  }
}

Panel de estado de dispositivos (Stat)

Para mostrar el estado actual de múltiples sensores:

// Query Flux: último valor de cada sensor
from(bucket: "sensores-iot")
  |> range(start: -5m)
  |> filter(fn: (r) => r._measurement == "estado_dispositivo")
  |> filter(fn: (r) => r._field == "online")
  |> last()
  |> group(columns: ["sensor_id"])

Mapa de calor para distribución de valores

// Query para mapa de calor: distribución de temperatura por hora del día
from(bucket: "sensores-iot")
  |> range(start: -30d)
  |> filter(fn: (r) => r._measurement == "temperatura")
  |> filter(fn: (r) => r._field == "valor")
  |> aggregateWindow(every: 1h, fn: mean)
  |> map(fn: (r) => ({
      r with
      hora: string(v: date.hour(t: r._time))
  }))

Variables y Plantillas de Dashboard

Las variables hacen los dashboards interactivos, permitiendo filtrar por sensor, ubicación o período:

# Las variables se configuran en Dashboard Settings → Variables

# Variable tipo Query para seleccionar sensor dinámicamente
# En InfluxDB Flux:
import "influxdata/influxdb/schema"

schema.tagValues(
  bucket: "sensores-iot",
  tag: "sensor_id"
)

# Variable de intervalo personalizada
# Name: intervalo
# Type: Interval
# Values: 1m,5m,15m,30m,1h,6h,1d
# Default: 5m

Uso de variables en queries:

// Usar la variable $sensor en una query Flux
from(bucket: "sensores-iot")
  |> range(start: v.timeRangeStart, stop: v.timeRangeStop)
  |> filter(fn: (r) => r._measurement == "temperatura")
  |> filter(fn: (r) => r.sensor_id == "${sensor}")
  |> aggregateWindow(every: ${intervalo}, fn: mean, createEmpty: false)

Configuración de Alertas

Grafana Alerting permite notificar cuando los valores de sensores superan umbrales:

# Configurar un canal de notificaciones (webhook, email, Slack, etc.)
# API de Grafana para crear un canal de notificaciones
curl -X POST http://localhost:3000/api/v1/provisioning/contact-points \
  -H "Content-Type: application/json" \
  -u admin:TuPassword \
  -d '{
    "name": "Alertas IoT",
    "type": "email",
    "settings": {
      "addresses": "[email protected]",
      "singleEmail": false
    }
  }'

Configuración de una regla de alerta desde la UI:

  1. Ve al panel de temperatura → Edit → Alert tab
  2. Configura:
    • Condition: WHEN last() OF query(A, 5m, now) IS ABOVE 35
    • Evaluate every: 1m for 5m
    • No data: Alerting
    • Notifications: selecciona el canal configurado

Ejemplo de regla de alerta en formato provisionamiento YAML:

# /etc/grafana/provisioning/alerting/temperatura-alta.yaml
apiVersion: 1
groups:
  - orgId: 1
    name: "Alertas IoT"
    folder: "IoT"
    interval: 1m
    rules:
      - uid: temperatura-sala-alta
        title: "Temperatura Sala Alta"
        condition: C
        data:
          - refId: A
            relativeTimeRange:
              from: 300
              to: 0
            datasourceUid: influxdb-uid
            model:
              query: |
                from(bucket: "sensores-iot")
                  |> range(start: -5m)
                  |> filter(fn: (r) => r._measurement == "temperatura")
                  |> filter(fn: (r) => r.ubicacion == "sala")
                  |> mean()
          - refId: C
            datasourceUid: __expr__
            model:
              type: threshold
              conditions:
                - evaluator:
                    params: [35]
                    type: gt
                  query:
                    params: [A]
        noDataState: NoData
        execErrState: Error
        for: 5m
        annotations:
          summary: "Temperatura en sala supera 35°C"
          description: "Temperatura actual: {{ $values.A.Value }}°C"
        labels:
          severity: warning
          ubicacion: sala

Dashboards Públicos y Compartidos

# Habilitar dashboards públicos en grafana.ini
sudo tee -a /etc/grafana/grafana.ini << 'EOF'

[public_dashboards]
enabled = true
EOF

sudo systemctl restart grafana-server

Para compartir un dashboard:

  1. Abre el dashboard → icono de compartir → pestaña Public Dashboard
  2. Activa Enable public access
  3. Copia la URL pública generada

Para incrustar un panel en una web:

<!-- Código de inserción (embed) de un panel de Grafana -->
<iframe
  src="https://grafana.tudominio.com/d-solo/abc123/mi-dashboard?orgId=1&panelId=2&from=now-1h&to=now&refresh=30s"
  width="800"
  height="400"
  frameborder="0">
</iframe>

Solución de Problemas

No se cargan datos en el panel:

# Verificar la conexión con la fuente de datos
# En Grafana: Data Sources → tu fuente → Save & Test

# Comprobar que Grafana puede alcanzar InfluxDB
curl -v http://localhost:8086/health

# Ver los logs de Grafana
sudo journalctl -u grafana-server --no-pager -n 50

Las alertas no se envían:

# Probar el canal de notificaciones manualmente desde Grafana
# Alerting → Contact points → Test

# Verificar la configuración SMTP
sudo grep -A 10 "\[smtp\]" /etc/grafana/grafana.ini

# Ver logs de alertas
sudo journalctl -u grafana-server | grep -i alert

El dashboard es lento con muchos datos:

# Reducir la resolución de los datos con aggregateWindow en Flux
# O usar GROUP BY time($__interval) en SQL

# Configurar el tiempo de caché de queries
sudo tee -a /etc/grafana/grafana.ini << 'EOF'
[caching]
enabled = true
ttl = 30s
EOF

sudo systemctl restart grafana-server

Grafana no arranca tras actualización:

# Ver los errores de inicio
sudo journalctl -u grafana-server --no-pager -n 100

# Verificar la integridad de la base de datos de Grafana
sudo -u grafana grafana-cli admin data-migration

Conclusión

Grafana convierte los datos crudos de sensores IoT en dashboards accionables que permiten a los equipos de operaciones tomar decisiones en tiempo real. La integración nativa con InfluxDB y TimescaleDB, combinada con el sistema de alertas y las variables dinámicas, hace de Grafana la plataforma ideal para monitorizar cualquier infraestructura IoT, desde instalaciones industriales hasta smart buildings. Con los dashboards públicos habilitados, compartir el estado de tu infraestructura con clientes o equipos externos es tan sencillo como copiar una URL.