IoT industrial

MQTT sobre red celular (LTE-M, NB-IoT): consejos de diseño para el consumo de datos, el keep alive y TLS

Los enlaces celulares cambian las reglas de MQTT. Aprende a estimar el consumo mensual de datos, elegir valores de keep alive que sobrevivan al NAT del operador, reducir el coste de los handshakes TLS y elegir entre LTE-M y NB-IoT.

Estación de bombeo remota que envía telemetría con MQTT sobre red celular a través de un módem LTE-M hacia un broker en la nube
Pruébalo en tu propio broker MQTTConsigue un broker MQTT privado y gestionado con el plan Free y empieza con ejemplos de código listos para usar.
Pruébalo gratis Ejemplos de código Sin tarjeta

MQTT sobre red celular es la forma en que la mayoría de los activos remotos (estaciones de bombeo, depósitos, plantas solares, vehículos, monitores ambientales) llegan a la nube, pero las reglas de diseño son distintas de las de Wi-Fi o Ethernet. Los bytes se facturan, los enlaces se caen más a menudo y las redes de los operadores cierran en silencio las conexiones inactivas. Esta guía te muestra cómo estimar un presupuesto de datos, elegir valores de keep alive, reducir la sobrecarga de TLS y decidir entre LTE-M y NB-IoT para que tus dispositivos sigan conectados sin facturas sorpresa.

Por qué la red celular cambia el diseño de MQTT

MQTT se diseñó originalmente para enlaces poco fiables y de bajo ancho de banda, así que ya encaja bien: una cabecera fija de 2 bytes, conexiones TCP persistentes y un modelo publicación-suscripción que evita el sondeo. Pero tres realidades de la red celular determinan los detalles:

  1. Se factura cada byte, incluidas las cabeceras TCP/IP, los registros TLS, las confirmaciones y los pings de keep alive, no solo tu payload.
  2. El NAT de nivel de operador (CGNAT) se sitúa entre la mayoría de los dispositivos e internet y elimina las asignaciones TCP inactivas tras un timeout que depende de cada operador. Si MQTT permanece en silencio más tiempo que eso, la conexión muere sin que ninguno de los dos extremos lo note de inmediato.
  3. El estado de la radio cuesta energía. Cada transmisión despierta el módem, que permanece un rato en un estado de alto consumo. Muchos envíos pequeños agotan las baterías más rápido que unos pocos agrupados.

LTE-M vs NB-IoT para MQTT

LTE-M (Cat-M1) NB-IoT (Cat-NB1/NB2)
Rendimiento Mayor, holgado para MQTT sobre TLS Muy bajo; los handshakes grandes tardan mucho
Latencia Comparable a LTE normal, lo bastante baja para comandos interactivos Alta y variable, a veces de segundos
Movilidad Admite traspaso entre celdas Diseñado para dispositivos fijos
Idoneidad para TCP/TLS Buena Posible, pero a menudo se combina con protocolos basados en UDP
Ahorro de energía Admite PSM y eDRX Admite PSM y eDRX, consumo en reposo muy bajo
Cobertura Buena, mejorada respecto a LTE normal Excelente en interiores profundos y ubicaciones subterráneas

Para MQTT sobre TCP y TLS, LTE-M suele ser la opción pragmática: los handshakes TLS se completan de forma fiable y los comandos llegan rápido al dispositivo. NB-IoT destaca en dispositivos fijos que envían informes pequeños y poco frecuentes desde lugares de difícil acceso; ahí, muchos equipos usan MQTT-SN u otro protocolo basado en UDP, o aceptan un establecimiento de conexión lento con MQTT sobre TCP. Comprueba qué tecnologías admite tu operador en la región de despliegue antes de elegir el hardware; algunos módems admiten ambas y cambian de una a otra automáticamente.

Estimar el presupuesto de datos de MQTT sobre red celular

El tamaño del payload suele ser la parte más pequeña de la factura. Ten en cuenta:

  • Las cabeceras TCP/IP de cada paquete (unos 40 bytes en IPv4 sin opciones, más con opciones o con IPv6), además de las confirmaciones TCP.
  • La sobrecarga de los registros TLS por mensaje, unas pocas decenas de bytes para la cabecera, el nonce y la etiqueta de autenticación.
  • La sobrecarga de MQTT: cabecera fija, nombre del topic (a menudo la parte más grande de un mensaje pequeño) y un PUBACK con QoS 1.
  • Los pings de keep alive: un PINGREQ y un PINGRESP ocupan solo 2 bytes cada uno, pero con TLS, cabeceras TCP/IP y ACK, cada ida y vuelta cuesta mucho más en la red.
  • El establecimiento de la conexión: un handshake TLS completo, con la cadena de certificados del servidor, suele ocupar varios kilobytes. Las reconexiones frecuentes pueden dominar el presupuesto.

El script siguiente da una estimación aproximada. Las cifras por paquete son suposiciones deliberadamente conservadoras; sustitúyelas por valores medidos en tu módem (la mayoría de los módems informan de los bytes enviados y recibidos).

