← Índice del curso

Internet de las Cosas · Tema 31

Procesamiento local

Un sensor que manda cada lectura a la nube es fácil de programar y caro de sostener: gasta batería, ocupa la red, llena la base de datos de valores casi iguales y deja de funcionar cuando se corta la conexión. El procesamiento local mueve parte del trabajo al dispositivo: decidir qué vale la pena enviar, resumir lo demás y reaccionar sin esperar respuesta.

01 · Punto de partida

Por qué decidir en el dispositivo

En el bloque anterior el dispositivo hacía de mensajero: leía un sensor, armaba un mensaje y lo mandaba. Toda la inteligencia estaba en el servidor. Ese diseño tiene tres problemas que crecen con la flota:

  • Energía y datos. Cada envío cuesta una conexión (o mantener una abierta) y bytes. En el tema 30 vimos que, con una conexión nueva por envío, 3600 mensajes por hora agotan una pila de 2000 mAh en un día y medio.
  • Latencia y dependencia. Si la decisión se toma en la nube, una orden de «apagá la bomba» necesita que haya red, que el servidor responda y que la respuesta vuelva. Con la conexión caída, el dispositivo no hace nada.
  • Costo y ruido. Almacenar y procesar miles de lecturas casi idénticas cuesta dinero y esconde los pocos datos que importan.

La respuesta es repartir el trabajo: el dispositivo, que tiene la información en el momento exacto y sin red de por medio, hace lo que sea simple, rápido y local; la nube hace lo que requiere ver muchos dispositivos o mucha historia. Esto se conoce como cómputo en el borde (edge computing), y en un microcontrolador se reduce a unas pocas técnicas que caben en decenas de líneas de código.

Los números del laboratorio salen de simular una hora de una cámara frigorífica con una lectura por segundo, con una puerta que se abre y deja subir la temperatura; podés reproducirlos con el código del tema.

02 · Panorama

Las técnicas básicas

Dónde ocurre el procesamiento local Un sensor entrega una lectura por segundo a un bloque de procesamiento local, que filtra por banda muerta, resume por ventanas y detecta eventos. Del bloque salen pocos mensajes por la red hacia la nube. Además, el bloque actúa directamente sobre un actuador local sin pasar por la red, y sigue funcionando si la red cae. Sensor1 lectura/s Filtrarbanda muerta, latido Resumirmínimo, media, máximo Decidirumbral, antirrebote Redpocos mensajes Nubehistoria, flota Actuador localsin pasar por la red en el microcontrolador
Técnicas de procesamiento local, qué hacen y qué cuestan.
TécnicaQué haceQué se pierde
Filtrado (tema 10)Elimina el ruido de cada lectura antes de usarlaRapidez de respuesta a cambios bruscos
Banda muerta (informar por excepción)Envía solo cuando el valor cambió lo suficienteCambios menores que la banda
Latido (heartbeat)Envía igual cada cierto tiempo, aunque no haya cambiosUn poco del ahorro
Resumen por ventanasReemplaza muchas lecturas por mínimo, media y máximoLa forma de la señal dentro de la ventana y el instante exacto
Detección de eventosAvisa al instante cuando se cumple una condición (umbral sostenido)Rapidez, por el antirrebote
Control localActúa sobre el actuador sin consultar a nadieFlexibilidad: la regla vive en el firmware
Muestreo adaptativo (tema 9)Mide más seguido cuando algo cambia y menos cuando noComplejidad del firmware

Casi todas tienen la misma forma: pierden información a cambio de ahorrar recursos. La ingeniería está en elegir qué información sobra. El resto del tema muestra cómo medir esa pérdida con un caso concreto.

03 · Enviar solo lo que cambió

Informar por excepción

La banda muerta (deadband) es la técnica más simple y de mayor retorno. El dispositivo recuerda el último valor enviado y solo manda uno nuevo si difiere del anterior en al menos la banda. Una temperatura estable entre 4,0 y 4,2 °C no genera mensajes; una que sube medio grado, sí.

