Quando um ESP32 se recusa a falar com um broker MQTT, o monitor serial costuma mostrar uma única linha pouco útil: failed, rc=-2 try again in 5 seconds. O caminho mais rápido é testar a conexão camada por camada (Wi-Fi, DNS, porta TCP, handshake MQTT e publicação) e parar na primeira camada que falhar.
Este guia traz um sketch de diagnóstico para o core Arduino (core ESP32 3.x com PubSubClient 2.8) que faz exatamente isso, explica cada resultado e mostra como confirmar, do lado do broker, que a placa está realmente lá.
O checklist em resumo
| Passo | O que pode dar errado | Como aparece |
|---|---|---|
| 1. Log serial | Baud rate errado, nenhum log | Lixo ou silêncio no monitor |
| 2. Wi-Fi | SSID/senha errados, rede de 5 GHz, sinal fraco | WiFi.status() nunca chega a WL_CONNECTED (3) |
| 3. DNS | DHCP sem servidor DNS, erro de digitação no host | hostByName falha |
| 4. Porta TCP | Firewall, porta errada, porta TLS com cliente sem TLS | rc=-2 do PubSubClient |
| 5. MQTT CONNECT | Client ID duplicado, credenciais erradas, ACL | rc=2, rc=4, rc=5 |
| 6. Publicação | Pacote maior que o buffer | publish() retorna false |
| 7. Manter a conexão | loop() não é chamado, keep alive curto demais | rc=-3 depois de um tempo |
Passo 1: tenha um log serial legível
Configure o monitor na mesma velocidade do Serial.begin(); 115200 é a convenção. No Arduino IDE, ajuste também Tools → Core Debug Level para Info ou Verbose: assim o core do ESP32 imprime eventos de Wi-Fi e erros de TLS que o seu sketch nunca vê. No PlatformIO, use monitor_speed = 115200 e build_flags = -DCORE_DEBUG_LEVEL=4.
Passos 2–6: o sketch de diagnóstico
Grave este sketch antes de mexer no seu firmware real. Ele verifica cada camada em ordem e imprime exatamente onde parou.
#include <WiFi.h>
#include <PubSubClient.h>
const char* WIFI_SSID = "your-ssid";
const char* WIFI_PASS = "your-wifi-password";
const char* MQTT_HOST = "broker.connectmq.com";
const uint16_t MQTT_PORT = 1883;
const char* MQTT_USER = "username"; // nullptr se o broker permitir acesso anônimo
const char* MQTT_PASS = "password";
WiFiClient net;
PubSubClient mqtt(net);
String clientId;
bool check() {
Serial.println("\n[1] Wi-Fi");
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
unsigned long t0 = millis();
while (WiFi.status() != WL_CONNECTED && millis() - t0 < 20000) { delay(250); Serial.print('.'); }
if (WiFi.status() != WL_CONNECTED) {
Serial.printf("\nWi-Fi failed, status=%d\n", WiFi.status());
return false;
}
Serial.printf("\nIP %s gateway %s RSSI %d dBm\n", WiFi.localIP().toString().c_str(),
WiFi.gatewayIP().toString().c_str(), WiFi.RSSI());
Serial.println("[2] DNS");
IPAddress ip;
if (!WiFi.hostByName(MQTT_HOST, ip)) { Serial.println("DNS lookup failed"); return false; }
Serial.printf("%s -> %s\n", MQTT_HOST, ip.toString().c_str());
Serial.println("[3] TCP port");
if (!net.connect(ip, MQTT_PORT)) { Serial.println("TCP connect failed"); return false; }
Serial.println("TCP OK");
net.stop();
Serial.println("[4] MQTT CONNECT");
clientId = "esp32-" + WiFi.macAddress();
clientId.replace(":", "");
mqtt.setServer(MQTT_HOST, MQTT_PORT);
mqtt.setBufferSize(1024);
mqtt.setKeepAlive(30);
if (!mqtt.connect(clientId.c_str(), MQTT_USER, MQTT_PASS)) {
Serial.printf("MQTT failed, rc=%d\n", mqtt.state());
return false;
}
Serial.printf("MQTT OK as %s\n", clientId.c_str());
Serial.println("[5] PUBLISH");
String topic = "testmqtt/esp32-check/" + clientId + "/hello";
bool ok = mqtt.publish(topic.c_str(), "ESP32 connection test passed");
Serial.printf("publish to %s: %s\n", topic.c_str(), ok ? "OK" : "FAILED");
return ok;
}
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println(check() ? "\nALL CHECKS PASSED" : "\nCHECK FAILED - see the last step above");
}
void loop() {
mqtt.loop();
}Lendo os resultados
Códigos de status do Wi-Fi
status | Significado | Correção |
|---|---|---|
1 WL_NO_SSID_AVAIL | Rede não encontrada | Confira a grafia do SSID; garanta que ele transmite em 2,4 GHz |
4 WL_CONNECT_FAILED | Rejeitado, geralmente senha errada | Digite a senha de novo; redes só WPA3 podem exigir o modo misto WPA2/WPA3 |
6 WL_DISCONNECTED | Ainda tentando ou caiu | Aproxime a placa; RSSI abaixo de cerca de -80 dBm não é confiável |
Um bom RSSI fica mais ou menos entre -50 e -70 dBm. Se o Wi-Fi conecta, mas cai depois, veja desconexões de Wi-Fi e MQTT no ESP32.
DNS e TCP
DNS lookup failed significa que a placa não recebeu um servidor DNS utilizável do DHCP ou que o nome do host está errado. Verifique o mesmo nome a partir de um notebook na mesma rede com nslookup broker.connectmq.com. Como teste temporário você pode conectar pelo IP, mas não coloque isso em produção.
TCP connect failed (e rc=-2 no seu próprio firmware) significa que nada respondeu naquele host e porta. Causas comuns: rede de convidados ou firewall corporativo bloqueando a 1883, erro de digitação na porta ou uso da porta 8883 com um WiFiClient comum; portas TLS exigem WiFiClientSecure e um certificado de CA, como mostrado em MQTT sobre TLS no ESP32. Confirme a partir de um notebook no mesmo Wi-Fi com nc -vz broker.connectmq.com 1883.
Códigos de retorno do PubSubClient
rc | Constante | Causa comum |
|---|---|---|
| -4 | MQTT_CONNECTION_TIMEOUT | CONNACK não chegou a tempo ou o keep alive expirou |
| -3 | MQTT_CONNECTION_LOST | Conexão caiu; loop() não é chamado com frequência suficiente |
| -2 | MQTT_CONNECT_FAILED | Falha na conexão TCP (veja acima) |
| -1 | MQTT_DISCONNECTED | Desconexão limpa / nunca conectou |
| 1 | MQTT_CONNECT_BAD_PROTOCOL | O broker não aceita MQTT 3.1.1 |
| 2 | MQTT_CONNECT_BAD_CLIENT_ID | Client ID rejeitado (vazio, longo demais, caracteres inválidos) |
| 3 | MQTT_CONNECT_UNAVAILABLE | Broker indisponível |
| 4 | MQTT_CONNECT_BAD_CREDENTIALS | Usuário ou senha errados |
| 5 | MQTT_CONNECT_UNAUTHORIZED | Não autorizado: faltam credenciais ou o cliente não tem permissão |
Valores positivos são a resposta CONNACK do próprio broker, ou seja, a rede está ok e só resta a configuração. A referência de reason codes lista os códigos MQTT correspondentes, e PubSubClient não conecta aprofunda cada um deles.
publish() retorna false
O buffer padrão do PubSubClient tem 256 bytes e precisa comportar o pacote inteiro: cabeçalho, topic e payload. Um payload JSON de 200 bytes em um topic longo já passa disso, e publish() simplesmente retorna false sem nada chegar ao broker. Chame setBufferSize() antes de conectar; a calculadora de tamanho de pacote mostra o tamanho real de um pacote.
Passo 7: manter a conexão
mqtt.loop() precisa rodar com frequência, pelo menos várias vezes por intervalo de keep alive. Um delay(60000) no loop() ou uma leitura de sensor bloqueante garantem um rc=-3 depois de cerca de um período e meio de keep alive, porque o broker nunca recebe um PINGREQ. Troque delays longos por timers com millis() e reconecte com backoff em vez de travar em um loop while (!mqtt.connected()).
Confirme do lado do broker
Uma linha serial dizendo "OK" é a opinião do dispositivo. Verifique de forma independente: abra o cliente MQTT online, conecte-se ao mesmo broker e assine testmqtt/esp32-check/#. Reinicie a placa e a mensagem hello deve chegar em poucos segundos. No terminal, a mesma verificação é:
mosquitto_sub -h broker.connectmq.com -p 1883 \
-u "$MQTT_USER" -P "$MQTT_PASS" \
-t 'testmqtt/esp32-check/#' -vAguarde o seu ESP32 no navegador
A maioria dos problemas de MQTT no ESP32 nem são problemas de MQTT: são questões de Wi-Fi, DNS ou porta escondidas atrás de um único código de retorno. Teste uma camada por vez, leia o valor exato de status ou rc e confirme do lado do broker antes de passar para o seu firmware real. Para a configuração completa no Arduino, continue com MQTT no ESP32 com Arduino.
Perguntas frequentes
O que significa "failed, rc=-2" no ESP32?
É o MQTT_CONNECT_FAILED do PubSubClient: não foi possível abrir a conexão TCP com o broker. A causa está abaixo do MQTT: sem Wi-Fi, host ou porta errados, falha de DNS, firewall ou uma porta TLS usada com um WiFiClient comum.
Por que meu ESP32 conecta, mas publish() retorna false?
Na maioria das vezes a mensagem é maior que o buffer do PubSubClient, que por padrão tem 256 bytes incluindo topic e cabeçalho. Chame setBufferSize(1024) ou mais antes de conectar e verifique o retorno de cada publish.
O ESP32 suporta Wi-Fi de 5 GHz para MQTT?
O ESP32 clássico, o S2, o S3 e o C3 só têm rádio de 2,4 GHz, então não entram em uma rede só de 5 GHz. Chips mais novos, como o ESP32-C5, são dual band. Confira o seu módulo exato antes de depurar o MQTT.