# Estimación aproximada de datos mensuales para un dispositivo celular con MQTT sobre TLS.
# Las cifras de sobrecarga son suposiciones: mide tu propio enlace y ajústalas.

PAYLOAD_BYTES = 60          # p. ej. JSON compacto
TOPIC_BYTES = 40            # longitud de la cadena del topic
MSGS_PER_HOUR = 12          # una lectura cada 5 minutos
QOS = 1                     # QoS 1 añade un PUBACK
KEEPALIVE_S = 240           # los pings solo cuentan cuando el enlace está inactivo
RECONNECTS_PER_DAY = 2      # cada una paga un handshake TLS completo
TLS_HANDSHAKE_BYTES = 6000  # suposición: domina la cadena de certificados
TCP_IP_PER_PACKET = 52      # IPv4 + TCP con la opción de timestamps
TLS_PER_RECORD = 29         # sobrecarga de registro TLS 1.2 AES-GCM
TCP_ACK = 52                # paquete ACK puro

def bytes_per_publish():
    mqtt = 2 + 2 + TOPIC_BYTES + (2 if QOS > 0 else 0) + PAYLOAD_BYTES
    out = mqtt + TLS_PER_RECORD + TCP_IP_PER_PACKET
    back = TCP_ACK
    if QOS > 0:
        back = 4 + TLS_PER_RECORD + TCP_IP_PER_PACKET + TCP_ACK  # PUBACK + nuestro ACK
    return out + back

def bytes_per_ping():
    one_way = 2 + TLS_PER_RECORD + TCP_IP_PER_PACKET
    return 2 * one_way + 2 * TCP_ACK

