ESP32 y Arduino

El ESP32 no conecta al broker MQTT: 12 soluciones que funcionan de verdad

Guía sistemática de resolución de problemas para cuando tu ESP32 no conecta a un broker MQTT: Wi-Fi, DNS, puertos, certificados TLS, credenciales, client ID y códigos de estado de PubSubClient.

Lista de comprobación de depuración junto a una placa ESP32 que muestra por qué el ESP32 no conecta al broker MQTT
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

Cuando tu ESP32 no conecta a un broker MQTT, la causa casi siempre es uno de una docena de problemas previsibles, desde la banda Wi-Fi equivocada hasta un cliente TLS en un puerto sin cifrar. Esta guía te da un orden sistemático de comprobaciones, explica qué significa cada código de error de PubSubClient y muestra cómo confirmar cada solución contra broker.connectmq.com.

Empieza por el código de error

Antes de cambiar nada, averigua dónde falla la conexión. PubSubClient expone el motivo a través de client.state(). Imprímelo cada vez que connect() devuelva false:

if (!mqtt.connect(clientId.c_str())) {
  Serial.printf("MQTT connect failed, state=%d\n", mqtt.state());
}
state() Constante Qué suele significar
-4 MQTT_CONNECTION_TIMEOUT El servidor no respondió dentro del periodo de keepalive
-3 MQTT_CONNECTION_LOST La conexión se cayó después de establecerse
-2 MQTT_CONNECT_FAILED No se pudo abrir en absoluto la conexión TCP o TLS
-1 MQTT_DISCONNECTED El cliente está desconectado limpiamente
1 MQTT_CONNECT_BAD_PROTOCOL El broker no acepta la versión del protocolo
2 MQTT_CONNECT_BAD_CLIENT_ID Client ID rechazado
3 MQTT_CONNECT_UNAVAILABLE Broker no disponible
4 MQTT_CONNECT_BAD_CREDENTIALS Usuario o contraseña incorrectos
5 MQTT_CONNECT_UNAUTHORIZED Cliente no autorizado

La división es útil: los códigos negativos indican que el problema está por debajo de MQTT (red, DNS, TCP, TLS), y los positivos, que el broker respondió y te rechazó. Un -2 te lleva a las soluciones 1–6; un 4 o un 5, a las soluciones 7–9.

Demuestra primero que el lado del broker funciona

Descarta el broker antes de tocar el firmware. Desde un equipo en la misma red, ejecuta:

mosquitto_sub -h broker.connectmq.com -p 1883 -i pc-test-4821 -t 'connectmq/demo/4821/#' -v

En un segundo terminal:

mosquitto_pub -h broker.connectmq.com -p 1883 -i pc-pub-4821 -t connectmq/demo/4821/hello -m test

Si el mensaje llega, el broker y el camino por internet funcionan, y el problema está en el ESP32 o en su segmento de red. Si tampoco llega desde tu equipo, revisa primero el firewall de tu red. La página del servidor MQTT público gratuito enumera los puertos y límites contra los que puedes probar.

Soluciones a nivel de red (códigos de estado negativos)

1. Asegúrate de que el Wi-Fi está realmente conectado en 2,4 GHz

El ESP32 (salvo las variantes más nuevas con soporte de 5 GHz) solo se comunica con redes de 2,4 GHz. Un router de doble banda con un mismo SSID para ambas bandas suele funcionar, pero un SSID solo de 5 GHz nunca funcionará. Comprueba también el orden de las operaciones: llamar a mqtt.connect() antes de que WiFi.status() == WL_CONNECTED falla siempre con -2. Imprime WiFi.localIP() y WiFi.RSSI(); una señal más débil que unos -80 dBm provoca fallos intermitentes que parecen problemas del broker.

2. Comprueba la resolución DNS

Si el hostname no se resuelve, la conexión TCP nunca empieza. Pruébalo directamente:

IPAddress ip;
if (WiFi.hostByName("broker.connectmq.com", ip)) {
  Serial.println(ip);
} else {
  Serial.println("DNS lookup failed");
}

Un fallo aquí apunta al servidor DNS que entrega el DHCP o a una configuración de IP estática sin entrada DNS. Cuando uses WiFi.config() para una dirección estática, pasa también la puerta de enlace y un servidor DNS.

3. Haz coincidir el puerto con la clase de cliente

Este es, con diferencia, el error más común. WiFiClient habla TCP sin cifrar y debe usar el puerto 1883. WiFiClientSecure habla TLS y debe usar el puerto 8883. Mezclarlos produce un -2 o un -4 sin más pistas.

Clase de cliente Puerto Transporte
WiFiClient 1883 MQTT sin cifrar sobre TCP
WiFiClientSecure 8883 MQTT sobre TLS
Librerías WebSocket 8083 / 8084 (ruta /mqtt) MQTT sobre WebSocket / WebSocket seguro

