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íntoma | Causa probable |
|---|---|
publish() devuelve false | Búfer pequeño o sin conexión |
| Se desconecta justo después de publicar | Política sin permiso para ese tópico |
| Se desconecta cada pocos segundos | Client ID repetido |
true y no llega, sin desconexiones | QoS 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.