Tiene dos fallas que se corrigen con reglas simples:

  • El silencio es ambiguo. Si el dispositivo no manda nada, la nube no sabe si la temperatura es estable o si el dispositivo murió. La solución es el latido: se envía el valor actual cada cierto tiempo (por ejemplo, 5 minutos) aunque no haya cambiado. Con el testamento del tema 28, la nube también se entera de las caídas de conexión, pero no de un sensor colgado.
  • La banda se mide contra lo último enviado, no contra la realidad. Una deriva lenta acumula error hasta que supera la banda, y un cambio menor que la banda nunca llega. La banda es el error máximo que se acepta entre lo que ve la nube y lo que pasa.

En nuestro ejemplo (una hora de una cámara frigorífica, una lectura por segundo), una banda de 0,5 °C con un latido de 5 minutos convierte 3600 mensajes en 31: un 99,1 % menos de datos, y la nube se entera de la puerta abierta a los 2 segundos de que la temperatura superó los 8,0 °C. Pero la banda se elige con cuidado: con 2,0 °C el pico real de 9,3 °C se reporta como 8,2 °C, y la nube no se entera de que se superó por más de un grado.

04 · Menos mensajes, más contenido

Resumir por ventanas

Otra opción es agregar: en lugar de enviar cada lectura, el dispositivo acumula las de una ventana de tiempo y envía un solo mensaje con su mínimo, su media y su máximo (y la cantidad de lecturas). Un mensaje por minuto reemplaza 60, y conserva lo esencial: el rango en que se movió la señal.

  • Guardar el máximo y el mínimo, no solo la media. La media de una ventana en la que la temperatura subió a 9,3 °C durante 20 segundos casi no se mueve, pero el máximo lo registra. Resumir solo con la media esconde justo los eventos que importan.
  • El resumen llega al final de la ventana. Si la ventana es de 5 minutos, la nube se entera de un evento hasta 5 minutos tarde. En nuestro ejemplo: 27 segundos con ventanas de 60 s, y 207 s con ventanas de 300 s.
  • El mensaje agregado es más grande que una lectura (111 bytes contra 84 en este caso) pero reemplaza a muchas: 60 lecturas por minuto pesan 5040 bytes, contra 111 del resumen.
  • La ventana es un compromiso entre ahorro y retraso: ventanas largas ahorran más y avisan más tarde.

Con ventanas de 60 s, una hora cuesta 60 mensajes y 6660 bytes (97,8 % menos que enviar todo) y la batería con una conexión por envío dura 82,5 días contra 1,4. Con ventanas de 5 minutos, 12 mensajes y 1,1 años.

05 · Autonomía

Reaccionar sin esperar a la nube

La tercera técnica no ahorra datos: ahorra tiempo y dependencia. Si el dispositivo detecta por sí mismo que algo anda mal, puede avisar en el acto, y si controla un actuador, puede actuar sin preguntar a nadie. Dos ejemplos:

  • Alarma local. El dispositivo vigila un umbral (8,0 °C en una cámara de frío) y, cuando se supera de forma sostenida, publica un evento y, si hace falta, enciende un aviso sonoro. No necesita que la nube lea 3600 mensajes por hora para darse cuenta.
  • Control en lazo cerrado. Un termostato o una bomba de riego decide con su propio sensor. Si se corta Internet, el termostato sigue regulando; si dependiera de la nube, la calefacción quedaría encendida o apagada.

Las alarmas simples tienen un problema típico: el ruido de la señal. Una temperatura que sube por la puerta cruza los 8,0 °C varias veces, porque las lecturas fluctúan unas décimas. La regla ingenua («avisar cada vez que se cruza») produce 5 alertas en nuestro ejemplo. Dos reglas lo arreglan, y el tema siguiente las estudia en detalle:

  • Antirrebote: exigir que la condición se cumpla durante varias lecturas seguidas (acá, 10).
  • Histéresis: una vez disparada, la alarma no se rearma hasta que el valor baje bastante por debajo del umbral (acá, 7,5 °C).

Con las dos, la alerta sale una sola vez, a los 13 segundos de que la temperatura superó el umbral (9 de esos segundos son las lecturas necesarias para confirmar). Compará con el costo de decidir en la nube:

