← Índice del curso

Internet de las Cosas · Tema 35

Telemetría

Una temperatura por segundo puede llenar la base de datos sin explicar por qué el sensor dejó de medir. Un latido puede demostrar que el firmware sigue enviando, pero repetir un valor viejo. Diseñar telemetría es elegir la información que permite entender el proceso y el dispositivo, con un presupuesto explícito de datos y de demora.

01 · Punto de partida

De guardar datos a producir información

En los temas 31 a 34 decidimos localmente, detectamos eventos, conservamos una cola y guardamos la historia en el servidor. Falta unir esas decisiones en un plan de telemetría: qué información sale del dispositivo, con qué significado, cuándo y bajo qué límites.

La telemetría es la medición y comunicación a distancia de información del sistema. En un equipo IoT incluye tanto el proceso que se observa —temperatura, nivel, posición— como las condiciones que permiten confiar en esa observación: calidad del sensor, antigüedad, reinicios y pérdidas.

Pregunta del proceso

«¿La cámara estuvo por encima de 8 °C?». Necesitás observar el incidente, conservar extremos o eventos y conocer cuándo ocurrió.

Pregunta del dispositivo

«¿Por qué hace diez minutos que no tengo una lectura válida?». Necesitás calidad, contadores, cola pendiente y señales de funcionamiento.

Enviar todo no garantiza responder bien ninguna de las dos. La frecuencia puede ser insuficiente para un evento breve y excesiva para una magnitud lenta. También puede faltar el único campo que permitiría distinguir una falla del sensor de una caída de la red.

Seguimos con una cámara frigorífica. El laboratorio plantea dos requisitos didácticos: que un incidente de cinco segundos se observe en la nube dentro de cinco segundos y que el tráfico estimado no supere una cuota elegida. Son condiciones de este ejercicio, no valores normativos para una instalación real.

02 · Elegir información útil

Qué conviene reportar

Partí de decisiones y preguntas concretas. Cada campo debería permitir interpretar el dato, detectar una falla o tomar una acción. Clasificar la información ayuda a darle frecuencia, prioridad y retención propias.

Tres flujos complementarios de telemetría para la cámara del curso.
FlujoContenidoDisparadorPara qué sirve
MedicionesTemperatura, unidad, tiempo y calidadPeríodo, cambio significativo o fin de ventanaHistoria y tendencias del proceso
EventosActivación y recuperación de alarma, causa y versión de reglaTransición confirmadaAtención oportuna y reconstrucción de incidentes
DiagnósticoTiempo encendido, memoria libre, señal, cola y descartesLatido periódico o cambio importanteEstado operativo y explicación de pérdidas

Los eventos no reemplazan toda la historia: una alarma dice que se cruzó una condición, pero no muestra cada lectura anterior. La historia tampoco reemplaza el evento cuando una decisión debe tomarse rápido. El diagnóstico explica si el silencio se debe a estabilidad, a una adquisición fallida o a información pendiente de entrega.

Medir lo que el receptor necesita saber

Si el panel muestra temperatura, tiene que conocer unidad, calidad y antigüedad. Si además alerta sobre pérdida de datos, necesita contadores de adquisición y descarte. Si compara dispositivos, necesita versiones de firmware y configuración para no mezclar equipos que aplican reglas distintas.

Algunos metadatos cambian poco, como modelo o versión de firmware. Se pueden publicar al arrancar y al cambiar, con una forma de consultarlos después. No hace falta repetir un texto largo idéntico en cada muestra, pero tampoco dejar al servidor sin forma de interpretar una carga compacta.

Un registro de depuración con detalles internos no es una medición del proceso. Puede tener otra retención y activarse temporalmente para diagnosticar. Mantener mensajes de depuración extensos de manera permanente puede consumir más datos que los sensores.

03 · Frecuencias independientes

Medir, reportar y enviar un latido

Hay tres períodos que no conviene tratar como una sola constante:

  • Muestreo: cada cuánto se adquiere una observación nueva. Determina qué cambios pueden verse.
  • Reporte: cada cuánto se prepara una entrega de mediciones o un resumen. Determina cuánto espera la información antes de enviarse.
  • Latido de diagnóstico: cada cuánto se informa el funcionamiento del equipo, aunque el proceso esté estable.
Muestreo, reporte y diagnóstico son tareas diferentes Un sensor se mide cada segundo. Las muestras alimentan una regla local que puede emitir eventos inmediatos y un buffer que prepara un reporte cada minuto. Una tarea de diagnóstico prepara un latido cada cinco minutos. Los tres flujos llegan a una cola de salida con prioridades. MuestreoCada 1 segundo Regla localEvento al confirmar la transición Lote o resumenReporte cada 60 segundos Diagnóstico cada 300 segundos Cola de salidaPrioridad y capacidadEnvío y reintentos

Medir cada segundo y reportar cada minuto permite ver un cambio breve localmente y ahorrar encabezados al agrupar. Medir cada minuto no permite recuperar después lo que ocurrió entre lecturas, aunque la red sea perfecta y se use un formato excelente.

Un evento de cinco segundos

