El MQTT Last Will and Testament (LWT) es la función que permite a un broker hablar en nombre de un cliente que ya no puede hacerlo por sí mismo. Un dispositivo registra un mensaje de "última voluntad" (will) al conectarse, y el broker lo publica si ese dispositivo desaparece sin una desconexión limpia. Al terminar este artículo conocerás todos los campos del will, las condiciones exactas que lo activan y cómo probarlo tú mismo matando un cliente conectado a un broker real.
Por qué MQTT necesita un Last Will
MQTT está diseñado para redes poco fiables. Los dispositivos se quedan sin alimentación, el firmware se cuelga y los enlaces celulares se caen durante minutos. En un sistema publish/subscribe puro, publicador y suscriptor nunca hablan directamente, así que nadie notaría que un sensor ha enmudecido hasta que una persona mirara un dashboard desactualizado.
El broker MQTT es el único que ve todas las conexiones. El LWT convierte esa posición en una función: el cliente le dice de antemano al broker "si desaparezco, publica esto por mí". Los suscriptores se enteran entonces del fallo a través de los mismos topics que ya están observando.
Los campos del MQTT Last Will and Testament
El will se define dentro del paquete CONNECT, así que debe configurarse antes de que el cliente se conecte. No se puede cambiar durante una sesión; para cambiarlo hay que reconectarse.
| Campo | Propósito | Valor típico |
|---|---|---|
| Will topic | Dónde publica el broker el will | site/line1/pump-07/status |
| Will payload | Cuerpo del mensaje | offline o {"state":"offline"} |
| Will QoS | Garantía de entrega del will | 1 |
| Will retain | Si el will pasa a ser el valor retenido | true |
| Will delay interval (MQTT 5) | Segundos de espera antes de publicar | De 0 a unos pocos segundos |
| Will properties (MQTT 5) | Message expiry, content type, user properties | Opcionales |
La mayoría de las configuraciones de producción usan QoS 1 y retain true. El retain es lo que hace útil el estado para los clientes que se conectan más tarde; sin él, un dashboard que se abre después del fallo nunca se entera de que el dispositivo está caído. El comportamiento de la retención se explica en detalle en los retained messages de MQTT.
Cuándo publica el broker el will
El broker publica el will cuando la conexión de red se cierra sin un paquete DISCONNECT. En la práctica, eso incluye:
- El sistema operativo del cliente cierra o resetea la conexión TCP, por ejemplo al matar el proceso.
- Vence el temporizador de keep alive. El broker espera aproximadamente 1,5 veces el intervalo de keep alive sin recibir paquetes antes de dar al cliente por muerto.
- El cliente viola el protocolo y el broker cierra la conexión.
- Un fallo de red que el broker detecta mediante un error de E/S.
No publica el will cuando:
- El cliente envía un DISCONNECT normal. El will se descarta.
- En MQTT 5, el cliente se reconecta antes de que venza el will delay interval y reanuda su sesión.
Esta regla es importante: un apagado limpio no genera will. Si también quieres "offline" en un apagado ordenado, publícalo tú antes de desconectarte. MQTT 5 también permite que un cliente envíe DISCONNECT con el reason code 0x04 (disconnect with will message) para pedir al broker que publique el will de todos modos.
Probar el LWT matando un cliente
La forma más rápida de entenderlo es ver cómo se dispara un will. Usamos el servidor MQTT público gratuito en broker.connectmq.com con un prefijo de topic único.
Terminal 1, el observador:
mosquitto_sub -h broker.connectmq.com -p 1883 -i lwt-watch-q81m \
-t 'connectmq/demo/q81m/#' -v
Terminal 2, el "dispositivo". Registra un will retenido, usa un keep alive de 10 segundos y se queda suscrito para que el proceso siga en marcha:
mosquitto_sub -h broker.connectmq.com -p 1883 -i lwt-device-q81m -k 10 \
--will-topic connectmq/demo/q81m/device1/status \
--will-payload offline --will-qos 1 --will-retain \
-t connectmq/demo/q81m/device1/cmd
Terminal 3, anuncia el dispositivo como conectado con un mensaje de nacimiento (birth) retenido:
mosquitto_pub -h broker.connectmq.com -p 1883 -i lwt-birth-q81m \
-t connectmq/demo/q81m/device1/status -m online -q 1 -r
Ahora mata el dispositivo de forma abrupta. En Linux o macOS:
pkill -9 -f lwt-device-q81m
La terminal 1 imprime connectmq/demo/q81m/device1/status offline casi de inmediato, porque el sistema operativo cerró el socket y el broker vio que se caía sin DISCONNECT. Si en cambio desconectas el cable de red o suspendes un portátil, el will solo llega cuando pasa la ventana de keep alive, unos 15 segundos con -k 10.
Prueba un experimento más: vuelve a arrancar el proceso del dispositivo y detenlo con Ctrl+C. Las herramientas de Mosquitto envían un DISCONNECT correcto al pulsar Ctrl+C, así que no se publica ningún will y el estado retenido se queda como estaba. Ese es el hueco que cierras publicando explícitamente "offline" al apagar.
LWT en Python con paho-mqtt
Este es el esqueleto completo de un dispositivo con paho-mqtt 2.x. Configura el will antes de conectarse, publica un mensaje de nacimiento retenido en on_connect (para que se repita tras cada reconexión) y publica "offline" al salir de forma ordenada:
import signal
import sys
import time
import paho.mqtt.client as mqtt
BROKER = "broker.connectmq.com"
CLIENT_ID = "lwt-py-device-q81m"
STATUS = "connectmq/demo/q81m/device2/status"
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=CLIENT_ID)
client.will_set(STATUS, payload="offline", qos=1, retain=True)
def on_connect(client, userdata, flags, reason_code, properties):
if reason_code.is_failure:
print("connect failed:", reason_code)
return
client.publish(STATUS, "online", qos=1, retain=True)
print("online")
def shutdown(signum, frame):
client.publish(STATUS, "offline", qos=1, retain=True).wait_for_publish(timeout=5)
client.disconnect()
client.loop_stop()
sys.exit(0)
client.on_connect = on_connect
signal.signal(signal.SIGINT, shutdown)
signal.signal(signal.SIGTERM, shutdown)
client.connect(BROKER, 1883, keepalive=15)
client.loop_start()
while True:
time.sleep(1)
Ejecútalo, observa cómo aparece "online" en la terminal del observador y luego mátalo con kill -9 para ver el will publicado por el broker, o pulsa Ctrl+C para ver el mensaje offline del propio script.
Consejos de diseño para una detección fiable de desconexiones
Elige un keep alive razonable. El keep alive fija el tiempo de detección en el peor caso para los fallos silenciosos. Entre 15 y 60 segundos es lo habitual en dispositivos conectados a la red eléctrica. Los valores muy cortos cuestan batería y ancho de banda; los muy largos retrasan las alertas. Nuestra guía del intervalo de keep alive de MQTT cubre los compromisos.
Combina siempre el LWT con un mensaje de nacimiento. El will solo dice "offline". Algo tiene que volver a decir "online" tras reconectarse, y debe ser retenido para que sobrescriba el will retenido.
Usa el mismo topic y el mismo flag retain para ambos. Si el will es retenido pero el mensaje de nacimiento no, los nuevos suscriptores verán "offline" para siempre.
Cuidado con los client IDs duplicados. Si dos dispositivos comparten client ID, el broker desconecta a uno cuando se conecta el otro, y se expulsan mutuamente en bucle. El estado oscila entre online y offline. Haz que cada client ID sea único.
Considera un will delay en MQTT 5. Un pequeño retraso evita un "offline" momentáneo cuando un dispositivo se reconecta rápido tras un corte de red. Consulta el will delay interval de MQTT para más detalles.
Incluye contexto en los payloads JSON. Un payload como {"state":"offline","reason":"lwt"} permite a los consumidores distinguir un fallo de un apagado planificado.
Last Will and Testament en ConnectMQ
ConnectMQ admite LWT en conexiones MQTT 3.1.1 y 5.0, incluidos los wills retenidos. En un broker de producción te conectas con las credenciales del portal (YOUR_USERNAME, YOUR_PASSWORD, YOUR_CLIENT_ID) por TLS en el puerto 8883, y el will funciona exactamente igual que en los ejemplos anteriores. Como en ConnectMQ los client IDs están vinculados a credenciales emitidas, es más fácil evitar el problema de las oscilaciones por IDs duplicados.
Para tener visibilidad desde el broker además del LWT, ConnectMQ también expone el estado de conexión en $SYS/broker/connection/{clientId}/state, algo práctico para herramientas de monitorización que no deberían depender de que cada dispositivo configure bien su will. Si estás decidiendo dónde alojarlo, compara opciones en nuestra visión general de broker MQTT gestionado, y para los fundamentos del protocolo consulta el tutorial de MQTT completo.
Empieza con ConnectMQ
Regístrate en app.connectmq.com y crea un broker en el plan Serverless gratuito, sin tarjeta de crédito. Apunta tus dispositivos hacia él con un Last Will retenido y un mensaje de nacimiento, y tendrás un estado online/offline fiable desde el primer día.
Preguntas frecuentes
¿Se publica el Last Will cuando un cliente se desconecta normalmente?
No. Un DISCONNECT normal indica al broker que descarte el will. Publica tu propio mensaje offline antes de desconectarte si lo necesitas para los apagados planificados.
¿Cuánto tarda el broker en publicar el will?
Si el socket se cierra, es casi inmediato. Si el dispositivo desaparece en silencio, el broker espera aproximadamente 1,5 veces el intervalo de keep alive, más el will delay interval de MQTT 5 si lo hay.
¿Debe ser retenido el mensaje will?
Normalmente sí. Un will retenido garantiza que los clientes que se conectan después del fallo sigan viendo el dispositivo como desconectado.
¿Puedo cambiar el will después de conectarme?
No. El will forma parte del paquete CONNECT. Para cambiarlo, desconéctate y vuelve a conectarte con la nueva configuración del will.