Cuándo se entera la nube de que la temperatura superó los 8,0 °C (primer dato por encima a los 1592 s).
EstrategiaRetrasoSi se corta la red en ese momento
Enviar todo (una lectura por segundo)0 s de datos, más lo que tarde la nube en decidir y contestarNo se entera ni reacciona hasta que vuelva
Banda muerta de 0,5 °C2 sIgual
Resumen cada 60 s27 sIgual
Resumen cada 300 s207 sIgual
Resumen cada 60 s + alerta local13 sEl dispositivo ya detectó y puede actuar o guardar el evento y enviarlo al reconectar

La decisión local no reemplaza a la nube: la nube sigue viendo el resumen, analizando tendencias entre dispositivos y mandando la configuración. Pero todo lo que debe ocurrir aunque no haya red tiene que poder decidirse en el dispositivo.

06 · Con criterio

Costos y límites del procesamiento local

  • Información perdida. Lo que se descarta o se resume no se puede recuperar. Si más adelante querés detectar un patrón que hoy no imaginás, no vas a tener los datos crudos. Una salida es guardar localmente las últimas horas en un buffer circular (tema 33) y enviarlas bajo demanda.
  • Reglas en el firmware. Una regla que vive en el dispositivo se cambia con una actualización o con configuración remota. Conviene que los parámetros (banda, ventana, umbral, antirrebote) sean configurables: un mensaje retenido en …/cfg con su versión (tema 29) los cambia sin tocar el código.
  • Recursos limitados. Un resumen necesita unos pocos bytes de RAM y un puñado de operaciones; un algoritmo complejo (aprendizaje automático embebido, o TinyML) necesita memoria y energía que cuestan lo que se ahorra en red. Se justifica cuando el dato es muy grande (audio, imágenes, vibración) y el resultado es muy chico (una clase o una alarma).
  • Errores silenciosos. Un fallo en una regla local no se ve desde la nube: el dispositivo «funciona» y reporta valores plausibles pero equivocados. Hay que probar las reglas con datos simulados (como hace el código de este tema) y mantener un latido que incluya un contador de lecturas.
  • Seguridad. Una decisión crítica (cerrar una válvula, abrir una cerradura) no debe depender solo de una orden remota: el dispositivo conserva límites propios (temperatura máxima, tiempo máximo) que la nube no puede saltear.
  • Consistencia de la flota. Si cada dispositivo filtra distinto, los datos no son comparables. Los parámetros comunes se publican como configuración y se versionan.

07 · Del algoritmo al firmware

Un fragmento en C++

El firmware recibe una lectura por segundo (en décimas de grado, como entero, tema 12) y aplica las tres técnicas. Nada de lo que sigue conoce la red: publicar es la función que arma el tópico y envía por MQTT (tema 27). El código sigue el estilo Arduino para ESP32; lo escribí a partir del modelo de este tema y no lo compilé, así que tratalo como un esquema a probar.

const int BANDA = 5;                      // 0,5 °C: error máximo aceptado respecto de lo enviado
const unsigned long LATIDO_S = 300;       // se envía igual cada 5 minutos
const int VENTANA_S = 60;                 // resumen cada 60 lecturas
const int UMBRAL = 80, REARME = 75;       // 8,0 °C y 7,5 °C (histéresis)
const int MUESTRAS_ALERTA = 10;           // antirrebote: 10 lecturas seguidas

bool hayUltimo = false;
int ultimoEnviado = 0;
unsigned long tUltimoEnvio = 0;

long suma = 0;
int n = 0, minimo = 0, maximo = 0;

int seguidas = 0;
bool armada = true;

void publicar(const char *canal, const char *carga);   // arma el tópico y publica (tema 27)

// 1. Informar por excepción (alternativa al resumen)
void porExcepcion(int d, unsigned long t) {
  if (!hayUltimo || abs(d - ultimoEnviado) >= BANDA || t - tUltimoEnvio >= LATIDO_S) {
    char buf[48];
    snprintf(buf, sizeof buf, "{\"t\":%lu,\"c\":%d}", t, d);
    publicar("tel", buf);
    hayUltimo = true;
    ultimoEnviado = d;
    tUltimoEnvio = t;
  }
}