En el laboratorio, la temperatura es 4,1 °C salvo entre t = 123 s y t = 128 s, intervalo [123, 128), donde vale 9,2 °C. Con muestreo en t = 0, 1, 2… se observan cinco lecturas altas. Con muestras cada 10 s, se mide en 120 y 130 s: el evento no aparece. Con una por minuto tampoco.

La regla del tema 32 activa con 3 s confirmados por encima del umbral y recupera con 2 s por debajo del rearme. Con muestreo de 1 s, activa en t = 126 s y recupera en t = 130 s. Los eventos salen sin esperar al reporte periódico.

Un lote o resumen de [120, 180) se envía al cerrar, en t = 180 s. La nube ve la primera evidencia periódica 57 s después del inicio del incidente. Con el evento local, la ve a los 3 s. Estas demoras excluyen red, cola y procesamiento remoto.

Un latido no reemplaza una adquisición

El firmware puede seguir enviando diagnóstico mientras el sensor falla. El latido prueba que una parte del sistema sigue ejecutándose y comunicándose; para probar que hay mediciones nuevas se necesita tiempo de última adquisición válida y avance de su contador.

04 · Qué viaja en cada envío

Individuales, lotes y resúmenes

Son tres planes diferentes, incluso si usan el mismo sensor y transporte. Elegir uno cambia la cantidad de mensajes, lo que espera la nube y la información que puede reconstruir.

Con una lectura por segundo y un reporte por minuto.
PlanQué envíaMensajes de medición por díaInformación conservada
IndividualUna carga por lectura, al adquirirla86.400Todas las observaciones
LoteUn arreglo con las 60 lecturas1440Todas las observaciones y su orden
ResumenCantidad, válidas, suma, mínimo y máximo1440Estadísticas de la ventana

Un lote reduce mensajes, pero no reduce la cantidad de lecturas que hay que almacenar si el servidor lo expande a filas individuales. Un resumen sí reduce esa cantidad, a costa de descartar la secuencia de valores. Esta diferencia conecta el presupuesto de red con el de la base del tema 34.

Un lote tampoco es necesariamente pequeño: 900 lecturas en un solo mensaje pueden superar el límite de carga elegido para la aplicación. Hay que dividirlo o cambiar el diseño. El planificador avisa si la carga representativa supera 1024 bytes; no la divide automáticamente ni incluye el costo de las partes que habría que generar.

Informar por cambio

La banda muerta del tema 31 también puede formar parte del plan: enviar cuando un valor cambie lo suficiente, con un límite máximo de silencio. Su tráfico depende de la señal real y no se deduce solo del período. Se dimensiona con trazas estables, incidentes y escenarios de ruido, además de un límite de ráfagas.

El laboratorio usa planes periódicos para que las cuentas se reproduzcan exactamente. En un producto se pueden combinar: resumen para la historia, eventos de regla para lo urgente y lecturas crudas temporales cuando se necesita diagnosticar.

05 · El receptor debe poder interpretarlo

El contrato de telemetría

Antes de optimizar bytes, fijá qué significa cada campo. En estos ejemplos, el dispositivo se identifica por su canal autenticado o por el tópico, no repitiendo su nombre en cada carga. El servidor verifica esa asociación: conocer un tópico no demuestra por sí solo quién lo publicó.

{
  "v": 1,
  "boot": 42,
  "seq": 1000,
  "t_ms": 1767225600000,
  "dt_ms": 1000,
  "c": [41, 42, null],
  "q": [1, 1, 0]
}

v identifica el esquema. boot y seq, junto con el dispositivo, identifican esta entrega. t_ms es el instante Unix de la primera posición; dt_ms, la separación fija entre posiciones. c contiene décimas de °C y q la calidad. El ejemplo representa 4,1 °C, 4,2 °C y una adquisición fallida.

El instante de la posición i se obtiene como t_ms + i × dt_ms. Esto exige una grilla temporal conocida: si la adquisición tiene tiempos variables, hay huecos no representados o se corrigió el reloj, se necesitan marcas u offsets por muestra. No se compacta inventando que todos los tiempos fueron regulares.

Identidad del lote e identidad de sus observaciones

Si seq identifica un lote, cada posición se distingue con su índice dentro de ese lote. El reintento conserva el mismo lote y su contenido. Si después se reagrupan las mismas observaciones en otros lotes, hace falta conservar también su identidad original o el receptor puede contarlas dos veces. No se mezcla sin documentarlo una secuencia por muestra con otra por mensaje.

La secuencia avanza para cada entrega nueva dentro del alcance definido por el contrato. El planificador repite seq=1000 solo como plantilla para medir tamaños: esos valores repetidos no son identidades listas para transmitir mediciones, activaciones y recuperaciones distintas.

Un resumen cambia el contrato. En lugar de arreglos, lleva inicio y duración de ventana, total, cantidad válida, suma y extremos. La media se calcula con la cantidad válida, como en el tema anterior; una ventana totalmente inválida no tiene una media de cero.

{
  "v": 1, "boot": 42, "seq": 1000,
  "from_ms": 1767225600000, "window_ms": 60000,
  "n": 60, "valid": 60,
  "sum_dc": 2460, "min_dc": 41, "max_dc": 41
}

