Cloud, DevOps e IoT en español

ESP32 publica en MQTT pero no llega a AWS IoT: causas y solución

17 de octubre de 2026 · Steven Carvajal · Tutoriales

Código de este artículo en GitHub →

El monitor serie dice que el mensaje se publicó. publish() devolvió true. Abres el cliente de pruebas MQTT de AWS IoT, te suscribes al tópico… y no llega nada.

Primero, lo más importante: con QoS 0, true no significa que el mensaje llegó. Significa que la librería lo escribió en el búfer de red. Si algo falla después, nadie te avisa.

Esta es la revisión, en el orden en que conviene hacerla.

1. Mira el tópico desde AWS, no desde el ESP32

En la consola de AWS IoT, abre el cliente de pruebas MQTT y suscríbete a # (todos los tópicos). Así descartas un error de escritura en el nombre del tópico: si el mensaje llega con otro nombre, lo verás.

2. El tamaño del búfer

PubSubClient tiene un búfer de 256 bytes por defecto, que incluye el tópico y la cabecera. Un JSON un poco largo no cabe, y publish() devuelve false… o lo trunca según la versión.

client.setBufferSize(1024);   // PubSubClient 2.8 o superior

3. La política de IoT

Si el dispositivo publica en un tópico que su política no permite, AWS cierra la conexión. No devuelve un error al publicar: simplemente te desconecta, y el ESP32 se reconecta en el siguiente ciclo. Si ves reconexiones constantes justo después de publicar, es esto.

La política necesita iot:Publish sobre el ARN del tópico (topic/...), no del filtro (topicfilter/..., que es para suscribirse). Lo explico en políticas de AWS IoT con mínimo privilegio.

4. Dos dispositivos con el mismo client ID

AWS IoT solo permite una conexión por client ID. Si otro dispositivo (o el cliente de pruebas, o un script) se conecta con el mismo, AWS desconecta al anterior con el motivo DUPLICATE_CLIENTID. Los dos se turnan para conectarse y los mensajes se pierden en medio.

5. No estás llamando a loop()

El cliente MQTT necesita que llames a client.loop() a menudo para enviar y recibir. Un delay() largo o un bucle bloqueante impide que los paquetes salgan y que se responda al keepalive.

void loop() {
  client.loop();      // siempre, en cada vuelta
  // nada de delay(10000) aquí: usa millis()
}

6. La librería

Si todo lo anterior está bien y los mensajes siguen sin llegar, prueba otra librería. Me pasó con un ESP32-S3: PubSubClient con WiFiClientSecure devolvía true en cada publish() y AWS no recibía nada. Descarté la política, los tiempos y el tamaño del búfer (lo subí a 2048). Con el mismo certificado, los mismos tópicos y el mismo código, cambiar a la librería MQTT de Joël Gähwiler (256dpi) lo resolvió.

Antes de cambiar de librería, una prueba útil: publica con el mismo certificado desde tu PC con mosquitto_pub. Si desde ahí llega, el problema está en el dispositivo, no en AWS.

mosquitto_pub --cafile AmazonRootCA1.pem --cert certificado.pem.crt --key privada.pem.key \
  -h TU-ENDPOINT-ats.iot.us-east-1.amazonaws.com -p 8883 \
  -i prueba-pc -t casa/prueba -m '{"hola":1}'

El mismo envío con la librería MQTT

Instálala desde el gestor de librerías buscando MQTT (autor: Joel Gaehwiler).

#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <MQTT.h>
#include "secrets.h"   // certificados y endpoint, fuera del repositorio

WiFiClientSecure red;
MQTTClient mqtt(1024);   // búfer de 1 KB

void conectarAWS() {
  red.setCACert(AWS_CERT_CA);
  red.setCertificate(AWS_CERT_CRT);
  red.setPrivateKey(AWS_CERT_PRIVATE);

  mqtt.begin(AWS_IOT_ENDPOINT, 8883, red);
  mqtt.setKeepAlive(60);

  while (!mqtt.connect(THING_NAME)) {
    Serial.print(".");
    delay(1000);
  }
  Serial.println("\nConectado a AWS IoT");
}

void setup() {
  Serial.begin(115200);
  // ... conectar Wi-Fi
  conectarAWS();
}

void loop() {
  mqtt.loop();
  if (!mqtt.connected()) conectarAWS();

  static uint32_t ultimo = 0;
  if (millis() - ultimo > 10000) {
    ultimo = millis();
    // QoS 1: publish() espera la confirmación (PUBACK) del broker
    bool ok = mqtt.publish("casa/sala/estado", "{\"temp\":22.5}", false, 1);
    Serial.println(ok ? "Confirmado por AWS" : "Sin confirmación");
  }
}

Con QoS 1, AWS responde con un PUBACK y el resultado de publish() por fin significa algo: el broker recibió el mensaje.

Diagnóstico rápido

SíntomaCausa probable
publish() devuelve falseBúfer pequeño o sin conexión
Se desconecta justo después de publicarPolítica sin permiso para ese tópico
Se desconecta cada pocos segundosClient ID repetido
true y no llega, sin desconexionesQoS 0 sin confirmación o problema de la librería

El procedimiento completo de conexión está en conectar un ESP32 a AWS IoT Core paso a paso. Este error es uno de los 13 errores reales de mi proyecto de domótica.

Preguntas frecuentes

¿PubSubClient no sirve para AWS IoT?

Mucha gente lo usa sin problemas. Si te funciona, no lo cambies. Si publish() dice que sí y los mensajes no llegan, cambiar a la librería de 256dpi es lo primero que conviene probar.

¿AWS IoT admite QoS 2?

No. AWS IoT Core admite QoS 0 y 1.

¿Cómo veo los errores del lado de AWS?

Activa los registros de AWS IoT en CloudWatch (nivel ERROR o INFO) y busca por el client ID del dispositivo. Ahí aparecen los rechazos por política y las desconexiones con su motivo.

Sigue leyendo

Guía completa de comunicación: esp-now y mqtt: ESP-NOW, Wi-Fi, Bluetooth o MQTT: cómo comunicar tus dispositivos ESP32