// 2. Resumir por ventanas
void resumir(int d, unsigned long t) {
  if (n == 0) { minimo = maximo = d; suma = 0; }
  suma += d;
  n++;
  if (d < minimo) minimo = d;
  if (d > maximo) maximo = d;
  if (n == VENTANA_S) {
    int media = (2 * suma + n) / (2 * n);              // media redondeada, solo con enteros
    char buf[96];
    snprintf(buf, sizeof buf, "{\"t\":%lu,\"n\":%d,\"min\":%d,\"med\":%d,\"max\":%d}",
             t - (VENTANA_S - 1), n, minimo, media, maximo);
    publicar("tel", buf);
    n = 0;
  }
}

// 3. Decidir: alarma con antirrebote e histéresis
void vigilar(int d, unsigned long t) {
  seguidas = (d > UMBRAL) ? seguidas + 1 : 0;
  if (armada && seguidas >= MUESTRAS_ALERTA) {
    char buf[64];
    snprintf(buf, sizeof buf, "{\"t\":%lu,\"a\":\"alta\",\"c\":%d}", t, d);
    publicar("evt/temperatura", buf);                  // QoS 1: es un evento (tema 29)
    armada = false;
    // aquí también se puede actuar: encender un aviso, cerrar una válvula…
  }
  if (d <= REARME) armada = true;
}

La alarma funciona con la red caída: publicar puede dejar el evento en una cola local (tema 33) para enviarlo al reconectar. Y las constantes de arriba son justo las que conviene recibir como configuración retenida.

08 · Analogía con programación

Comparar estrategias en Python

El programa genera la hora de temperatura de la cámara (4,0 °C que suben unas décimas, una puerta abierta entre los 1500 y los 1900 s que sube hasta unos 9 °C, y ruido de ±0,1 °C, con un generador pseudoaleatorio determinista para que cualquier ejecución dé lo mismo) y aplica cinco estrategias: enviar todo, banda muerta de 0,5 °C con latido, resumen cada 60 s, resumen cada 300 s y resumen cada 60 s con alerta local. Para cada una cuenta mensajes, bytes reales de MQTT 3.1.1 más 40 de encabezados de red, la duración de la batería de 2000 mAh con una conexión nueva por envío (los mismos 0,0167 mAh por conexión del tema 25), el retraso con que la nube ve un dato por encima de 8,0 °C y el pico máximo que llega. Al final compara las alertas con la regla ingenua y con antirrebote e histéresis.

Python en tu navegador. Los bloques son editables: cambiá los valores y presioná «Ejecutar Python». La primera vez tarda unos segundos en cargar.

# Procesamiento local: qué se manda a la nube cuando el dispositivo filtra, resume o decide solo.
# Una cámara frigorífica se mide una vez por segundo durante una hora; las lecturas se guardan en décimas de grado.
import re

T0 = 1794061920
SEGUNDOS = 3600
UMBRAL = 80                 # 8,0 °C: por encima se rompe la cadena de frío
REARME = 75                 # la alarma se rearma por debajo de 7,5 °C (histéresis)
MUESTRAS_ALERTA = 10        # la alarma exige 10 lecturas seguidas por encima del umbral (antirrebote)
BATERIA_MAH = 2000
SUENO_MAH_DIA = 0.24
MAH_CONEXION = 100 * 0.6 / 3600
IP_TCP = 40
TOPICO = "casa/camara07/tel"


def generar():
    """Temperatura real: 4,0 °C que suben despacio, una puerta abierta (de 1500 s a 1900 s) y ruido de ±0,1 °C."""
    estado = 777
    lecturas = []
    for t in range(SEGUNDOS):
        base = 40 + 4 * t / SEGUNDOS                               # décimas de grado
        if 1500 <= t < 1620:
            base += 50 * (t - 1500) / 120
        elif 1620 <= t < 1700:
            base += 50
        elif 1700 <= t < 1900:
            base += 50 * (1 - (t - 1700) / 200)
        estado = (estado * 1664525 + 1013904223) % 2**32
        ruido = (estado / 4294967296 - 0.5) * 2                      # entre -1 y 1 décima
        lecturas.append(int(base + ruido + 0.5))
    return lecturas