El tipo de mensaje debe quedar inequívoco por canal, esquema o campo de tipo. El ejemplo reserva canales distintos para mediciones, eventos y diagnóstico. También fija la unidad fuera de toda duda: 41 significa 4,1 °C, no 41 °C ni una cuenta de ADC.

Versionar sin romper lo que ya está instalado

Documentá campos obligatorios, opcionales, límites de arreglos y políticas para versiones desconocidas. Agregar una magnitud no permite reinterpretar una columna vieja con otra unidad. El receptor puede aceptar versiones compatibles en paralelo mientras se actualiza una flota, y debe distinguir una carga rechazada de una entrega pendiente de reintento.

06 · Contar bytes de verdad

JSON y binario: medir tamaños

JSON facilita inspeccionar los mensajes y evolucionar campos. Su tamaño depende de nombres, espacios, números y codificación. Para intercambios abiertos se utiliza UTF-8, según RFC 8259. Por eso se cuentan bytes codificados y no solo caracteres visibles.

import json

carga = {"v": 1, "c": 41, "q": 1}
texto = json.dumps(carga, separators=(",", ":"), ensure_ascii=False, allow_nan=False)
print(texto)
print(len(texto.encode("utf-8")), "bytes")

Quitar espacios reduce tamaño sin perder campos. En cambio, cambiar todos los nombres por letras sin un esquema versionado puede ahorrar poco y volver difíciles el diagnóstico y las migraciones. No se incluyen NaN ni infinitos como números JSON: una medición inválida se expresa con calidad y un valor nulo.

Un esquema binario ilustrativo

Para comparar, usamos una cabecera explícita de 26 bytes y posiciones de tres bytes. Es un formato didáctico definido para el planificador, diferente del registro local de 24 bytes del tema 33:

Formato binario del cálculo, orden little-endian y sin relleno implícito.
ParteCamposTamaño
CabeceraVersión (1), tipo (1), arranque (4), secuencia (4), tiempo Unix ms (8), período ms (4), cantidad (2), indicadores (2)26 bytes
Una posición crudaTemperatura con signo (2) y calidad (1)3 bytes
Cuerpo de resumenVálidas (2), suma con signo (4), mínimo con signo (2), máximo con signo (2)10 bytes

Un lote de 60 posiciones ocupa 26 + 60 × 3 = 206 bytes. Un resumen ocupa 36 bytes. La temperatura usa enteros con signo; en una posición inválida, calidad cero hace que los dos bytes de valor se ignoren. En un resumen con cero válidas, los extremos se consideran ausentes aunque ocupen espacio reservado.

La cantidad de 16 bits limita el lote a 65.535 posiciones, y la suma de 32 bits tiene un rango que hay que verificar con la magnitud y ventana admitidas. Una cabecera compacta no exime de validar longitudes, versión y campos antes de reservar memoria. La documentación de struct de Python explica cómo fijar tamaños y orden de bytes con un prefijo explícito.

Qué incluye la comparación

El planificador codifica cargas representativas con temperatura 4,1 °C, calidad válida, secuencia 1000 y una fecha de longitud fija. Multiplica su tamaño por la cantidad de envíos. En operación, la longitud del JSON puede cambiar con valores, contadores y cantidad de inválidas. El tamaño binario es fijo para ese número de posiciones.

Diagnóstico y eventos se mantienen en JSON en ambas opciones para aislar el cambio de formato de las mediciones. Los bytes adicionales de transporte se estiman aparte. El formato binario de red no incluye un CRC propio en esta cuenta; si el producto lo necesita, se agrega a su esquema y a su presupuesto. Tampoco se está comparando el costo de persistir esos mensajes en flash.

07 · Saber si se puede confiar

Diagnóstico y frescura

El diagnóstico debería ayudar a explicar fallas concretas. «Todo bien» dice poco; una combinación acotada de valores actuales, contadores y versiones permite ver tendencias y distinguir causas.

Campos útiles, según el problema que se quiera detectar.
CampoInterpretaciónCuidado
Tiempo desde arranque y causa de reinicioDetectar reinicios repetidosNo confundirlo con una fecha UTC
Última adquisición válida y su contadorComprobar que hay muestras nuevasEnviar un valor viejo no renueva su fecha de medición
Cola pendiente y antigüedad del más viejoMedir deuda de entregaUna cola corta puede contener un evento muy antiguo
Descartes y errores acumuladosSaber que se perdió informaciónIndicar reinicios y alcance del contador
Memoria libre y mínimos observadosVer presión de memoriaDefinir qué memoria mide la API del hardware
RSSI y reconexionesDiagnosticar conectividadRSSI no mide por sí solo latencia ni éxito de entrega
Firmware, esquema y configuración aplicadaExplicar diferencias entre equiposDistinguir configuración solicitada de aplicada

Valores actuales y contadores acumulados

La memoria libre o el largo de cola son valores de un instante. Un contador de descartes indica cuántos ocurrieron durante un período de vida conocido. Si el equipo informa 12 y después 15 descartes, hubo tres nuevos; si se reinició y ahora informa 1, restar sin mirar el arranque daría un resultado absurdo.

