Ver failed, rc=-2 en el monitor serie es el error más habitual de PubSubClient, y el mensaje por sí solo dice muy poco. PubSubClient rc=-2 significa que la conexión TCP (o TLS) con el broker nunca llegó a abrirse, así que el problema casi siempre está en el DNS, el puerto, la red o el certificado, no en MQTT en sí. Esta guía descifra todos los códigos de estado de -4 a 5 y te da una solución para cada uno.
De dónde sale el valor rc
PubSubClient guarda el resultado del último intento de conexión en una variable de estado interna que se lee con mqtt.state(). La mayoría de los sketches de ejemplo la imprimen cuando connect() devuelve false:
if (!mqtt.connect(clientId)) {
Serial.print("failed, rc=");
Serial.println(mqtt.state());
}
Los valores están definidos como constantes en PubSubClient.h. Los negativos proceden de la librería y de la capa de red. Los positivos son los códigos de retorno que el broker devuelve en su paquete CONNACK, tal y como los define la especificación MQTT 3.1.1.
| rc | Constante | Capa | Significado en lenguaje llano |
|---|---|---|---|
| -4 | MQTT_CONNECTION_TIMEOUT |
Librería | El socket se abrió, pero no llegó el CONNACK (o la respuesta al ping) a tiempo |
| -3 | MQTT_CONNECTION_LOST |
Red | Se cortó una conexión ya establecida |
| -2 | MQTT_CONNECT_FAILED |
Red | No se pudo abrir el socket |
| -1 | MQTT_DISCONNECTED |
Librería | El cliente está desconectado de forma limpia |
| 0 | MQTT_CONNECTED |
Broker | Conectado |
| 1 | MQTT_CONNECT_BAD_PROTOCOL |
Broker | Versión de protocolo no aceptada |
| 2 | MQTT_CONNECT_BAD_CLIENT_ID |
Broker | Client ID rechazado |
| 3 | MQTT_CONNECT_UNAVAILABLE |
Broker | El broker no puede aceptar conexiones ahora mismo |
| 4 | MQTT_CONNECT_BAD_CREDENTIALS |
Broker | Usuario o contraseña incorrectos |
| 5 | MQTT_CONNECT_UNAUTHORIZED |
Broker | Sin autorización para conectar |
La idea clave: con los códigos negativos, el broker o no te vio o no respondió. Con los positivos, el broker recibió tu paquete CONNECT y lo rechazó deliberadamente.
Cómo solucionar PubSubClient rc=-2
Como rc=-2 se notifica siempre que falla client.connect(host, port) en el objeto de red subyacente, tiene muchas causas posibles. Revísalas en este orden.
1. El Wi-Fi no está conectado de verdad
Llamar a mqtt.connect() antes de que WiFi.status() == WL_CONNECTED devuelve siempre -2. También ocurre cuando el punto de acceso te desconecta y tu bucle solo comprueba MQTT. Comprueba el Wi-Fi primero en cada iteración y reconéctalo antes de tocar MQTT.
2. Falla el DNS o el nombre de host es incorrecto
Errores tipográficos como broker.connectmq.com con un espacio al final, mqtt://broker.connectmq.com (una URL en lugar de un nombre de host) o los prefijos http:// fallan sin avisar. setServer() espera un nombre de host sin más o una IPAddress. Prueba el DNS directamente:
IPAddress ip;
if (WiFi.hostByName("broker.connectmq.com", ip)) {
Serial.print("Resolved to ");
Serial.println(ip);
} else {
Serial.println("DNS lookup failed");
}
Si el DNS falla, comprueba que el DHCP te haya asignado un servidor DNS o configura uno explícitamente con WiFi.config().
3. Puerto incorrecto para el tipo de cliente
Es el error clásico. El puerto y el cliente de red tienen que coincidir:
| Puerto | Transporte | Cliente de Arduino que debes usar |
|---|---|---|
| 1883 | MQTT sin cifrar sobre TCP | WiFiClient |
| 8883 | MQTT sobre TLS | WiFiClientSecure |
| 8083 / 8084 | MQTT sobre WebSocket | No compatible con PubSubClient |
Un WiFiClient apuntando al 8883 abre un socket TCP y después se queda colgado o falla porque el broker espera un handshake TLS, lo que suele aparecer como -2 o -4. Un WiFiClientSecure apuntando al 1883 falla en el handshake y devuelve -2. PubSubClient no habla WebSockets en absoluto, así que los puertos 8083 y 8084 son para navegadores y herramientas como las descritas en MQTT sobre WebSocket.
4. Problemas con el certificado TLS
Con WiFiClientSecure, una verificación de certificado fallida también se notifica como rc=-2. Causas habituales:
- No se ha configurado ningún certificado CA. En ESP32, llama a
setCACert()con la raíz que firmó el certificado del broker. ConnectMQ usa Let's Encrypt, así que la raíz es ISRG Root X1. - Fijar (pinning) un certificado hoja o intermedio que ya ha rotado. Los certificados de Let's Encrypt tienen una vida corta; fija la raíz, no la hoja.
- Hora del sistema incorrecta en ESP8266. BearSSL comprueba las fechas de validez, así que sincroniza la hora con
configTime()antes de conectar. - Falta de memoria. El handshake TLS necesita un buen trozo de heap; en ESP8266, un buffer MQTT grande más TLS puede agotarlo.
En ESP32 puedes imprimir el error TLS subyacente tras un intento fallido:
char err[128];
int code = netClient.lastError(err, sizeof(err));
Serial.printf("TLS error %d: %s\n", code, err);
Para aislar el problema, llama temporalmente a netClient.setInsecure(). Si así conecta, el problema está en la configuración del certificado. Nunca publiques firmware con setInsecure().
5. La red bloquea MQTT saliente
Las redes corporativas, de centros educativos y algunas redes de invitados bloquean los puertos salientes 1883 y 8883. Prueba el mismo sketch con el punto de acceso del móvil. Si ahí funciona, pide que abran el puerto o mueve el dispositivo a otro segmento de red.
6. Conectar con demasiada frecuencia
Si el broker detecta intentos fallidos repetidos desde tu dirección IP, puede rechazar conexiones temporalmente. ConnectMQ, por ejemplo, bloquea direcciones IP tras varios inicios de sesión fallidos para proteger a sus clientes. Un bucle de reconexión sin pausa puede convertir una sola contraseña incorrecta en un bloqueo que luego parece un rc=-2. Espera siempre al menos unos segundos entre intentos y aumenta la espera tras varios fallos.
Cómo solucionar rc=-4 (tiempo de conexión agotado)
Aquí el socket se abrió, pero el CONNACK no llegó dentro del tiempo de espera del socket (15 segundos por defecto). Causas típicas:
- Un
WiFiClientsin cifrar en un puerto TLS, como se ha explicado antes. - Señal Wi-Fi débil con mucha pérdida de paquetes. Imprime
WiFi.RSSI(); cualquier valor en torno a -80 dBm o peor es poco fiable. - Código bloqueante. Si la conexión ya estaba establecida, -4 también puede aparecer más tarde cuando no se llama a
loop()con la frecuencia suficiente para responder a los pings de keep alive.
Cómo solucionar rc=-3 (conexión perdida)
La conexión funcionó y luego se cortó. Revisa lo siguiente:
- Client IDs duplicados. Cuando un segundo dispositivo se conecta con el mismo ID, el broker desconecta al primero. Dos placas grabadas con el mismo sketch se irán expulsando por turnos. Genera los IDs a partir de la dirección MAC.
- Caducidad del keep alive. Las llamadas largas a
delay()o las lecturas lentas de sensores dejan sin ejecutarmqtt.loop(). - Mensajes demasiado grandes o demasiado frecuentes. Los brokers aplican límites. En el servidor MQTT público gratuito de broker.connectmq.com, los clientes están limitados a 64 KB por mensaje y 10 mensajes por segundo, y superar los límites puede cerrar la sesión.
Cómo solucionar los códigos positivos (1 a 5)
rc=1: protocolo incorrecto
PubSubClient envía MQTT 3.1.1 por defecto. Solo verás este error si compilaste con MQTT_VERSION fijado en 3.1 contra un broker que lo rechaza. Elimina esa redefinición.
rc=2: client ID rechazado
El culpable habitual es un client ID vacío. El servidor público de ConnectMQ exige un client ID no vacío incluso en conexiones anónimas. En un broker privado de ConnectMQ, los client IDs están vinculados a las credenciales emitidas en el portal, así que usa exactamente el client ID que te dieron.
rc=3: servidor no disponible
El broker está en marcha pero no acepta conexiones, por ejemplo durante un mantenimiento o cuando se ha alcanzado una cuota de conexiones. Espera y vuelve a intentarlo. Si persiste en tu propio broker, comprueba si has superado el límite de conexiones de tu plan.
rc=4: usuario o contraseña incorrectos
Vuelve a copiar las credenciales desde el portal y vigila los caracteres invisibles. Recuerda que connect(id, user, pass) recibe primero el client ID; intercambiar el orden es sorprendentemente común.
rc=5: sin autorización
El broker entendió las credenciales pero rechaza a este cliente, por ejemplo porque el usuario y el client ID no corresponden entre sí. Vuelve a revisar los tres valores.
Una función de conexión con diagnóstico
Añádela a cualquier sketch para obtener un motivo legible en lugar de un número a secas:
const char* mqttStateText(int s) {
switch (s) {
case -4: return "timeout (no CONNACK) - check port/TLS match and signal";
case -3: return "connection lost - duplicate client ID or keep alive";
case -2: return "socket failed - Wi-Fi, DNS, port, firewall or TLS cert";
case -1: return "disconnected";
case 0: return "connected";
case 1: return "bad protocol version";
case 2: return "client ID rejected - empty or not allowed";
case 3: return "server unavailable - retry later";
case 4: return "bad username or password";
case 5: return "not authorised";
default: return "unknown";
}
}
bool connectWithDiagnostics() {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("Wi-Fi not connected, skipping MQTT");
return false;
}
if (mqtt.connect(DEVICE_ID)) {
Serial.println("MQTT connected");
return true;
}
int s = mqtt.state();
Serial.printf("MQTT failed rc=%d: %s\n", s, mqttStateText(s));
return false;
}
Lista rápida para aislar el problema
- ¿Llega tu ordenador al broker? Ejecuta
mosquitto_sub -h broker.connectmq.com -p 1883 -i test-$RANDOM -t "connectmq/demo/check/#" -v. Si falla, el problema es tu red, no la placa. - ¿Resuelve la placa el nombre de host? Usa el fragmento de DNS anterior.
- ¿Conecta en el 1883 con
WiFiClienty un client ID anónimo y único? Si es así, el problema está en tu TLS o en tus credenciales. - ¿Conecta en el 8883 con
setInsecure()? Si es así, corrige el certificado CA. - ¿Conecta con la CA pero falla con credenciales? Revisa de nuevo usuario, contraseña y client ID.
Una vez conectado, el siguiente problema con el que se topa la gente es el tamaño de los mensajes; consulta el tamaño del buffer de PubSubClient. Para un recorrido completo por la librería, lee nuestro tutorial de PubSubClient para Arduino, y para un proyecto completo consulta la guía principal del tutorial de IoT con ESP32.
Deja de perseguir códigos rc con un broker MQTT gestionado
Muchos errores rc=-2 y rc=-4 proceden de brokers autoalojados con certificados caducados, puertos del firewall cerrados o servicios caídos. Un broker MQTT gestionado como ConnectMQ elimina esas variables: TLS en el 8883 con un certificado de Let's Encrypt que se renueva automáticamente, credenciales generadas para ti y un cliente MQTT en el navegador dentro del portal para que confirmes que el lado del broker funciona antes de depurar el dispositivo. Crea un broker Serverless gratuito en app.connectmq.com, sin tarjeta de crédito.
Preguntas frecuentes
¿Qué significa rc=-2 en PubSubClient?
Significa que no se pudo abrir la conexión de red con el broker. El broker nunca recibió tu paquete CONNECT, así que revisa el Wi-Fi, el DNS, el puerto y, si usas TLS, el certificado CA.
¿Por qué obtengo rc=-2 solo en el puerto 8883?
Tu configuración TLS está fallando. Asegúrate de usar WiFiClientSecure, cargar el certificado raíz correcto (ISRG Root X1 para brokers con Let's Encrypt) y, en ESP8266, ajustar el reloj antes de conectar.
Mi placa conecta y luego muestra rc=-3 cada pocos segundos. ¿Por qué?
Lo más frecuente es que otro dispositivo o herramienta esté usando el mismo client ID, de modo que el broker sustituye una sesión por la otra continuamente. Asigna a cada dispositivo un ID único, por ejemplo basado en su dirección MAC.
¿Es rc=5 distinto de rc=4?
Sí. rc=4 significa que el usuario o la contraseña son incorrectos. rc=5 significa que las credenciales se entendieron pero este cliente no tiene permiso para conectar, a menudo porque el client ID no coincide con las credenciales.