def largo_publish(carga):
    cuerpo = 2 + len(TOPICO) + len(carga)
    return 1 + (1 if cuerpo < 128 else 2) + cuerpo + IP_TCP


def todo(lect):
    return [(t, f'{{"t":{T0 + t},"c":{d}}}') for t, d in enumerate(lect)]


def banda_muerta(lect, banda, latido=300):
    envios, ultimo, t_ultimo = [], None, 0
    for t, d in enumerate(lect):
        if ultimo is None or abs(d - ultimo) >= banda or t - t_ultimo >= latido:
            envios.append((t, f'{{"t":{T0 + t},"c":{d}}}'))
            ultimo, t_ultimo = d, t
    return envios


def resumen(lect, ventana):
    envios = []
    for ini in range(0, SEGUNDOS, ventana):
        v = lect[ini:ini + ventana]
        media = (2 * sum(v) + len(v)) // (2 * len(v))
        envios.append((ini + len(v) - 1, f'{{"t":{T0 + ini},"n":{len(v)},"min":{min(v)},"med":{media},"max":{max(v)}}}'))
    return envios


def alertas(lect):
    """Alarma local con antirrebote (10 lecturas seguidas) e histéresis (se rearma por debajo de 7,5 °C)."""
    envios, seguidas, armada = [], 0, True
    for t, d in enumerate(lect):
        seguidas = seguidas + 1 if d > UMBRAL else 0
        if armada and seguidas >= MUESTRAS_ALERTA:
            envios.append((t, f'{{"t":{T0 + t},"a":"alta","c":{d}}}'))
            armada = False
        if d <= REARME:
            armada = True
    return envios


def alertas_ingenuas(lect):
    envios, antes = [], False
    for t, d in enumerate(lect):
        sobre = d > UMBRAL
        if sobre and not antes:
            envios.append((t, f'{{"t":{T0 + t},"a":"alta","c":{d}}}'))
        antes = sobre
    return envios


def evaluar(envios, lect):
    """Mensajes, bytes, vida de la batería con una conexión por envío, pico y retraso con que la nube se entera."""
    n = len(envios)
    bytes_h = sum(largo_publish(c) for _, c in envios)
    mah_dia = SUENO_MAH_DIA + n * 24 * MAH_CONEXION
    primero = next(t for t, d in enumerate(lect) if d > UMBRAL)         # primer instante realmente por encima
    visto = [t for t, c in envios if mayor_valor(c) > UMBRAL]
    retraso = (min(visto) - primero) if visto else None
    return n, bytes_h, BATERIA_MAH / mah_dia, retraso


def mayor_valor(carga):
    """El mayor dato de un mensaje: el máximo del resumen o la lectura suelta."""
    return int(re.search(r'"(?:max|c)":(\d+)', carga).group(1))


lect = generar()
real_max = max(lect)
print(f"Lecturas: {len(lect)}, mínimo {min(lect) / 10:.1f} °C, máximo {real_max / 10:.1f} °C, primer dato por encima de 8,0 °C en t = {next(t for t, d in enumerate(lect) if d > UMBRAL)} s")
print()
print(f"{'estrategia':<28} {'mensajes':>8} {'bytes/h':>9} {'reducción':>10} {'batería':>12} {'retraso':>8} {'pico':>6}")
base_bytes = None
for nombre, envios in (("todo (1 por segundo)", todo(lect)),
                       ("banda muerta 0,5 °C", banda_muerta(lect, 5)),
                       ("resumen cada 60 s", resumen(lect, 60)),
                       ("resumen cada 300 s", resumen(lect, 300)),
                       ("resumen 60 s + alerta", sorted(resumen(lect, 60) + alertas(lect)))):
    n, b, dias, ret = evaluar(envios, lect)
    base_bytes = base_bytes or b
    pico = max(mayor_valor(c) for _, c in envios)
    bat = f"{dias / 365:.1f} años" if dias >= 365 else f"{dias:.1f} días"
    print(f"{nombre:<28} {n:>8} {b:>9} {100 * (1 - b / base_bytes):>9.1f}% {bat:>12} {('—' if ret is None else str(ret) + ' s'):>8} {pico / 10:>5.1f}")

