Tutoriales de IoT

Tutorial de Paho MQTT en Python: publicar y suscribirse en 10 minutos

Instala paho-mqtt 2.x, crea un publicador y un suscriptor que funcionan contra broker.connectmq.com, aprende el bucle de red, QoS, los retained messages y el Last Will, y pasa después a un broker privado con credenciales y TLS.

Terminal de Python ejecutando un publicador y un suscriptor de Paho MQTT en Python que intercambian mensajes de sensores
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

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

  1. ValueError: Unsupported callback API version o un TypeError sobre callback_api_version: estás usando la construcción del estilo 1.x con paho-mqtt 2.x. Pasa mqtt.CallbackAPIVersion.VERSION2 como primer argumento.
  2. 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.
  3. ConnectionRefusedError o timeouts: el puerto 1883 está bloqueado en tu red. Prueba TLS en el 8883.
  4. Se conecta y se desconecta en bucle: otro cliente usa el mismo client ID. Genera uno único, como hacen los ejemplos.
  5. No se recibe nada: comprueba que los topics coinciden exactamente, incluidas mayúsculas y el prefijo, y que hay un bucle en marcha.
  6. 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:

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.

Artículos relacionados

Las mejores herramientas CLI de MQTT para pruebas, scripts y depuración

Una comparativa práctica de mosquitto_pub/sub, MQTTX CLI, HiveMQ MQTT CLI, la CLI de MQTT.js, curl y mqttui, con la misma prueba ejecutada contra broker.connectmq.com para que elijas la herramienta adecuada para scripts y depuración.

9 min de lectura

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.