ESP32 y Arduino

PubSubClient rc=-2 y otros códigos de estado explicados (con soluciones)

¿PubSubClient solo muestra "failed, rc=-2"? Esta guía descifra todos los códigos de estado de -4 a 5 y ofrece soluciones probadas para problemas de DNS, puerto, TLS, client ID y credenciales.

Monitor serie que muestra un error de conexión PubSubClient rc=-2 junto a una placa ESP32
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

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 WiFiClient sin 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 ejecutar mqtt.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

  1. ¿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.
  2. ¿Resuelve la placa el nombre de host? Usa el fragmento de DNS anterior.
  3. ¿Conecta en el 1883 con WiFiClient y un client ID anónimo y único? Si es así, el problema está en tu TLS o en tus credenciales.
  4. ¿Conecta en el 8883 con setInsecure()? Si es así, corrige el certificado CA.
  5. ¿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.

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.