print()
ing, bue = alertas_ingenuas(lect), alertas(lect)
print(f"Alertas con la regla ingenua (un aviso cada vez que cruza 8,0 °C): {len(ing)}")
print(f"Alertas con antirrebote e histéresis: {len(bue)}, en t = {bue[0][0]} s")

Resultado con los valores iniciales. La hora tiene 3600 lecturas entre 3,9 y 9,3 °C, y el primer dato por encima de 8,0 °C aparece a los 1592 s. Enviar todo: 3600 mensajes, 302.400 bytes y 1,4 días de batería. Banda muerta de 0,5 °C: 31 mensajes, 2604 bytes (99,1 % menos), 158,2 días, retraso de 2 s y pico de 9,2 °C. Resumen cada 60 s: 60 mensajes, 6660 bytes (97,8 % menos), 82,5 días, retraso de 27 s y pico de 9,3 °C. Resumen cada 300 s: 12 mensajes, 1344 bytes (99,6 % menos), 1,1 años, retraso de 207 s. Resumen cada 60 s con alerta: 61 mensajes, 81,2 días y retraso de 13 s. La regla ingenua da 5 alertas; con antirrebote e histéresis, 1, a los 1605 s.

Tres conclusiones. Los ahorros son enormes (de 60 a 300 veces menos mensajes), porque la señal casi no cambia. El costo es el retraso y el detalle: el resumen de 5 minutos se entera 207 segundos tarde, y la banda muerta no ve cambios menores que la banda. Y la alerta local es lo que desacopla el retraso del ahorro: con ventanas largas para ahorrar datos y una alerta local para avisar, los eventos llegan en 13 segundos y el tráfico sigue siendo mínimo (el resumen cada 300 s con alerta cuesta 13 mensajes por hora).

09 · Exploración interactiva

¿Qué ve la nube?, de extremo a extremo

Este laboratorio aplica las estrategias a la misma hora simulada del código anterior. Elegí la estrategia, la banda (para la banda muerta) y la ventana (para el resumen y el resumen con alerta). El gráfico muestra la temperatura real en gris, lo que ve la nube en azul y el umbral en rojo; abajo, el costo y lo que se pierde.

Procesamiento local de una hora de temperatura

Mensajes en una hora—
Datos en una hora—
Reducción de datos—
Batería (una conexión por envío)—
Pico que ve la nube—
Retraso en ver más de 8,0 °C—
Resultado—

Elegí la estrategia y presioná «Calcular».

Recorrido guiado

  1. Con «Enviar todo», calculá: 3600 mensajes, 302 KB por hora y 1,4 días de batería con una conexión por envío. La nube se entera enseguida, pero casi todos los datos son iguales. Resultado: «Ahorra poco».
  2. Pasá a Banda muerta de 0,5 °C: 31 mensajes (99,1 % menos), 158,2 días de batería, la puerta se detecta a los 2 s. En el gráfico se ve la línea azul seguir a la real en escalones.
  3. Probá las otras bandas. Con 0,2 °C, 352 mensajes y 14,2 días; con 1,0 °C, 21 mensajes y 231,5 días. Con 2,0 °C, solo 15 mensajes, pero la nube ve un pico de 8,2 °C cuando el real fue de 9,3 °C: «Pierde el pico».
  4. Pasá a Resumen con ventana de 60 s: 60 mensajes, 82,5 días y retraso de 27 s; el pico (9,3 °C) se conserva porque se guarda el máximo. Las barras azules del gráfico muestran el rango de cada minuto.
  5. Subí la ventana a 5 minutos: 12 mensajes y 1,1 años, pero el retraso es de 207 s; con 15 minutos, 4 mensajes y 3,0 años, con el mismo retraso de 207 s. Resultado: «Se entera tarde».
  6. Pasá a Resumen + alerta local con ventana de 5 minutos: 13 mensajes por hora, 1,0 años de batería, y la alerta sale a los 13 s. El ahorro de los resúmenes largos sin el retraso. Es la combinación habitual.