Publicar contadores acumulados permite recuperar diferencias aunque se pierda un reporte intermedio, siempre que se conozcan reinicios, alcance y posibles desbordamientos. No convierte los datos en una auditoría perfecta de pérdidas ocurridas antes de un corte. El modelo de métricas de OpenTelemetry distingue valores puntuales, sumas y temporalidades; aquí tomamos esa distinción conceptual sin exigir su protocolo en el microcontrolador.

Fresco, atrasado y desconocido

La nube debería conservar por separado última recepción de diagnóstico, última medición válida y atraso de la cola. Son tres edades diferentes. Un dispositivo puede estar conectado, el sensor sin datos y una cola antigua todavía reenviándose.

Si el latido nominal es de 300 s, un umbral de silencio podría ser dos períodos más un margen, por ejemplo 610 s desde el último latido recibido. Ese número es una política del ejemplo: debe permitir la variación esperable y cumplir el tiempo de detección requerido. No demuestra el instante exacto en que falló el equipo.

Cuando se vacía una cola después de un corte, recibir un latido histórico no debería declarar que el equipo está sano ahora. El protocolo distingue diagnóstico actual de historia pendiente y mantiene las fechas originales. El estado de conexión del transporte tampoco demuestra que la tarea del sensor esté funcionando.

08 · Datos, almacenamiento y energía

Presupuestar datos y escala

Primero separá cantidad de mediciones, cantidad de mensajes y cantidad de registros en la nube. Para un día de 86.400 s y períodos constantes que lo dividen exactamente:

Muestras/día = 86.400 / período de muestreo
Reportes/día = 86.400 / período de reporte
Bytes/día estimados = Σ envíos de cada flujo × (carga representativa + bytes adicionales)
Bytes/30 días = bytes/día × 30
Tráfico de flota = tráfico por dispositivo × cantidad de dispositivos

En el modo individual, cada muestra es un reporte. En lotes y resúmenes, el reporte se rige por su ventana. Los latidos y eventos se cuentan aparte. Para el ejemplo base hay 1440 resúmenes, 288 diagnósticos y dos eventos al día: 1730 mensajes en total.

El modelo añade 100 bytes por envío como estimación configurable de lo que no está en la carga. No es el tamaño universal de MQTT, HTTP o TLS. En una medición real hay que considerar encabezados, confirmaciones, segmentación, reintentos, conexiones, tráfico de mantenimiento y, si aplica, reglas de contabilización del enlace.

Estimación por dispositivo en 30 días: muestreo de 1 s, ventana de 60 s, diagnóstico cada 300 s, dos eventos y 100 B adicionales por envío.
PlanCarga de mediciónEstimación totalRegistros de medición/día en la nube
Individual JSON79 B444,16 MiB86.400
Lote JSON de 60374 B21,21 MiB86.400 si se expande
Lote binario de 60206 B14,29 MiB86.400 si se expande
Resumen JSON126 B10,99 MiB1440 resúmenes
Resumen binario36 B7,29 MiB1440 resúmenes

Un MiB equivale a 1.048.576 bytes; un MB decimal, a 1.000.000. El laboratorio usa MiB y un período de 30 días fijo. La cuota es un límite de ejercicio, no una oferta comercial ni una simulación de facturación de un proveedor.

El tráfico de diagnóstico puede dominar

Con resúmenes JSON cada cinco minutos, el flujo de mediciones baja a 288 mensajes diarios. El diagnóstico también tiene 288: ya no es una fracción despreciable. Optimizar solo la temperatura sin mirar los otros flujos puede dejar casi intacto el costo total.

Bytes no son energía

Reducir bytes suele ayudar, pero la energía depende también de cuánto permanece encendida la radio, conexiones, esperas y retransmisiones. Dos planes con el mismo volumen mensual pueden tener consumos muy distintos si uno abre miles de conexiones y el otro agrupa. Para estimar autonomía se vuelve al modelo medido de los temas 30 y 31; este laboratorio no calcula batería.

El presupuesto de almacenamiento tampoco se deduce directamente del tráfico: un lote puede ahorrar encabezados y seguir generando la misma cantidad de filas. Los factores de índices, retención y réplicas del tema 34 se aplican sobre los datos que finalmente se conservan.

09 · Que lo urgente no espere detrás de lo antiguo

Prioridades y comportamiento sin red

Preparar un evento en el momento correcto no alcanza si queda detrás de diez mil lecturas históricas. El plan debe definir prioridad de salida, capacidad de cada cola y cuánto trabajo se permite por iteración sin bloquear adquisición ni control.

  • Eventos recientes: pueden tener una cuota reservada y prioridad, con identidad y reintentos.
  • Diagnóstico actual: puede actualizarse o combinarse para no reenviar cientos de estados obsoletos, si el producto no necesita ese historial.
  • Historia pendiente: se vacía con una tasa acotada, conservando fechas e identidades originales.

La política depende del flujo: combinar dos latidos antiguos puede ser aceptable, pero eliminar una activación o recuperación de alarma cambia la historia del incidente. El tema 33 definió capacidad y descarte; ahora esas decisiones se asignan explícitamente a cada clase de información.

Evitar ráfagas de toda la flota

Si mil equipos reportan exactamente al mismo segundo o vacían su cola sin límite al reconectar, el servidor puede recibir una carga mucho mayor que el promedio. Distribuí el comienzo de los reportes o aplicá una variación acotada, y limitá el drenaje por equipo.