PubSubClient no habla WebSocket, así que los puertos 8083 y 8084 son solo para clientes de navegador y librerías con soporte WebSocket.

4. Corrige la validación del certificado TLS

Con WiFiClientSecure, un certificado de CA ausente o incorrecto hace que el handshake falle en silencio. ConnectMQ usa un certificado de Let's Encrypt, así que carga la raíz ISRG Root X1 con setCACert(). Para confirmar que TLS es el culpable, llama temporalmente a secureClient.setInsecure(): si la conexión de repente funciona, tu CA es incorrecta. No lleves setInsecure() a producción. Lee el error TLS con secureClient.lastError(buf, sizeof(buf)) para obtener detalles, y consulta MQTT sobre SSL/TLS en ESP32 para una configuración segura completa.

5. Dale a TLS memoria y tiempo suficientes

Los handshakes TLS necesitan decenas de kilobytes de heap. Si tu sketch reserva búferes grandes, datos de imagen o varios clientes seguros, el handshake falla con -2. Imprime ESP.getFreeHeap() justo antes de conectar. Comprueba también el reloj: la validación del certificado compara fechas, y algunas librerías requieren una hora válida. Sincronizar NTP con configTime() antes de conectar evita toda una categoría de fallos desconcertantes.

6. Comprueba si la ruta de red bloquea puertos

Las Wi-Fi de invitados, las redes corporativas y algunos puntos de acceso móviles bloquean el 1883 o el 8883 de salida. Si la prueba con el equipo solo funcionó desde otra red, prueba el ESP32 con el punto de acceso de un teléfono. Si ahí conecta, la red original está filtrando los puertos MQTT y necesitas que el administrador de red los abra.

Soluciones a nivel de broker (códigos de estado positivos)

7. Usa un client ID no vacío y único

Cada cliente necesita su propio client ID. Dos dispositivos flasheados con el mismo ID fijo en el código se expulsarán mutuamente del broker una y otra vez, lo que parece desconexiones aleatorias con estado -3. Muchos brokers rechazan también un client ID vacío, incluida el área pública de broker.connectmq.com. Deriva el ID de la MAC del chip:

String clientId = "esp32-" + String((uint32_t)ESP.getEfuseMac(), HEX);

8. Usa exactamente las credenciales y el client ID del portal

En un broker privado de ConnectMQ, el client ID está vinculado a las credenciales emitidas. Si te inventas tu propio client ID mientras usas el usuario y la contraseña generados, el broker rechaza la conexión con el código 5 aunque la contraseña sea correcta. Copia los tres valores del portal: YOUR_CLIENT_ID, YOUR_USERNAME, YOUR_PASSWORD. Ojo con los espacios invisibles al final al pegarlos en el código.

9. Detén las tormentas de reintentos antes de que te bloqueen

Un bucle de reconexión sin espera puede lanzar al broker muchos intentos de conexión por segundo. Con credenciales incorrectas es especialmente dañino, porque ConnectMQ bloquea direcciones IP tras repetidos inicios de sesión fallidos como parte de su protección contra inundaciones. Espera entre intentos y aumenta la espera tras cada fallo:

unsigned long nextAttempt = 0;
unsigned long backoff = 2000;

void ensureMqtt() {
  if (mqtt.connected() || millis() < nextAttempt) return;
  if (mqtt.connect(clientId.c_str())) {
    backoff = 2000;
    mqtt.subscribe("connectmq/demo/4821/cmd");
  } else {
    Serial.printf("connect failed, state=%d, retry in %lu ms\n", mqtt.state(), backoff);
    nextAttempt = millis() + backoff;
    backoff = min(backoff * 2, 60000UL);
  }
}

La estructura sin bloqueos también mantiene ágil loop(). La guía de reconexión MQTT en ESP32 profundiza en una lógica de reconexión robusta.

Soluciones para conexiones que se caen tras conectar

10. Llama a mqtt.loop() con la frecuencia suficiente

PubSubClient solo envía los pings de keepalive y lee los paquetes entrantes dentro de loop(). Un delay() largo o una lectura de sensor bloqueante que supere el intervalo de keepalive (15 segundos por defecto) hacen que el broker crea que el cliente ha muerto, y verás -3 o -4. Sustituye los delays largos por temporizadores con millis(), o sube el keepalive con mqtt.setKeepAlive(30). Si además la placa se reinicia, lee sobre reinicios del task watchdog del ESP32 en proyectos MQTT.

11. Aumenta el búfer para mensajes grandes

El búfer de paquetes por defecto de PubSubClient es de 256 bytes, incluidos el topic y la cabecera. Una publicación mayor falla sin hacer ruido: publish() devuelve false, y un mensaje entrante demasiado grande se descarta. Llama a mqtt.setBufferSize(1024) antes de conectar, dimensionado para tu mensaje más grande.

