Paho MQTT para Python es la forma estándar de hablar MQTT desde Python, tanto si programas un sensor en una Raspberry Pi como si construyes un servicio de backend o pruebas un dispositivo. En este tutorial instalarás paho-mqtt 2.x, escribirás un suscriptor y un publicador que se comunican a través de broker.connectmq.com y entenderás el bucle de red lo suficiente como para evitar los errores habituales. Al final pasarás el mismo código a un broker privado con credenciales y TLS.
Qué necesitas
- Python 3.8 o posterior (
python --version) - Un terminal con acceso saliente al puerto 1883, o al 8883 para TLS
- Diez minutos
Para la primera parte no necesitas cuenta. El servidor MQTT público gratuito en broker.connectmq.com acepta conexiones anónimas con cualquier client ID único y no vacío. Su área pública la puede leer cualquiera, así que usa un prefijo de topic aleatorio y nunca envíes datos reales allí.
Instala paho-mqtt
Crea un entorno virtual e instala la librería:
python -m venv .venv
source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install "paho-mqtt>=2.0"
python -c "import paho.mqtt; print(paho.mqtt.__version__)"
Deberías ver una versión 2.x. La versión 2.0 cambió cómo se construye el cliente y cómo se llaman los callbacks. Muchos tutoriales en internet siguen mostrando el estilo 1.x, que ahora falla o muestra avisos de obsolescencia. Todo lo de aquí usa el estilo actual; si estás migrando código existente, consulta la guía de la versión de la API de callbacks de paho-mqtt 2.0.
Paso 1: un suscriptor
Guarda esto como subscriber.py. Cambia k7f3q2 por tu propia cadena aleatoria:
import uuid
import paho.mqtt.client as mqtt
BROKER = "broker.connectmq.com"
PORT = 1883
TOPIC = "connectmq/demo/k7f3q2/#"
def on_connect(client, userdata, flags, reason_code, properties):
if reason_code.is_failure:
print(f"Connection failed: {reason_code}")
return
print(f"Connected to {BROKER}")
# Suscribirse aquí para que la suscripción se restaure tras cada reconexión.
client.subscribe(TOPIC, qos=1)
def on_subscribe(client, userdata, mid, reason_code_list, properties):
for rc in reason_code_list:
if rc.is_failure:
print(f"Subscription rejected: {rc}")
else:
print(f"Subscribed with QoS {rc.value}")
def on_message(client, userdata, msg):
retained = " (retained)" if msg.retain else ""
print(f"{msg.topic}{retained}: {msg.payload.decode('utf-8', errors='replace')}")
def on_disconnect(client, userdata, disconnect_flags, reason_code, properties):
print(f"Disconnected: {reason_code}")
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=f"sub-{uuid.uuid4().hex[:8]}")
client.on_connect = on_connect
client.on_subscribe = on_subscribe
client.on_message = on_message
client.on_disconnect = on_disconnect
client.connect(BROKER, PORT, keepalive=60)
client.loop_forever()
Ejecútalo con python subscriber.py. Muestra "Connected" y "Subscribed with QoS 1" y se queda esperando.
Paso 2: un publicador
En un segundo terminal, guarda esto como publisher.py:
import json
import random
import time
import uuid
import paho.mqtt.client as mqtt
BROKER = "broker.connectmq.com"
PORT = 1883
PREFIX = "connectmq/demo/k7f3q2"
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id=f"pub-{uuid.uuid4().hex[:8]}")
# Last Will: lo publica el broker si este cliente desaparece sin desconectarse.
client.will_set(f"{PREFIX}/status/publisher", payload="offline", qos=1, retain=True)
client.connect(BROKER, PORT, keepalive=60)
client.loop_start() # bucle de red en un hilo en segundo plano
# Estado retenido: los nuevos suscriptores lo reciben de inmediato.
client.publish(f"{PREFIX}/status/publisher", "online", qos=1, retain=True)
try:
for i in range(10):
payload = json.dumps({"seq": i, "temperature": round(random.uniform(20, 25), 2)})
info = client.publish(f"{PREFIX}/sensors/room1", payload, qos=1)
info.wait_for_publish(timeout=5)
print(f"Sent #{i} (mid={info.mid}, published={info.is_published()})")
time.sleep(1)
finally:
client.publish(f"{PREFIX}/status/publisher", "offline", qos=1, retain=True).wait_for_publish(5)
client.disconnect()
client.loop_stop()
Ejecuta python publisher.py. El suscriptor muestra el mensaje de estado y diez lecturas del sensor, una por segundo.
Entender el bucle de red de Paho MQTT en Python
El bucle de red es la parte de paho que lee y escribe en el socket, envía los pings de keepalive y llama a tus callbacks. Sin él no ocurre nada. Tienes tres formas de ejecutarlo:
| Método | ¿Bloquea? | Úsalo cuando |
|---|---|---|
loop_forever() |
Sí, hasta disconnect() |
El script solo reacciona a mensajes, como un suscriptor o un logger |
loop_start() / loop_stop() |
No, se ejecuta en un hilo en segundo plano | Tu hilo principal hace otro trabajo, como leer sensores |
loop(timeout) |
Una iteración por llamada | Lo integras con tu propio bucle de eventos |
El error de principiante más común es llamar a publish() sin ningún bucle en marcha y preguntarse por qué no llega nada, o por qué los mensajes QoS 1 nunca se confirman. Otro es olvidar que, con loop_start(), los callbacks se ejecutan en el hilo del bucle; si un callback toca datos que también usa tu hilo principal, protégelos con un lock o una queue.Queue.
loop_forever() y loop_start() se reconectan automáticamente tras una conexión caída. Por eso el suscriptor se suscribe dentro de on_connect: con una sesión limpia, las suscripciones se pierden al reconectar y hay que volver a hacerlas. La guía de reconexión de paho-mqtt profundiza en el backoff y el almacenamiento mientras se está offline.
QoS, retained messages y Last Will en la práctica
El publicador usó tres funciones de MQTT que merece la pena entender:
QoS 1 significa que el broker confirma cada mensaje y paho lo reenvía si la confirmación no llega. wait_for_publish() bloquea hasta esa confirmación, algo práctico en scripts que terminan justo después de publicar. QoS 0 es "dispara y olvida" y algo más barato; QoS 2 garantiza la entrega exactamente una vez a costa de un viaje de ida y vuelta adicional. Nuestro artículo niveles de QoS de MQTT explicados los compara en detalle.
Los retained messages los guarda el broker, uno por topic, y se envían de inmediato a quien se suscriba más tarde. Son ideales para estados como "online" o la configuración actual de un dispositivo. Reinicia el suscriptor y verás que recibe al instante el estado retenido, marcado como "(retained)".
El Last Will se registra al conectar. Si el publicador falla o pierde la red, el broker publica "offline" en su nombre cuando expira el keepalive. Pulsa Ctrl+C durante el bucle y verás el "offline" limpio del bloque finally; si en cambio matas el proceso con kill -9, el broker envía el testamento.
Probar con herramientas de línea de comandos
Puedes verificar tu código Python desde el otro lado con los clientes de Mosquitto:
mosquitto_sub -h broker.connectmq.com -p 1883 -i cli-k7f3q2 -t 'connectmq/demo/k7f3q2/#' -v
El portal de ConnectMQ ofrece la misma vista sin instalar nada: su cliente MQTT en el navegador y su explorador de topics en vivo muestran cada topic bajo tu prefijo a medida que llegan los mensajes, algo útil cuando depuras en una máquina donde no puedes instalar Mosquitto. Ten en cuenta que el portal trabaja con tu broker privado, mientras que el comando de arriba apunta al público.
Si la línea de comandos recibe tus mensajes pero tu suscriptor en Python no, el problema está en el código del suscriptor; si ninguno los recibe, revisa el publicador o la red.
Errores comunes y cómo solucionarlos
ValueError: Unsupported callback API versiono unTypeErrorsobrecallback_api_version: estás usando la construcción del estilo 1.x con paho-mqtt 2.x. Pasamqtt.CallbackAPIVersion.VERSION2como primer argumento.TypeError: on_connect() takes 4 positional arguments but 5 were given: tu callback tiene la firma antigua. Usa las firmas de la versión 2 mostradas arriba.ConnectionRefusedErroro timeouts: el puerto 1883 está bloqueado en tu red. Prueba TLS en el 8883.- Se conecta y se desconecta en bucle: otro cliente usa el mismo client ID. Genera uno único, como hacen los ejemplos.
- No se recibe nada: comprueba que los topics coinciden exactamente, incluidas mayúsculas y el prefijo, y que hay un bucle en marcha.
- Mensajes descartados en el broker público: permite 10 mensajes por segundo por cliente y 64 KB por mensaje. Reduce el ritmo o agrupa las lecturas.
Pasar a un broker privado con credenciales y TLS
Para datos reales, crea tu propio broker. Regístrate en app.connectmq.com, elige el plan Serverless gratuito, crea un broker y copia el usuario, la contraseña y el client ID generados. Después cambia el código de conexión:
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2, client_id="YOUR_CLIENT_ID")
client.username_pw_set("YOUR_USERNAME", "YOUR_PASSWORD")
client.tls_set() # usa el almacén de CA del sistema y verifica el nombre de host
client.connect("broker.connectmq.com", 8883, keepalive=60)
Usa el nombre de host que el portal muestra para tu broker. Los brokers de ConnectMQ usan un certificado de Let's Encrypt, en el que ya confía el paquete de CA por defecto de la mayoría de los sistemas. Si te encuentras con errores de certificado, la guía de TLS para MQTT en Python los repasa uno a uno. Guarda las credenciales en variables de entorno en lugar de en los archivos de código.
Hacia dónde seguir
Cuando publicar y suscribirse funcione, estas guías se apoyan en el mismo cliente:
- Estructura bien los payloads con el tutorial de JSON con MQTT en Python.
- Ejecuta muchas tareas concurrentes con aiomqtt y asyncio.
- Persiste los mensajes con el tutorial de MQTT a SQLite en Python.
- Compara Python con otros lenguajes en la visión general de librerías cliente MQTT.
La documentación oficial de Eclipse Paho para Python es la referencia de cada método y opción.
Empieza con ConnectMQ
ConnectMQ es un broker MQTT gestionado que admite MQTT 3.1.1 y 5.0 sobre TCP, TLS y WebSocket, con un portal que incluye un cliente MQTT en el navegador y un explorador de topics en vivo para comprobar lo que publica tu código Python. Experimenta en el broker público y ejecuta después el tráfico de producción en tu propio broker MQTT en la nube. Crea una cuenta gratuita en el plan Serverless, sin necesidad de tarjeta de crédito.
Preguntas frecuentes
¿Qué versión de paho-mqtt debo usar?
Usa la versión 2.x actual. Requiere un argumento callback_api_version al crear el cliente, y los callbacks de la versión 2 son la opción recomendada para código nuevo.
¿Qué diferencia hay entre loop_start y loop_forever?
loop_forever() bloquea el hilo que lo llama y ejecuta el bucle de red hasta que te desconectas. loop_start() ejecuta el mismo bucle en un hilo en segundo plano para que tu código principal siga funcionando.
¿Paho MQTT para Python admite MQTT 5?
Sí. Pasa protocol=mqtt.MQTTv5 al crear el cliente para usar funciones de MQTT 5 como las propiedades, los reason codes y la expiración de sesión.
¿Necesito una cuenta para probar paho-mqtt con ConnectMQ?
No. broker.connectmq.com acepta clientes anónimos en el puerto 1883 para pruebas. Crea una cuenta gratuita cuando necesites un broker privado con credenciales.