La variación de envío no obliga a mover las ventanas de medición. Se pueden mantener resúmenes de minutos alineados y transmitirlos con unos segundos de diferencia. Ese desplazamiento también consume parte del presupuesto de demora y debe incluirse al verificar los requisitos.

Reducir tráfico sin cambiar silenciosamente la señal

Ante una cola llena, el firmware puede activar un modo de menor detalle previamente definido. La configuración y los mensajes deben indicar ese modo, sus períodos y lo descartado. Si el servidor sigue esperando una muestra por segundo mientras el equipo pasó a una por minuto, podría interpretar la reducción como una falla.

Los reintentos conservan identidad y cuentan para el tráfico. En el planificador no hay pérdidas de red y el envío se considera inmediato una vez preparado; sus resultados son un punto de partida. La validación final necesita capturas o contadores del transporte y trazas de los cortes esperados.

10 · Un presupuesto reproducible

Un planificador en Python

El programa construye cargas representativas JSON y binarias, calcula los tres flujos y evalúa el incidente [123, 128). Para la alarma recorre las muestras y confirma entrada y salida como en el tema 32. No requiere placa, servicios ni paquetes adicionales.

Python en el navegador. Podés editar y ejecutar el bloque. La primera ejecución descarga Pyodide. También funciona como un archivo Python 3 local. Los ejemplos se calculan para un día y se extrapolan a 30 días iguales.

import json
import struct

DIA = 86400
T0 = 1767225600000
CAB = struct.Struct("<BBIIQIHH")  # 26 bytes
MUESTRA = struct.Struct("<hB")    # 3 bytes
RESUMEN = struct.Struct("<Hihh")  # 10 bytes


def bytes_json(objeto):
    return len(json.dumps(objeto, separators=(",", ":"), ensure_ascii=False,
                          allow_nan=False).encode("utf-8"))


def evento_local(muestreo):
    activa, desde = False, None
    cambios = []
    for t in range(0, 300, muestreo):
        c = 92 if 123 <= t < 128 else 41
        cumple = c <= 75 if activa else c >= 80
        if not cumple:
            desde = None
            continue
        if desde is None:
            desde = t
        espera = 2 if activa else 3
        if t - desde >= espera:
            activa = not activa
            desde = None
            cambios.append((t, "alarm_on" if activa else "alarm_off", c))
    return cambios