Desafío: ¿cada cuánto se puede resumir para que la pila dure un año?

Con una conexión nueva por envío, la pila de 2000 mAh dura un año si gasta 2000 / 365 = 5,48 mAh por día o menos. Como el sueño consume 0,24 mAh por día y cada conexión 0,0167 mAh, caben (5,48 − 0,24) / 0,0167 ≈ 314 envíos por día, es decir, 13 por hora: una ventana mínima de 3600 / 13,1 ≈ 275 segundos. Con ventanas de 300 s (12 por hora) se llega a 1,1 años; con 60 s, a 82,5 días. Es la misma cuenta del sondeo del tema 30: el presupuesto de conexiones por día de una pila fija cuánto se puede reportar, y el procesamiento local decide qué ocupa ese presupuesto. Con la alerta local, los eventos urgentes no dependen de la ventana.

10 · Método de trabajo

Método de trabajo

  1. Definí qué decisión importa: qué evento no puede pasar inadvertido y con cuánto retraso.
  2. Calculá el presupuesto de envíos que permiten la batería, el enlace y el plan de datos.
  3. Filtrá lo que sobra: banda muerta con un error máximo justificado y un latido para distinguir «estable» de «muerto».
  4. Resumí con mínimo, media y máximo, y elegí la ventana según el retraso tolerable para lo que no es urgente.
  5. Agregá una alerta local con antirrebote e histéresis para lo urgente, independiente de la ventana.
  6. Hacé que el control crítico sea autónomo, con límites propios que la nube no pueda saltear.
  7. Hacé configurables los parámetros y versionalos; probá cada regla con datos simulados antes de poner el dispositivo en campo.
  8. Conservá un buffer local de los datos crudos recientes, para diagnosticar lo que el filtro descartó.
  9. Medí la pérdida: compará lo que ve la nube con la señal real (pico, retraso, error) antes de aceptar una estrategia.

11 · Comprensión

Confusiones frecuentes

«Conviene mandar todo a la nube, que ahí se decide mejor»

Mandar todo cuesta batería, ancho de banda y dinero, y deja al dispositivo inútil sin conexión. La nube decide mejor lo que requiere ver la flota o la historia, no lo que depende de una lectura puntual.

«Con una banda muerta no hace falta un latido»

Sin latido, el silencio es ambiguo: puede ser un valor estable o un sensor muerto. El latido cuesta pocos mensajes y evita no saber nunca qué pasó.

«Resumir con la media alcanza»

La media esconde los picos breves, que suelen ser lo que se quiere detectar. Un resumen útil lleva al menos mínimo, media, máximo y cantidad de lecturas.

«Una alarma local es simplemente if (d > umbral)»

Con ruido, eso dispara varias veces en cada cruce (5 alertas en nuestro ejemplo). Hace falta antirrebote (varias lecturas seguidas) e histéresis (un umbral de rearme más bajo).

«Procesar localmente ahorra siempre»

Ahorra datos y energía de radio, pero gasta CPU y memoria y complica el firmware. Los algoritmos pesados pueden costar más de lo que ahorran, y las reglas locales son más difíciles de cambiar.

«Lo que se descartó siempre se puede recuperar de la nube»

Si el dispositivo no lo envió, la nube nunca lo tuvo. Por eso conviene un buffer local de datos crudos que se pueda pedir bajo demanda.

12 · Práctica guiada

Predecí antes de comprobar

1. Bytes de una lectura

Una lectura {"t":1794061920,"c":41} se publica en casa/camara07/tel (17 caracteres). ¿Cuántos bytes ocupa en la red (con 40 de encabezados) y cuántos por hora si se envía cada segundo?

Ver solución

La carga mide 23 bytes. El cuerpo del PUBLISH es 2 + 17 + 23 = 42; con el encabezado fijo (1 + 1), 44; con los 40 de red, 84 bytes. Por hora: 84 × 3600 = 302.400 bytes.

