La monitorización de la cadena de frío consiste en demostrar que los alimentos refrigerados, las vacunas o los productos biológicos nunca salieron de su rango de temperatura entre el almacén y el cliente, y en reaccionar rápido cuando sí lo hacen. En este artículo diseñarás un sistema de monitorización para camiones y contenedores frigoríficos, igual de útil en una ruta de Querétaro a Monterrey que en una de Valencia a Bilbao, que envía los datos de temperatura por MQTT celular, sobrevive a las zonas sin cobertura sin perder lecturas y genera alertas de desviación en función de cuánto tiempo se superó un límite, no de cada pico aislado.
Qué tiene que demostrar la monitorización de la cadena de frío
El registro de temperatura en el transporte tiene dos públicos. El personal de operaciones necesita información en vivo para que un conductor pueda cerrar una puerta entreabierta o atender un equipo frigorífico que falla antes de que se pierda la carga. El personal de calidad necesita un registro completo tras el viaje, a menudo para satisfacer a los clientes y los requisitos de buenas prácticas de distribución, que muestre cada lectura con su hora y su ubicación.
Esas dos necesidades determinan todo el diseño:
| Requisito | Por qué importa | Consecuencia de diseño |
|---|---|---|
| Visibilidad en vivo | Intervenir antes de que el producto se dañe | Conectividad celular, telemetría MQTT cada pocos minutos |
| Sin huecos en el registro | Una hora perdida puede invalidar un envío | Búfer de almacenamiento y reenvío en el dispositivo |
| Marcas de tiempo fiables | El orden y la duración de las desviaciones deben poder demostrarse | Marca de tiempo en el momento de la medición, no de la llegada |
| Contexto | ¿Estaba la puerta abierta? ¿Dónde estaba el camión? | Sensores de puerta y GPS junto a la temperatura |
| Alertas con sentido | Los ciclos de desescarche y las aperturas de puerta provocan picos breves | Reglas de desviación basadas en la duración |
Hardware en el vehículo
Una instalación típica tiene tres partes:
- Sondas de temperatura en la zona de carga, normalmente dos o más: una cerca del evaporador y otra al fondo, junto a las puertas, por donde entra el aire caliente. Los registradores BLE inalámbricos son habituales para el seguimiento por palé; las sondas cableadas encajan en instalaciones frigoríficas fijas.
- Un gateway telemático con módem LTE-M, NB-IoT o LTE y GPS. Puede ser una unidad comercial o una pequeña placa Linux con un HAT celular. Lee las sondas, almacena los datos en búfer y publica por MQTT.
- Sensores de contacto de puerta para que cada subida de temperatura pueda correlacionarse con una apertura.
El gateway es el único dispositivo que habla con el broker. Incluso un remolque largo lleno de registradores BLE por palé se convierte en una sola conexión MQTT.
Diseño de topics para la monitorización de la cadena de frío
Organiza por activo de la flota y no por viaje; los viajes son metadatos dentro del payload:
coldchain/<fleet>/<asset-id>/telemetry {"ts":1760600000,"t_front":3.8,"t_rear":4.6,"door":"closed","lat":41.01,"lon":28.97,"trip":"TR-8812"}
coldchain/<fleet>/<asset-id>/status retained {"online":true,"fw":"1.4.2","battery_v":12.6}
coldchain/<fleet>/<asset-id>/alarm {"type":"excursion","probe":"t_rear","max":9.4,"duration_s":1260,"trip":"TR-8812"}
coldchain/<fleet>/<asset-id>/config retained {"min":2.0,"max":8.0,"grace_s":900}
Retener el topic config es un truco muy práctico. El gateway se suscribe a él al conectarse, de modo que cambiar una carga de refrigerada a congelada es una única publicación retenida desde el sistema de expedición, y un gateway que se reinicia recupera sus límites de inmediato.
Código del gateway con almacenamiento y reenvío
La cobertura celular en autopistas, túneles y puertos es irregular. El gateway debe seguir muestreando mientras está sin conexión y enviarlo todo cuando vuelva el enlace. El esbozo en Python que sigue usa paho-mqtt 2.x, MQTT 5 y una cola SQLite en el gateway. Apunta al servidor MQTT público gratuito para que puedas probarlo en cualquier ordenador; sustituye read_probes() por el código real de tus sensores.
import json
import random
import sqlite3
import time
import paho.mqtt.client as mqtt
PREFIX = "connectmq/demo/f2q7b5" # tu propio prefijo aleatorio
ASSET = "TRK-104"
BASE = f"{PREFIX}/coldchain/fleet1/{ASSET}"
SAMPLE_EVERY = 60 # segundos
db = sqlite3.connect("buffer.db")
db.execute("CREATE TABLE IF NOT EXISTS q (id INTEGER PRIMARY KEY, payload TEXT)")
connected = False
limits = {"min": 2.0, "max": 8.0, "grace_s": 900}
def read_probes():
# sustituir por sensores reales; estos valores simulan una carga refrigerada
return {"t_front": round(random.uniform(3, 5), 1), "t_rear": round(random.uniform(3.5, 6), 1),
"door": "closed"}
def on_connect(client, userdata, flags, reason_code, properties):
global connected
connected = not reason_code.is_failure
if connected:
client.subscribe(f"{BASE}/config", qos=1)
client.publish(f"{BASE}/status", json.dumps({"online": True}), qos=1, retain=True)
def on_disconnect(client, userdata, flags, reason_code, properties):
global connected
connected = False
def on_message(client, userdata, msg):
limits.update(json.loads(msg.payload))
def flush(client, batch=50):
rows = db.execute("SELECT id, payload FROM q ORDER BY id LIMIT ?", (batch,)).fetchall()
for row_id, payload in rows:
info = client.publish(f"{BASE}/telemetry", payload, qos=1)
info.wait_for_publish(timeout=10)
if not info.is_published():
return # el enlace se cayó; conservar las filas restantes
db.execute("DELETE FROM q WHERE id = ?", (row_id,))
db.commit()
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=f"gw-{ASSET}-f2q7b5",
protocol=mqtt.MQTTv5)
client.will_set(f"{BASE}/status", json.dumps({"online": False}), qos=1, retain=True)
client.on_connect = on_connect
client.on_disconnect = on_disconnect
client.on_message = on_message
client.reconnect_delay_set(min_delay=2, max_delay=120)
client.connect_async("broker.connectmq.com", 1883, keepalive=120)
client.loop_start()
while True:
reading = {"ts": int(time.time()), "trip": "TR-8812", **read_probes()}
db.execute("INSERT INTO q (payload) VALUES (?)", (json.dumps(reading),))
db.commit()
if connected:
flush(client)
time.sleep(SAMPLE_EVERY)
Cada lectura entra primero en la cola y solo se borra cuando el broker la ha confirmado con QoS 1. Eso convierte el búfer en la fuente de verdad: los cortes de alimentación, los reinicios y las largas zonas sin cobertura no pueden crear huecos, y el campo ts registra cuándo se tomó la medida, no cuándo llegó. Para saber más sobre garantías de entrega, consulta los niveles de QoS de MQTT explicados.
Alertas de desviación que ignoran el ruido
Una regla ingenua del tipo "alertar si la temperatura supera 8 °C" salta cada vez que se abren las puertas en una parada de entrega o el evaporador hace un desescarche. Las alertas reales de cadena de frío tienen en cuenta la duración: solo se activa la alarma cuando una sonda lleva fuera de rango más tiempo que un periodo de gracia.
import json
import paho.mqtt.client as mqtt
PREFIX = "connectmq/demo/f2q7b5"
LIMITS = {"min": 2.0, "max": 8.0, "grace_s": 900}
state = {} # (asset, probe) -> primera marca de tiempo fuera de rango
def check(client, asset, probe, value, ts):
key = (asset, probe)
out = value < LIMITS["min"] or value > LIMITS["max"]
if not out:
state.pop(key, None)
return
start = state.setdefault(key, ts)
if start is not None and ts - start >= LIMITS["grace_s"]:
alarm = {"type": "excursion", "probe": probe, "value": value, "since": start,
"duration_s": ts - start}
client.publish(f"{PREFIX}/coldchain/fleet1/{asset}/alarm", json.dumps(alarm), qos=1)
state[key] = None # una alarma por desviación; se reinicia al volver al rango
def on_connect(client, userdata, flags, reason_code, properties):
client.subscribe(f"{PREFIX}/coldchain/fleet1/+/telemetry", qos=1)
def on_message(client, userdata, msg):
asset = msg.topic.split("/")[-2]
r = json.loads(msg.payload)
for probe in ("t_front", "t_rear"):
if probe in r:
check(client, asset, probe, r[probe], r["ts"])
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="excursion-f2q7b5")
client.on_connect = on_connect
client.on_message = on_message
client.connect("broker.connectmq.com", 1883, keepalive=60)
client.loop_forever()
Como usa la marca de tiempo de la medición, el detector gestiona correctamente los datos almacenados en búfer: una desviación de veinte minutos ocurrida en un túnel se sigue reconociendo como de veinte minutos cuando llega la acumulación pendiente. Para límites fijos sencillos sobre un valor en vivo, las reglas de alerta por umbral del portal son una forma rápida de empezar; la lógica basada en la duración como esta corresponde a tu propio suscriptor o a Node-RED.
Vigilar la flota
Suscríbete a todo mientras haces pruebas:
mosquitto_sub -h broker.connectmq.com -p 1883 -i ops-f2q7b5 -t "connectmq/demo/f2q7b5/coldchain/#" -v
El servidor MQTT público gratuito sirve para este prototipo, pero los datos de envío y las ubicaciones de los clientes no deben circular por un área legible públicamente. Los gateways de producción deben usar TLS en el puerto 8883 y credenciales generadas en el portal.
Consejos de conectividad celular
- Mantén los payloads pequeños. Claves cortas y muestras por minuto mantienen los planes de datos contenidos; agrupa varias lecturas por mensaje si tu plan cobra por mensaje.
- Configura un keep-alive generoso. Valores de dos a cinco minutos reducen los despertares de la radio sin retrasar demasiado la detección de desconexiones.
- Usa la expiración de sesión de MQTT 5 para que las suscripciones sobrevivan a cortes breves; consulta las características de MQTT 5.
- Espera progresivamente al reconectar. Los retrasos exponenciales evitan saturar la red tras un cambio de antena.
- Vigila el módem. Publica la intensidad de la señal y el tipo de red en
statuspara diagnosticar las zonas sin cobertura.
Errores habituales
Sonda solo junto al evaporador. Esa sonda muestra el aire más frío de la caja y puede ocultar una zona caliente junto a las puertas. Mide donde el producto corre más riesgo.
Marcas de tiempo en el servidor. Si sellas las lecturas al llegar, una acumulación pendiente se comprime en un solo minuto y las duraciones de las desviaciones pierden sentido. Sella siempre en el momento de la medición.
Avalanchas de alarmas tras reconectar. Un gateway que vacía una hora de datos puede disparar muchas alertas a la vez. Genera una alarma por desviación, como hace el detector anterior, y marca las alarmas tardías como "históricas" para que los responsables de expedición sepan que el evento ya terminó.
Credenciales compartidas en toda la flota. Un único inicio de sesión filtrado expone entonces todos los camiones. Da a cada gateway sus propias credenciales y su propio client ID.
Lista de comprobación para el despliegue
- Sondas colocadas en los puntos más cálidos y más fríos del espacio de carga, y calibradas.
- Sensores de puerta instalados y reportados en cada mensaje de telemetría.
- El gateway almacena en búfer en memoria no volátil y solo borra tras la confirmación.
- Marcas de tiempo de un reloj sincronizado (GPS o NTP).
- Límites entregados como configuración retenida por activo.
- Reglas de desviación basadas en la duración por categoría de producto.
- Un suscriptor de archivo duradero que escriba cada lectura en una base de datos para los informes de viaje.
- TLS y credenciales únicas por gateway.
Patrones relacionados
La monitorización de flotas tiene mucho en común con otros sistemas IoT distribuidos. La idea de un gateway por activo refleja los gateways de campo de una arquitectura IoT de ciudad inteligente, y agregar muchos activos en vistas de estado en vivo es el mismo problema que se resuelve en el aparcamiento inteligente IoT. Para construir un modelo duradero de estado retenido, nuestro proyecto de detección de fugas de agua muestra alarmas retenidas en un entorno más pequeño, y la guía de automatización IoT incluye más ejemplos.
Ejecuta la telemetría de tu cadena de frío en ConnectMQ
La telemetría de flotas necesita un broker accesible desde cualquier red celular, protegido con TLS y mantenido las 24 horas. ConnectMQ ofrece un broker MQTT gestionado con MQTT 5, TLS, credenciales generadas y un explorador de topics en vivo para ver cómo informa cada camión. Empieza con un broker Serverless gratuito en app.connectmq.com, sin tarjeta de crédito, y escala a un plan de pago o Dedicated a medida que crezca la flota. Para conocer el contexto del alojamiento, lee sobre el enfoque de broker MQTT en la nube.
Preguntas frecuentes
¿Con qué frecuencia debe informar de la temperatura un camión frigorífico?
Lo habitual es muestrear cada uno a cinco minutos. Muestrea localmente a ese ritmo incluso sin conexión y envía las lecturas almacenadas cuando vuelva la cobertura.
¿Qué ocurre si el camión no tiene señal?
El gateway guarda las lecturas con sus marcas de tiempo de medición en el almacenamiento local y las publica en orden al conectarse, borrando cada una solo después de que el broker la confirme.
¿Por qué alertar por duración en lugar de por una sola lectura alta?
Las aperturas de puerta y los ciclos de desescarche provocan picos breves que no dañan el producto. Un periodo de gracia los filtra sin dejar de detectar las desviaciones reales.
¿ConnectMQ almacena el historial de temperatura?
No. ConnectMQ entrega los mensajes en tiempo real. Ejecuta un suscriptor que escriba cada lectura en tu propia base de datos para los informes de viaje y las auditorías.