def planificar(estrategia="resumen", formato="json", muestreo=1, reporte=60,
               latido=300, extra=100, flota=100, cuota=20, alertas=True):
    if estrategia not in ("directo", "lote", "resumen") or formato not in ("json", "binario"):
        raise ValueError("Estrategia o formato desconocido")
    if muestreo not in (1, 10, 60) or reporte not in (60, 300, 900):
        raise ValueError("Períodos fuera del modelo")
    if latido not in (60, 300, 900) or extra < 0 or flota < 1 or cuota <= 0:
        raise ValueError("Presupuesto inválido")
    n = 1 if estrategia == "directo" else reporte // muestreo
    periodo = muestreo if estrategia == "directo" else reporte
    cabecera = dict(v=1, boot=42, seq=1000)
    if formato == "binario":
        tipo = 2 if estrategia == "resumen" else 1
        cab = CAB.pack(1, tipo, 42, 1000, T0, muestreo * 1000, n, 1)
        cuerpo = (RESUMEN.pack(n, 41 * n, 41, 41) if tipo == 2
                  else MUESTRA.pack(41, 1) * n)
        carga = len(cab + cuerpo)
    elif estrategia == "resumen":
        carga = bytes_json(dict(**cabecera, from_ms=T0, window_ms=reporte * 1000,
                                n=n, valid=n, sum_dc=41 * n, min_dc=41, max_dc=41))
    else:
        carga = bytes_json(dict(**cabecera, t_ms=T0, dt_ms=muestreo * 1000,
                                c=[41] * n, q=[1] * n))
    diagnostico = bytes_json(dict(**cabecera, uptime_s=3600, rssi_dbm=-67,
                                 heap_free=180000, queue=0, drop_total=0))
    cambios = evento_local(muestreo) if alertas else []
    bytes_eventos = sum(bytes_json(dict(**cabecera, t_ms=T0 + t * 1000,
                                       event=nombre, c=c, cfg_v=3)) + extra
                        for t, nombre, c in cambios)
    medidas_dia = DIA // muestreo
    mensajes_dia = DIA // periodo
    diagnosticos_dia = DIA // latido
    bytes_medidas = mensajes_dia * (carga + extra)
    bytes_diagnostico = diagnosticos_dia * (diagnostico + extra)
    bytes_dia = bytes_medidas + bytes_diagnostico + bytes_eventos
    bytes_mes = bytes_dia * 30

    primera = next((t for t in range(0, 300, muestreo) if 123 <= t < 128), None)
    demora_regular = None
    if primera is not None:
        envio = primera if estrategia == "directo" else (primera // reporte + 1) * reporte
        demora_regular = envio - 123
    demora_alerta = cambios[0][0] - 123 if cambios else None
    demoras = [d for d in (demora_regular, demora_alerta) if d is not None]
    demora = min(demoras) if demoras else None
    excede_carga = carga > 1024
    excede_cuota = bytes_mes > cuota * 1024**2
    return dict(n=n, periodo=periodo, medidasDia=medidas_dia, mensajesDia=mensajes_dia,
                carga=carga, diagnostico=diagnostico, diagnosticosDia=diagnosticos_dia,
                eventosDia=len(cambios), bytesMedidas=bytes_medidas,
                bytesDiagnostico=bytes_diagnostico, bytesEventos=bytes_eventos,
                bytesDia=bytes_dia, bytesMes=bytes_mes, bytesFlota=bytes_mes * flota,
                mensajesTotales=mensajes_dia + diagnosticos_dia + len(cambios),
                registrosMedidas=mensajes_dia if estrategia == "resumen" else medidas_dia,
                demoraRegular=demora_regular, demoraAlerta=demora_alerta, demora=demora,
                excedeCarga=excede_carga, excedeCuota=excede_cuota,
                cumple=not excede_carga and not excede_cuota and demora is not None and demora <= 5)


print("Plan                 carga B  mensajes/día  MiB/30 días  demora")
for nombre, cfg in [("Individual JSON", dict(estrategia="directo")),
                    ("Lote JSON", dict(estrategia="lote")),
                    ("Lote binario", dict(estrategia="lote", formato="binario")),
                    ("Resumen JSON", {}),
                    ("Resumen binario", dict(formato="binario"))]:
    r = planificar(**cfg)
    print(f'{nombre:20} {r["carga"]:7} {r["mensajesTotales"]:13}'
          f' {r["bytesMes"]/1024**2:12.3f} {r["demora"]} s')

base = planificar()
assert CAB.size == 26 and MUESTRA.size == 3 and RESUMEN.size == 10
assert (base["carga"], base["mensajesTotales"], base["bytesDia"]) == (126, 1730, 384275)
assert (base["demoraRegular"], base["demoraAlerta"], base["demora"]) == (57, 3, 3)
assert evento_local(1) == [(126, "alarm_on", 92), (130, "alarm_off", 41)]
assert planificar(estrategia="directo")["demora"] == 0
assert planificar(estrategia="lote", formato="binario")["carga"] == 206
assert planificar(estrategia="lote", reporte=900)["excedeCarga"]
assert planificar(muestreo=10)["demora"] is None
assert planificar(alertas=False)["demora"] == 57
assert planificar(estrategia="lote")["excedeCuota"]
print("\n10 comprobaciones superadas.")
print("Base: 10,994 MiB por dispositivo en 30 días; evento observado a los 3 s.")

Resultado inicial: resumen JSON de 126 bytes, 1730 mensajes diarios y 384.275 bytes estimados por día; 10,994 MiB por dispositivo en 30 días. El reporte regular muestra el incidente a los 57 s; el evento confirmado, a los 3 s. Las diez comprobaciones terminan sin errores.

Interpretar el resultado

Las cargas de medición usan un caso representativo estable; el incidente se simula por separado para comprobar observabilidad y calcular sus dos eventos JSON. No se recalcula cada carga periódica de un día con la señal del incidente. Por eso el tráfico es una estimación de planificación, no una captura exacta de una jornada.

Se supone un incidente idéntico al día, entrega inmediata al preparar y red sin pérdidas. Las mediciones comienzan en t = 0; cada ventana contiene muestras de [inicio, fin) y se envía al cerrar. No se incluye un latido adicional de arranque en el conteo diario. En un firmware real se programan adquisiciones y envíos sin bloquear y se usan sus tiempos efectivamente observados.

El formato binario se construye para medir su longitud, pero no se implementa aquí un receptor de producción. Hacen falta validación, pruebas de compatibilidad y un contrato de calidad y reloj completo. El objetivo del código es comparar planes con supuestos visibles antes de invertir en una implementación.

11 · Exploración interactiva

Costo y utilidad de un plan

El objetivo es observar el incidente en cinco segundos o menos, usar cargas de medición de hasta 1024 B y respetar la cuota elegida. Los tres requisitos deben cumplirse. La comparación no exige conservar todas las muestras: si tu proyecto lo requiere, elegí individuales o lotes y verificá también ese requisito.

Plan de telemetría de una cámara

Adquisiciones por día—
Envíos totales por día—
Carga representativa de medición—
Estimación por dispositivo / 30 días—
Estimación de la flota / 30 días—
Registros de medición en la nube / día—
Primera evidencia del incidente—
Resultado del modelo—
Por reporte de medición—
Por evento local—

Preparando plan.

Se incluyen cargas representativas y bytes adicionales por envío. Diagnóstico y eventos siguen en JSON en ambos formatos.
FlujoEnvíos/díaBytes/día estimadosEstimación / 30 días

Recorrido guiado

  1. Calculá con los valores iniciales. Hay 1730 mensajes diarios y unos 10,99 MiB por dispositivo en 30 días. Para cien equipos, aproximadamente 1,07 GiB. El incidente se observa a los 3 s por el evento local.
  2. Desmarcá los eventos locales. El volumen cambia poco, pero la evidencia aparece a los 57 s: el plan deja de cumplir el objetivo de demora.
  3. Restaurá los eventos y elegí Una por envío. La evidencia periódica llega a los 0 s del modelo, pero el tráfico sube a unos 444,16 MiB. La cuota de 20 MiB no alcanza.
  4. Elegí Lote JSON con reporte de 60 s. Conserva todas las lecturas en 374 B por carga; estima 21,21 MiB. Cambiá solo a binario: 206 B y 14,29 MiB. En ambos casos hay 86.400 registros de medición diarios si el servidor expande los lotes.
  5. Volvé al resumen JSON y subí el muestreo a 10 s. Se reducen adquisiciones, pero el evento entre 123 y 128 s desaparece. La alerta local tampoco lo detecta.
  6. Restaurá muestreo de 1 s y reportá el resumen cada 5 minutos. La estimación baja a unos 3,58 MiB y hay 578 envíos diarios. El evento conserva su demora de 3 s; el reporte periódico pasa a 177 s.
  7. Con muestreo de 1 s, elegí lote JSON cada 15 minutos. La carga representativa ocupa 4574 B y supera el límite del ejercicio. Que el volumen mensual parezca aceptable no vuelve válido ese mensaje.
  8. Variá la frecuencia de diagnóstico y los bytes adicionales por envío. Observá en la tabla cuánto del tráfico viene de cada flujo; luego cambiá la flota para ver cómo se multiplica el total.

«Cumple» solo se refiere a los límites del laboratorio: cuota estimada, tamaño representativo y demora del incidente simulado. No certifica autonomía, precisión del sensor, durabilidad de una cola ni disponibilidad de la red. Cada plan conserva además una cantidad distinta de detalle.

Desafío: ¿por qué el resumen casi no se achica al medir cada diez segundos?

Porque su carga lleva una cantidad fija de campos: suma, cantidad y extremos ocupan casi lo mismo con seis que con sesenta muestras. Se ahorra trabajo de adquisición, pero el número de reportes no cambia. En este caso se pierde el incidente breve sin obtener una reducción importante del tráfico.

12 · Convertir requisitos en un plan

Método y confusiones frecuentes

  1. Definí preguntas y eventos: qué decisión necesita la nube y cuál debe seguir siendo local.
  2. Fijá el cambio más breve que importa: elegí muestreo y confirmación compatibles con detectarlo.
  3. Separá los flujos: mediciones, eventos, diagnóstico y depuración tienen necesidades distintas.
  4. Especificá el contrato: identidad, tiempos, unidad, calidad, versión y límites de tamaño.
  5. Elegí el detalle: individuales, lotes, resumen o banda muerta según la información que se debe conservar.
  6. Calculá y medí: tamaños de cargas, tráfico completo, almacenamiento y energía con el patrón real.
  7. Diseñá la degradación: prioridades, reintentos, capacidad y políticas de descarte durante cortes.
  8. Probá con trazas: señal estable, evento corto, ruido, sensor inválido, reinicio y recuperación de una cola.
  9. Versioná y observá el plan: el servidor debe conocer qué configuración está aplicada y qué información dejó de producirse.

Una ficha mínima antes de programar

Para cada flujo, anotá campos y unidades, disparador, período o condición, máximo tamaño, prioridad, identidad, tiempo de validez, retención local y remota, y respuesta si no se puede entregar. Esa ficha permite revisar el diseño entre firmware, servidor y panel antes de que cada parte haga supuestos distintos.

«Reportar cada minuto significa medir cada minuto»

Son períodos independientes. Se puede adquirir una vez por segundo y reportar lotes o resúmenes por minuto. Reducir adquisición puede ocultar eventos; reducir reportes agrega demora y exige memoria intermedia.

«Un lote y un resumen ahorran lo mismo»

El lote conserva observaciones individuales y puede ocupar más. El resumen descarta la secuencia y ocupa poco incluso con muchas muestras. Ambos reducen mensajes, pero tienen costos de información y almacenamiento diferentes.

«El latido demuestra que la temperatura sigue actualizada»

Demuestra que se recibió ese diagnóstico. Para saber si el sensor avanzó, se mira la fecha de adquisición válida y su contador. Un firmware puede seguir comunicándose mientras el sensor devuelve errores.

«Menos bytes garantizan más batería»

También importan conexiones, radio encendida, esperas y reintentos. La reducción de volumen se verifica junto con el patrón energético del hardware, no como sustituto de esa medición.

«Si no hubo evento, el proceso estuvo normal»

Puede no haber ocurrido, no haberse muestreado, no haberse confirmado o no haberse entregado. Calidad, antigüedad y contadores permiten distinguir algunas de esas situaciones. La ausencia de un mensaje no es una prueba física de normalidad.

«El campo de temperatura alcanza para interpretar la lectura»

Sin unidad, tiempo y calidad puede interpretarse mal. En un lote también hay que saber cómo reconstruir tiempos e identificar cada posición; en un resumen, qué ventana y cuántas observaciones representa.

«Si la cuota mensual alcanza, cualquier tamaño de lote sirve»

El mensaje puede superar un límite de aplicación, del cliente o del enlace, o necesitar demasiado buffer. Dividirlo cambia la cantidad de envíos y su costo. Los límites por carga y por período se verifican por separado.

13 · Práctica guiada

Predecí antes de comprobar

1. Cuatro cantidades diferentes

Un dispositivo mide cada 2 s, arma lotes de un minuto y publica diagnóstico cada 5 min. Sin eventos ni reintentos, ¿cuántas muestras, lotes, diagnósticos y mensajes totales produce al día?

Ver solución

Hay 43.200 muestras, 1440 lotes de 30 posiciones y 288 diagnósticos. Total: 1728 mensajes. Si la nube expande cada lote, guarda 43.200 observaciones de medición.

2. Un pico entre muestras

El sensor se adquiere en t = 120 y 130 s. La temperatura sube solo entre 123 y 128 s. ¿Qué mejora permite detectarlo: un JSON más corto, una regla local o un muestreo más frecuente?

Ver solución

Hay que revisar el muestreo. Ni el formato ni la regla pueden detectar valores que no se observaron. En esta traza, medir cada segundo observa el pico; después se decide cómo confirmarlo y comunicarlo.

3. Confirmar y reportar

Con muestras cada segundo y confirmación de entrada de 3 s, el primer dato alto aparece en t = 123 s. El resumen se envía a los 180 s. ¿Cuándo puede salir el evento local y cuánto demora cada camino desde el comienzo del pico?

Ver solución

La regla confirma en t = 126 s, después de observar 123, 124, 125 y 126: demora de 3 s. El resumen sale a los 180: demora de 57 s. Se excluyen las demoras de transporte y cola.

4. Tamaño de un lote binario

Con cabecera de 26 B y tres bytes por posición, ¿cuánto ocupan 60 muestras? Con límite de carga de 1024 B, ¿cuántas posiciones caben como máximo en ese formato?

Ver solución

26 + 60 × 3 = 206 B. El máximo es floor((1024 − 26) / 3) = 332 posiciones, que ocupan 1022 B. La número 333 llevaría el total a 1025 B y ya no cabe.

5. Costo de diagnóstico

Un diagnóstico ocupa 103 B de carga y se estiman 100 B adicionales por envío. Si se envía cada cinco minutos, ¿cuántos bytes diarios suma al plan?

Ver solución

86.400 / 300 = 288 envíos. Cada uno se estima en 203 B: 58.464 bytes diarios. En treinta días son 1.753.920 bytes, aproximadamente 1,67 MiB.

6. Contadores después de reiniciar

Un equipo informa boot=42, drop_total=15 y después boot=43, drop_total=2. ¿Se concluye que hubo −13 descartes?

Ver solución

No. Cambió el arranque y el contador tiene otro origen. Hay dos descartes registrados en el nuevo arranque; para un total entre arranques se necesitan una política persistente o el historial de contadores con sus límites conocidos.

7. Red y almacenamiento

Pasás de una lectura por envío a lotes de 60, conservando todas las observaciones. ¿La cantidad de filas de medición en la nube se divide necesariamente por 60?

Ver solución

No. Se reduce la cantidad de mensajes. Si el servidor expande el lote, conserva la misma cantidad de observaciones. Guardar resúmenes en lugar de muestras sí cambia la cantidad y el detalle de los registros.

8. Plan de una flota

Cada dispositivo usa una estimación de 12 MiB en treinta días y hay 250 equipos. ¿Cuál es el volumen de flota en MiB y GiB? ¿Eso expresa el consumo de batería?

Ver solución

12 × 250 = 3000 MiB, aproximadamente 2,93 GiB. Es volumen estimado de datos, no energía. Para batería se necesita un modelo del hardware y del patrón de adquisición y comunicación.

14 · Síntesis

Ideas para recordar

  • La telemetría debe explicar el proceso y el funcionamiento del dispositivo. Mediciones, eventos y diagnóstico se diseñan como flujos complementarios.
  • Muestrear, reportar y enviar un latido son tareas con frecuencias distintas. Un evento no observado no se recupera mejorando el transporte.
  • Los lotes conservan muestras; los resúmenes conservan estadísticas. Reducir mensajes no implica reducir filas históricas.
  • Un contrato fija identidad, versión, tiempo, unidad, calidad y límites. Un formato compacto sigue necesitando validación y documentación.
  • El tráfico incluye mediciones, diagnóstico, eventos y costos adicionales. Se estima con cargas representativas y se verifica con mediciones reales.
  • Frescura del dato, llegada de diagnóstico y atraso de la cola son edades distintas. Un latido no renueva una lectura vieja.
  • En el plan base se estiman 10,99 MiB por dispositivo en treinta días y un aviso a los 3 s. Sin el evento local, el resumen avisa a los 57 s.
  • El plan también define prioridades, capacidad, descarte y recuperación de cortes. Bytes, energía y almacenamiento son presupuestos relacionados que se comprueban por separado.

¿Listo para el tema 36?

Antes de avanzar, escribí una ficha de telemetría para tu proyecto y calculá un día normal y otro con incidentes y desconexión. Compará lo que la nube puede saber con lo que el proceso necesita, no solo cuántos mensajes ahorraste.

En el próximo tema, Paneles y visualización, usamos esta información para construir gráficas, alertas y vistas de estado que distingan datos frescos, históricos, inválidos y ausentes sin confundir a quien opera el sistema.