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
| Técnica | Qué hace | Qué se pierde |
|---|---|---|
| Filtrado (tema 10) | Elimina el ruido de cada lectura antes de usarla | Rapidez de respuesta a cambios bruscos |
| Banda muerta (informar por excepción) | Envía solo cuando el valor cambió lo suficiente | Cambios menores que la banda |
| Latido (heartbeat) | Envía igual cada cierto tiempo, aunque no haya cambios | Un poco del ahorro |
| Resumen por ventanas | Reemplaza muchas lecturas por mínimo, media y máximo | La forma de la señal dentro de la ventana y el instante exacto |
| Detección de eventos | Avisa al instante cuando se cumple una condición (umbral sostenido) | Rapidez, por el antirrebote |
| Control local | Actúa sobre el actuador sin consultar a nadie | Flexibilidad: la regla vive en el firmware |
| Muestreo adaptativo (tema 9) | Mide más seguido cuando algo cambia y menos cuando no | Complejidad 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:
| Estrategia | Retraso | Si 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 contestar | No se entera ni reacciona hasta que vuelva |
| Banda muerta de 0,5 °C | 2 s | Igual |
| Resumen cada 60 s | 27 s | Igual |
| Resumen cada 300 s | 207 s | Igual |
| Resumen cada 60 s + alerta local | 13 s | El 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
…/cfgcon 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
Elegí la estrategia y presioná «Calcular».
Recorrido guiado
- 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».
- 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.
- 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».
- 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.
- 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».
- 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
- Definí qué decisión importa: qué evento no puede pasar inadvertido y con cuánto retraso.
- Calculá el presupuesto de envíos que permiten la batería, el enlace y el plan de datos.
- Filtrá lo que sobra: banda muerta con un error máximo justificado y un latido para distinguir «estable» de «muerto».
- Resumí con mínimo, media y máximo, y elegí la ventana según el retraso tolerable para lo que no es urgente.
- Agregá una alerta local con antirrebote e histéresis para lo urgente, independiente de la ventana.
- Hacé que el control crítico sea autónomo, con límites propios que la nube no pueda saltear.
- Hacé configurables los parámetros y versionalos; probá cada regla con datos simulados antes de poner el dispositivo en campo.
- Conservá un buffer local de los datos crudos recientes, para diagnosticar lo que el filtro descartó.
- 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.