12. Respeta los límites del broker público

El área pública de broker.connectmq.com permite 64 KB por mensaje, 10 mensajes por segundo por cliente, 10 suscripciones por cliente y hasta 8 niveles de topic. Un sketch que publica en un bucle cerrado o se suscribe a muchos topics puede ser desconectado por superarlos. Limita la frecuencia de publicación, usa una suscripción con comodín en lugar de muchas individuales y mantén cortos los topics como connectmq/demo/4821/room/temp.

Un sketch de diagnóstico

Flashea esto cuando estés atascado. Informa de cada etapa por separado, así que verás exactamente dónde se rompe la cadena:

#include <WiFi.h>
#include <PubSubClient.h>

const char* WIFI_SSID = "YOUR_WIFI";
const char* WIFI_PASS = "YOUR_WIFI_PASSWORD";
const char* HOST = "broker.connectmq.com";

WiFiClient net;
PubSubClient mqtt(net);

void setup() {
  Serial.begin(115200);
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  while (WiFi.status() != WL_CONNECTED) { delay(250); Serial.print('.'); }
  Serial.printf("\nWi-Fi OK  ip=%s rssi=%d\n", WiFi.localIP().toString().c_str(), WiFi.RSSI());

  IPAddress ip;
  if (!WiFi.hostByName(HOST, ip)) { Serial.println("DNS FAILED"); return; }
  Serial.printf("DNS OK    %s\n", ip.toString().c_str());

  if (!net.connect(HOST, 1883)) { Serial.println("TCP 1883 FAILED"); return; }
  Serial.println("TCP OK");
  net.stop();

  String id = "esp32-diag-" + String((uint32_t)ESP.getEfuseMac(), HEX);
  mqtt.setServer(HOST, 1883);
  if (mqtt.connect(id.c_str())) {
    Serial.println("MQTT OK");
    mqtt.publish("connectmq/demo/4821/diag", "hello from diag");
  } else {
    Serial.printf("MQTT FAILED state=%d\n", mqtt.state());
  }
}

void loop() { mqtt.loop(); }

Mantén mosquitto_sub en marcha sobre connectmq/demo/4821/# y verás el mensaje de diagnóstico en cuanto todo funcione.

Cuando funciona en el broker de pruebas pero no en producción

La mayoría de los fallos en producción se reducen a las soluciones 3, 4 y 8: pasar al puerto 8883 sin pasar a WiFiClientSecure, olvidar el certificado de CA o usar un client ID inventado con credenciales del portal. Cambia una sola cosa cada vez y sigue imprimiendo el código de error. El tutorial de IoT con ESP32 muestra un proyecto completo que conecta sin problemas en ambos modos, y qué hace un broker MQTT explica por qué el broker rechaza ciertas conexiones.

Pon tu ESP32 en línea con ConnectMQ

En cuanto el dispositivo conecte con el broker de pruebas, pásalo a un broker privado en el plan gratuito Serverless en app.connectmq.com, sin tarjeta de crédito. El cliente MQTT del navegador y el explorador de topics en vivo del portal facilitan confirmar el tráfico tanto desde el lado del dispositivo como desde el del servidor. ConnectMQ es un broker MQTT gestionado con TLS en el 8883 de serie, así que el único certificado que necesitas en el ESP32 es la raíz pública de Let's Encrypt.

Preguntas frecuentes

¿Qué significa rc=-2 en PubSubClient?

Significa que no se pudo abrir la conexión TCP o TLS, así que el broker nunca vio un paquete MQTT. Revisa el Wi-Fi, el DNS, la combinación de puerto y clase de cliente, el certificado de CA y el heap libre.

¿Por qué mi ESP32 se conecta y se desconecta cada pocos segundos?

Normalmente otro dispositivo está usando el mismo client ID, o mqtt.loop() no se llama con la frecuencia suficiente para enviar los pings de keepalive. Da a cada dispositivo un ID único y elimina los delays largos que bloquean.

¿Puedo usar el puerto 8083 u 8084 con PubSubClient?

No. Son puertos WebSocket, y PubSubClient solo habla MQTT sobre TCP directo o TLS. Usa el 1883 con WiFiClient o el 8883 con WiFiClientSecure.

¿Por qué se rechazan mis credenciales aunque la contraseña sea correcta?

En un broker privado con credenciales emitidas, el client ID está vinculado a ellas. Usa exactamente el client ID que muestra el portal junto con el usuario y la contraseña generados.

Artículos relacionados

Ejemplo de ESP-IDF MQTT: crea un cliente esp-mqtt desde cero

Construye un proyecto ESP-IDF desde una carpeta vacía con el componente esp-mqtt: configuración de Wi-Fi en modo estación, el manejador de eventos MQTT, publicación y suscripción, propiedades de MQTT 5 y TLS con el bundle de certificados.

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.