Quando o seu ESP32 não conecta a um broker MQTT, a causa quase sempre é um de uma dúzia de problemas previsíveis, da banda de Wi-Fi errada a um cliente TLS em uma porta sem criptografia. Este guia mostra uma ordem sistemática de verificações, explica o que significa cada código de erro do PubSubClient e mostra como confirmar cada correção contra o broker.connectmq.com.
Comece pelo código de erro
Antes de mudar qualquer coisa, descubra onde a conexão falha. O PubSubClient expõe o motivo por meio de client.state(). Imprima-o toda vez que connect() retornar false:
if (!mqtt.connect(clientId.c_str())) {
Serial.printf("MQTT connect failed, state=%d\n", mqtt.state());
}
state() |
Constante | O que geralmente significa |
|---|---|---|
| -4 | MQTT_CONNECTION_TIMEOUT |
O servidor não respondeu dentro do período de keepalive |
| -3 | MQTT_CONNECTION_LOST |
A conexão caiu depois de estabelecida |
| -2 | MQTT_CONNECT_FAILED |
A conexão TCP ou TLS nem chegou a ser aberta |
| -1 | MQTT_DISCONNECTED |
O cliente está desconectado de forma limpa |
| 1 | MQTT_CONNECT_BAD_PROTOCOL |
O broker não aceita a versão do protocolo |
| 2 | MQTT_CONNECT_BAD_CLIENT_ID |
Client ID rejeitado |
| 3 | MQTT_CONNECT_UNAVAILABLE |
Broker indisponível |
| 4 | MQTT_CONNECT_BAD_CREDENTIALS |
Usuário ou senha incorretos |
| 5 | MQTT_CONNECT_UNAUTHORIZED |
Cliente não autorizado |
Essa divisão é útil: códigos negativos indicam que o problema está abaixo do MQTT (rede, DNS, TCP, TLS), e códigos positivos indicam que o broker respondeu e recusou você. Um -2 leva você às soluções 1–6; um 4 ou 5, às soluções 7–9.
Primeiro, prove que o lado do broker funciona
Descarte o broker antes de mexer no firmware. Em um computador na mesma rede, execute:
mosquitto_sub -h broker.connectmq.com -p 1883 -i pc-test-4821 -t 'connectmq/demo/4821/#' -v
Em um segundo terminal:
mosquitto_pub -h broker.connectmq.com -p 1883 -i pc-pub-4821 -t connectmq/demo/4821/hello -m test
Se a mensagem chegar, o broker e o caminho pela internet funcionam, e o problema está no ESP32 ou no segmento de rede dele. Se ela também não chegar no computador, verifique primeiro o firewall da sua rede. A página do servidor MQTT público (gratuito) lista as portas e os limites que você pode testar.
Soluções no nível da rede (códigos de estado negativos)
1. Garanta que o Wi-Fi esteja realmente conectado em 2,4 GHz
O ESP32 (exceto variantes mais novas com suporte a 5 GHz) só conversa com redes de 2,4 GHz. Um roteador dual-band com um único SSID para as duas bandas normalmente funciona, mas um SSID só de 5 GHz nunca vai funcionar. Verifique também a ordem das operações: chamar mqtt.connect() antes de WiFi.status() == WL_CONNECTED sempre falha com -2. Imprima WiFi.localIP() e WiFi.RSSI(); um sinal mais fraco que cerca de -80 dBm leva a falhas intermitentes que parecem problemas do broker.
2. Verifique a resolução DNS
Se o hostname não resolve, a conexão TCP nunca começa. Teste diretamente:
IPAddress ip;
if (WiFi.hostByName("broker.connectmq.com", ip)) {
Serial.println(ip);
} else {
Serial.println("DNS lookup failed");
}
Uma falha aqui aponta para o servidor DNS fornecido pelo DHCP ou para uma configuração de IP estático sem DNS. Ao usar WiFi.config() para um endereço estático, passe também o gateway e um servidor DNS.
3. Combine a porta com a classe do cliente
Este é, isoladamente, o erro mais comum. O WiFiClient fala TCP simples e deve usar a porta 1883. O WiFiClientSecure fala TLS e deve usar a porta 8883. Misturá-los produz um -2 ou um -4 sem nenhuma outra pista.
| Classe do cliente | Porta | Transporte |
|---|---|---|
WiFiClient |
1883 | MQTT simples sobre TCP |
WiFiClientSecure |
8883 | MQTT sobre TLS |
| Bibliotecas WebSocket | 8083 / 8084 (caminho /mqtt) |
MQTT sobre WebSocket / WebSocket seguro |
O PubSubClient não fala WebSocket, então as portas 8083 e 8084 são apenas para clientes de navegador e bibliotecas com suporte a WebSocket.
4. Corrija a validação do certificado TLS
Com WiFiClientSecure, um certificado de CA ausente ou errado faz o handshake falhar silenciosamente. O ConnectMQ usa um certificado Let's Encrypt, então carregue a raiz ISRG Root X1 com setCACert(). Para confirmar que o TLS é o culpado, chame temporariamente secureClient.setInsecure(): se a conexão passar a funcionar de repente, sua CA está errada. Não leve setInsecure() para produção. Leia o erro TLS com secureClient.lastError(buf, sizeof(buf)) para obter detalhes e veja ESP32 MQTT com SSL/TLS para uma configuração segura completa.
5. Dê ao TLS memória e tempo suficientes
Handshakes TLS precisam de dezenas de kilobytes de heap. Se o seu sketch aloca buffers grandes, dados de imagem ou vários clientes seguros, o handshake falha com -2. Imprima ESP.getFreeHeap() logo antes de conectar. Verifique também o relógio: a validação do certificado compara datas, e algumas bibliotecas exigem uma hora válida. Sincronizar o NTP com configTime() antes de conectar evita toda uma classe de falhas intrigantes.
6. Verifique se há portas bloqueadas no caminho de rede
Wi-Fi de visitantes, redes corporativas e alguns hotspots móveis bloqueiam as portas 1883 ou 8883 de saída. Se o teste no computador acima só funcionou em outra rede, experimente o ESP32 em um hotspot de celular. Se ele conectar ali, a rede original está filtrando as portas MQTT e você precisa que o administrador de rede as libere.
Soluções no nível do broker (códigos de estado positivos)
7. Use um client ID não vazio e único
Todo cliente precisa do próprio client ID. Dois dispositivos gravados com o mesmo ID fixo no código vão derrubar um ao outro do broker repetidamente, o que parece desconexões aleatórias com estado -3. Um client ID vazio também é recusado por muitos brokers, incluindo o servidor MQTT público broker.connectmq.com. Derive o ID do MAC do chip:
String clientId = "esp32-" + String((uint32_t)ESP.getEfuseMac(), HEX);
8. Use exatamente as credenciais e o client ID do portal
Em um broker ConnectMQ privado, o client ID é vinculado às credenciais emitidas. Se você inventar seu próprio client ID usando o usuário e a senha gerados, o broker rejeita a conexão com o código 5, mesmo que a senha esteja correta. Copie os três valores do portal: YOUR_CLIENT_ID, YOUR_USERNAME, YOUR_PASSWORD. Atenção a espaços invisíveis no final ao colar no código.
9. Pare as tempestades de reconexão antes que você seja bloqueado
Um loop de reconexão sem atraso pode atingir o broker com várias tentativas de conexão por segundo. Com credenciais erradas isso é especialmente prejudicial, porque o ConnectMQ bloqueia endereços IP após repetidas falhas de login como parte da sua proteção contra flood. Espere entre as tentativas e aumente o atraso após cada falha:
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);
}
}
A estrutura não bloqueante também mantém o loop() responsivo. O guia de reconexão MQTT no ESP32 se aprofunda em uma lógica de reconexão robusta.
Soluções para conexões que caem depois de conectar
10. Chame mqtt.loop() com frequência suficiente
O PubSubClient só envia pings de keepalive e lê os pacotes recebidos dentro de loop(). Um delay() longo ou uma leitura de sensor bloqueante maior que o intervalo de keepalive (15 segundos por padrão) faz o broker achar que o cliente morreu, e você vê -3 ou -4. Substitua delays longos por temporizadores com millis() ou aumente o keepalive com mqtt.setKeepAlive(30). Se a placa também reinicia, leia sobre resets do task watchdog do ESP32 em projetos MQTT.
11. Aumente o buffer para mensagens grandes
O buffer de pacotes padrão do PubSubClient é de 256 bytes, incluindo tópico e cabeçalho. Uma publicação maior falha em silêncio: publish() retorna false, e uma mensagem recebida grande demais é descartada. Chame mqtt.setBufferSize(1024) antes de conectar, dimensionado para a sua maior mensagem.
12. Respeite os limites do servidor MQTT público
O servidor MQTT público broker.connectmq.com permite 64 KB por mensagem, 10 mensagens por segundo por cliente, 10 inscrições por cliente e até 8 níveis de tópico. Um sketch que publica em um loop apertado ou se inscreve em muitos tópicos pode ser desconectado por excedê-los. Limite a taxa de publicação, use uma inscrição com curinga em vez de muitas individuais e mantenha tópicos como connectmq/demo/4821/room/temp curtos.
Um sketch de diagnóstico
Grave isto quando estiver travado. Ele reporta cada etapa separadamente, para que você veja exatamente onde a cadeia se rompe:
#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(); }
Mantenha o mosquitto_sub rodando em connectmq/demo/4821/# e você verá a mensagem de diagnóstico no instante em que tudo funcionar.
Quando funciona no servidor MQTT público, mas não em produção
A maioria das falhas em produção se resume às soluções 3, 4 e 8: mudar para a porta 8883 sem mudar para WiFiClientSecure, esquecer o certificado da CA ou usar um client ID inventado com credenciais do portal. Mude uma coisa de cada vez e continue imprimindo o código de erro. O tutorial de IoT com ESP32 mostra um projeto completo que conecta sem problemas nos dois modos, e o que um broker MQTT faz explica por que o broker recusa certas conexões.
Coloque seu ESP32 online com o ConnectMQ
Assim que o dispositivo conectar ao servidor MQTT público, mova-o para um broker privado no plano Serverless gratuito em app.connectmq.com, sem cartão de crédito. O cliente MQTT no navegador e o explorador de tópicos ao vivo do portal facilitam confirmar o tráfego tanto do lado do dispositivo quanto do lado do servidor. O ConnectMQ é um broker MQTT gerenciado com TLS na 8883 pronto para uso, então o único certificado de que você precisa no ESP32 é a raiz pública da Let's Encrypt.
Perguntas frequentes
O que significa rc=-2 no PubSubClient?
Significa que a conexão TCP ou TLS não pôde ser aberta, então o broker nunca viu um pacote MQTT. Verifique o Wi-Fi, o DNS, a combinação de porta e classe do cliente, o certificado da CA e o heap livre.
Por que meu ESP32 conecta e desconecta a cada poucos segundos?
Normalmente outro dispositivo está usando o mesmo client ID, ou mqtt.loop() não é chamado com frequência suficiente para enviar os pings de keepalive. Dê a cada dispositivo um ID único e remova delays longos que bloqueiam.
Posso usar as portas 8083 ou 8084 com o PubSubClient?
Não. Essas são portas WebSocket, e o PubSubClient só fala MQTT sobre TCP puro ou TLS. Use 1883 com WiFiClient ou 8883 com WiFiClientSecure.
Por que minhas credenciais são rejeitadas mesmo com a senha correta?
Em um broker privado com credenciais emitidas, o client ID é vinculado a elas. Use exatamente o client ID mostrado no portal junto com o usuário e a senha gerados.