msgs = MSGS_PER_HOUR * 24 * 30
gap = 3600 / MSGS_PER_HOUR
pings_per_gap = int(gap // KEEPALIVE_S)        # pings necesarios entre mensajes
pings = pings_per_gap * msgs

total = (msgs * bytes_per_publish()
         + pings * bytes_per_ping()
         + RECONNECTS_PER_DAY * 30 * TLS_HANDSHAKE_BYTES)

print(f"messages/month: {msgs}")
print(f"payload only:   {msgs * PAYLOAD_BYTES / 1e6:.2f} MB")
print(f"estimated total: {total / 1e6:.2f} MB")

Al ejecutarlo verás el mismo patrón que encontrarás sobre el terreno: el total en la red es varias veces el payload en bruto, y la longitud del topic, las confirmaciones y las reconexiones importan tanto como los propios datos. Por eso las optimizaciones siguientes se centran en la sobrecarga.

Keep alive frente a los timeouts NAT del operador

El intervalo de keep alive de MQTT indica al broker cuánto tiempo puede permanecer en silencio el cliente. Si no se envía nada más, el cliente envía un PINGREQ antes de que venza el intervalo. En la red celular, el keep alive tiene una segunda función: mantener viva la asignación NAT del operador.

  • Si el keep alive es más largo que el timeout de inactividad del NAT, la asignación se elimina en silencio. El dispositivo sigue creyendo que está conectado; la siguiente publicación desaparece y la conexión solo se detecta como muerta tras un timeout TCP. Mientras tanto, los comandos desde la nube nunca llegan.
  • Si el keep alive es mucho más corto de lo necesario, pagas pings que no necesitas y mantienes la radio despierta.

Los timeouts NAT varían entre operadores y pueden ser de solo unos minutos. Mide el tuyo: conéctate con un keep alive largo, espera cada vez más tiempo entre publicaciones y anota cuándo dejan de llegar los mensajes desde la nube. Después fija el keep alive holgadamente por debajo de ese valor. Si el dispositivo solo envía datos y no necesita recibir comandos, una alternativa es conectar, publicar y desconectar, aceptando el coste del handshake cada vez, lo que puede salir más barato cuando los informes están separados por horas.

Reducir el coste de TLS sin renunciar a TLS

Nunca envíes MQTT en texto plano por una red celular pública. En su lugar, abarata TLS:

  1. Mantén las conexiones abiertas cuando los informes sean frecuentes, para que el handshake ocurra rara vez.
  2. Usa la reanudación de sesión TLS si tu módem o tu librería TLS admiten session IDs o session tickets. Un handshake reanudado se salta el intercambio de certificados y ahorra bytes y tiempo.
  3. Prefiere ECDHE con ECDSA cuando ambos lados lo admitan; las claves y firmas de curva elíptica son más pequeñas que sus equivalentes RSA.
  4. Confía solo en la raíz que necesitas (por ejemplo ISRG Root X1 para certificados de Let's Encrypt) para que el dispositivo almacene y valide una cadena mínima.
  5. Evita las tormentas de reconexión. Usa backoff exponencial con jitter para que una caída de la celda no desencadene cientos de handshakes seguidos.

Reduce cada mensaje

  • Topics cortos. Un topic como t/pump7/p es mucho más barato en cada publicación que una larga jerarquía descriptiva. Con MQTT 5, los topic aliases permiten al cliente enviar el topic completo una vez y después referirse a él con un entero pequeño, conservando topics legibles sin el coste por mensaje.
  • Payloads compactos. Claves JSON cortas, enteros con escalado implícito o un formato binario como Protobuf (el que usa Sparkplug B).
  • Agrupa las lecturas. Envía diez lecturas en un mensaje cada diez minutos en lugar de una por minuto; las cabeceras se pagan una sola vez.
  • Report by exception. Envía solo los cambios significativos más un heartbeat; consulta report by exception.
  • Elección de QoS. QoS 1 añade un PUBACK por mensaje. Úsalo donde importe la entrega, y QoS 0 para valores de alta frecuencia en los que la siguiente lectura sustituye a la anterior.

Ajustes de sesión de MQTT 5 para enlaces intermitentes

Una sesión persistente permite al broker conservar las suscripciones y encolar mensajes QoS 1 para el dispositivo mientras está brevemente desconectado, de modo que los comandos enviados durante un corte no se pierden. Con paho-mqtt 2.x:

import paho.mqtt.client as mqtt
from paho.mqtt.packettypes import PacketTypes
from paho.mqtt.properties import Properties

client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2,
                     client_id="YOUR_CLIENT_ID", protocol=mqtt.MQTTv5)
client.username_pw_set("YOUR_USERNAME", "YOUR_PASSWORD")
client.tls_set()                                   # almacén de CA del sistema
client.reconnect_delay_set(min_delay=2, max_delay=300)

props = Properties(PacketTypes.CONNECT)
props.SessionExpiryInterval = 3600                 # conserva la sesión 1 h tras una caída

client.connect("YOUR_BROKER_HOST", 8883, keepalive=240,
               clean_start=False, properties=props)
client.loop_forever()

Combínalo con un búfer local para los datos salientes, porque una sesión solo protege los mensajes que el broker ha recibido. La guía de store and forward MQTT muestra una cola respaldada en disco precisamente para eso.

Checklist de diseño para red celular

  1. Elige LTE-M para MQTT sobre TCP/TLS y comandos interactivos; considera NB-IoT para dispositivos fijos que informan con poca frecuencia.
  2. Mide el timeout de inactividad del NAT del operador y fija el keep alive por debajo.
  3. Mantén TLS, pero minimiza los handshakes y usa la reanudación de sesión cuando esté disponible.
  4. Acorta los topics o usa topic aliases de MQTT 5; compacta y agrupa los payloads.
  5. Aplica report by exception en el edge.
  6. Guarda en un búfer del dispositivo los datos salientes durante las caídas.
  7. Usa backoff exponencial con jitter para las reconexiones.
  8. Vigila los bytes por dispositivo y día para que las anomalías aparezcan antes que la factura.

Para un ejemplo práctico con módem, consulta SIM7600 MQTT. Para la arquitectura a nivel de gateway, lee sobre el gateway edge IIoT y la guía de IoT industrial con MQTT.

Prueba antes de pagar por los datos

Valida primero la estructura de topics y el formato del payload por Wi-Fi o Ethernet, usando el servidor MQTT público gratuito con un prefijo único como connectmq/demo/cell-4f8a/.... Después pasa el dispositivo a la red celular con credenciales de producción en un broker MQTT gestionado como ConnectMQ, que ofrece endpoints TLS y funciones de MQTT 5 sin que tengas que operar servidores. ¿Comparando opciones de alojamiento? La guía del broker MQTT en la nube explica qué buscar.

Conecta tu flota celular a ConnectMQ

ConnectMQ admite MQTT 3.1.1 y 5.0 sobre TLS en el puerto 8883, con expiración de sesión, topic aliases y expiración de mensajes para enlaces intermitentes. Su topic $SYS/broker/connection/{clientId}/state y las vistas de dispositivos del portal te ayudan a ver qué dispositivos remotos están conectados. Crea un broker Serverless gratuito en app.connectmq.com, sin tarjeta de crédito.

Preguntas frecuentes

¿Cuántos datos consume MQTT sobre red celular?

Depende mucho más de la sobrecarga que del payload: cabeceras TCP/IP y TLS, confirmaciones, pings de keep alive y handshakes de reconexión. Haz una estimación con un modelo como el script anterior y verifícala después con los contadores de bytes de tu módem.

¿Qué keep alive debo usar en LTE-M?

Uno más corto que el timeout de inactividad del NAT de tu operador, que deberías medir. Unos pocos minutos es un punto de partida habitual; ajústalo después de probar.

¿Es NB-IoT adecuado para MQTT?

Puede funcionar para mensajes pequeños y poco frecuentes de dispositivos fijos, pero la alta latencia y el bajo rendimiento hacen que los handshakes TCP y TLS sean lentos. LTE-M encaja más fácilmente con MQTT estándar sobre TLS.

¿Deben los dispositivos celulares desconectarse entre informes?

Si los informes están separados por horas y no hace falta que lleguen comandos al dispositivo, conectar-publicar-desconectar puede salir más barato. Para informes frecuentes o control remoto, mantén la conexión abierta con un keep alive ajustado.

Artículos relacionados

Todos los artículos →

Tu broker MQTT privado, a unos pocos clics

Crea una cuenta, obtén las credenciales de tu broker y conecta tu primer dispositivo, sin servidores que instalar, parchear ni escalar.