2. El mensaje de resumen

Un resumen pesa 111 bytes (contra los 84 de una lectura). ¿Cuánto se ahorra al resumir 60 lecturas en un mensaje?

Ver solución

60 lecturas pesan 60 × 84 = 5040 bytes; el resumen, 111. Se ahorra 97,8 % (111 / 5040 ≈ 2,2 %), aunque el mensaje individual sea 32 % más grande.

3. El presupuesto de una pila

Con una conexión por envío y una pila de 2000 mAh, ¿cada cuánto como máximo se puede enviar un resumen para durar un año?

Ver solución

Cada envío cuesta 0,0167 mAh y el sueño 0,24 mAh por día; el límite es 5,48 mAh por día. Caben unos 314 envíos por día (13 por hora), es decir, uno cada 275 segundos como mínimo. Con 5 minutos se llega a 1,1 años.

4. Subir el antirrebote

¿Qué pasa si la alarma exige 60 lecturas seguidas en lugar de 10?

Ver solución

Elimina todavía más falsas alarmas, pero la alerta sale como mínimo 59 segundos después de que la temperatura se sostiene por encima del umbral (en vez de 9). Para una cadena de frío, donde el límite se mide en minutos, puede ser aceptable; para una fuga de gas, no.

5. La banda que esconde el pico

Con una banda de 2,0 °C la nube ve un pico de 8,2 °C y el real fue de 9,3 °C. ¿Por qué? ¿Cómo se corrige?

Ver solución

La banda se compara con el último valor enviado: después de enviar 8,2 °C, el dato siguiente solo se manda si difiere en 2,0 °C o más, y 9,3 − 8,2 = 1,1 °C no alcanza. Se corrige con una banda más chica, agregando el máximo en un resumen o con una alerta local que no dependa de la banda.

6. Se corta la red

La puerta se abre justo cuando se cae el Wi-Fi durante 10 minutos. ¿Qué estrategia sigue siendo útil?

Ver solución

Solo la que decide en el dispositivo: la alerta local detecta el evento a los 13 segundos, puede actuar (aviso sonoro, cierre de un circuito) y guarda el evento para enviarlo al reconectar (con QoS 1 y sesión persistente, tema 28). Las estrategias que dependen de que la nube vea los datos no se enteran hasta que vuelve la red.

13 · Síntesis

Ideas para recordar

  • Procesar en el dispositivo ahorra batería, red y almacenamiento, reduce la latencia y mantiene el funcionamiento sin conexión.
  • La banda muerta envía solo lo que cambió más que un error aceptado; necesita un latido para distinguir un valor estable de un dispositivo muerto.
  • El resumen por ventanas reemplaza muchas lecturas por mínimo, media, máximo y cantidad; la media sola esconde los picos.
  • Con una banda de 0,5 °C o ventanas de 60 s, una hora pasa de 3600 a 31 o 60 mensajes (97,8 a 99,1 % menos datos) y la batería de 1,4 a 158 o 82 días.
  • El ahorro se paga en retraso (27 s con ventanas de 60 s, 207 s con ventanas de 5 minutos) y en detalle perdido (una banda de 2,0 °C esconde el pico real).
  • La alerta local con antirrebote e histéresis detecta el evento en 13 s y una sola vez (5 alertas con la regla ingenua), sin depender de la ventana ni de la red.
  • Lo que debe ocurrir aunque no haya red se decide en el dispositivo; la nube conserva la historia y la visión de toda la flota.
  • Los parámetros de las reglas deben ser configurables y versionados, probados con datos simulados y acompañados de un buffer local de datos crudos.

¿Listo para el tema 32?

Antes de avanzar, tomá un sensor de tu proyecto y simulá una hora de datos con un evento. Aplicá una banda muerta y un resumen, y anotá mensajes, bytes, retraso y el pico que llega. Después probá una alarma con y sin antirrebote.

En el próximo tema profundizamos la parte de decisión con Reglas y umbrales en el dispositivo: cómo escribir, configurar y probar las reglas que el dispositivo ejecuta solo.