Fix PubSubClient “failed, rc=-2” (MQTT connect failed)
rc=-2 means the TCP connection to the broker failed before MQTT even started: wrong IP or port, broker not running, localhost, or a PC firewall.
Community draft — report a mistake
What you see
WiFi connected
IP address: 192.168.1.37
Attempting MQTT connection...failed, rc=-2 try again in 5 seconds
Attempting MQTT connection...failed, rc=-2 try again in 5 seconds
Attempting MQTT connection...failed, rc=-2 try again in 5 secondsWiFi connected IP address: 192.168.1.37 Attempting MQTT connection...failed, rc=-2 try again in 5 seconds Attempting MQTT connection...failed, rc=-2 try again in 5 seconds Attempting MQTT connection...failed, rc=-2 try again in 5 seconds
PubSubClient’s state() (printed as rc=) tells you how far the connection got. -2 is the earliest possible failure: the board could not even open a TCP connection to the broker’s address and port. Fix the address, the broker’s listener, or the firewall — not the MQTT code.
A minimal mosquitto.conf for a home network:
listener 1883
allow_anonymous true
Restart Mosquitto after editing it. For anything beyond your own LAN, use a password file instead of anonymous access.
Quick fix — try in order
- 1
Use the broker’s LAN IP, not localhost
localhostor127.0.0.1on the ESP32 means the ESP32 itself. Put the PC/Raspberry Pi’s address, e.g.192.168.1.10(find it withipconfig/hostname -I). - 2
Let Mosquitto listen on the network
Mosquitto 2.x listens only on localhost by default. Add
listener 1883andallow_anonymous true(or a password file) to mosquitto.conf and restart it. - 3
Open port 1883 in the PC firewall
Windows Defender Firewall blocks incoming 1883 by default. Add an inbound rule for TCP 1883, or test with the firewall off for a minute.
- 4
Check the port and protocol
1883 = plain MQTT with
WiFiClient. 8883 = TLS and needsWiFiClientSecure. Cloud brokers (HiveMQ Cloud, EMQX Cloud) usually only allow 8883. - 5
Test from another device
Use MQTT Explorer or an MQTT app on your phone, on the same Wi-Fi, to connect to the same IP and port. If that fails too, the board is not the problem.
Diagnostic sketch
// MQTT check for the ESP32 (PubSubClient): tests the TCP port first, then the MQTT login,
// and prints what each rc= code means. Also shows why a long publish() returns false.
#include <WiFi.h>
#include <PubSubClient.h>
#include "fr_settings.h"
const char* WIFI_SSID = "YOUR_WIFI_NAME";
const char* WIFI_PASS = "YOUR_WIFI_PASSWORD";
const char* MQTT_HOST = "192.168.1.10"; // your broker's IP or name - never "localhost"
const uint16_t MQTT_PORT = 1883; // plain MQTT; 8883 is TLS and needs WiFiClientSecure
const char* MQTT_USER = ""; // leave empty if the broker allows anonymous
const char* MQTT_PASS = "";
// Filled in from the FlashReady flasher over USB (kept on the board), or type the values above.
FrSetting SETTINGS[] = {
{"wifi_ssid", &WIFI_SSID, true},
{"wifi_pass", &WIFI_PASS, true},
{"mqtt_host", &MQTT_HOST, true},
{"mqtt_user", &MQTT_USER, false},
{"mqtt_pass", &MQTT_PASS, false},
};
WiFiClient net;
PubSubClient mqtt(net);
const char* stateName(int s) {
switch (s) {
case MQTT_CONNECTION_TIMEOUT: return "-4 timeout: broker stopped answering";
case MQTT_CONNECTION_LOST: return "-3 connection lost";
case MQTT_CONNECT_FAILED: return "-2 TCP connect failed: wrong IP/port, broker down or firewall";
case MQTT_DISCONNECTED: return "-1 disconnected";
case MQTT_CONNECTED: return "0 connected";
case MQTT_CONNECT_BAD_PROTOCOL: return "1 broker refused the protocol version";
case MQTT_CONNECT_BAD_CLIENT_ID: return "2 client ID rejected";
case MQTT_CONNECT_UNAVAILABLE: return "3 broker unavailable";
case MQTT_CONNECT_BAD_CREDENTIALS: return "4 bad user name or password";
case MQTT_CONNECT_UNAUTHORIZED: return "5 not authorised: login needed or not allowed";
}
return "unknown";
}
void setup() {
Serial.begin(115200);
frSettingsBegin(SETTINGS);
if (!*WIFI_SSID || !*MQTT_HOST) {
Serial.println("MQTT check off - no Wi-Fi name or broker address set. Flash from flashready.in or type them into the code.");
return;
}
WiFi.begin(WIFI_SSID, WIFI_PASS);
unsigned long t0 = millis();
while (WiFi.status() != WL_CONNECTED && millis() - t0 < 20000) delay(300);
if (WiFi.status() != WL_CONNECTED) {
Serial.println("Wi-Fi did not connect - fix that first.");
return;
}
Serial.printf("Wi-Fi OK, IP %s\n", WiFi.localIP().toString().c_str());
// Step 1: can we reach the port at all? If not, MQTT will report rc=-2.
WiFiClient probe;
if (probe.connect(MQTT_HOST, MQTT_PORT, 5000)) {
Serial.printf("TCP %s:%u open\n", MQTT_HOST, MQTT_PORT);
probe.stop();
} else {
Serial.printf("TCP %s:%u closed - broker not running, wrong address, or a firewall (rc=-2)\n", MQTT_HOST, MQTT_PORT);
return;
}
// Step 2: MQTT login.
mqtt.setServer(MQTT_HOST, MQTT_PORT);
mqtt.setBufferSize(512); // default 256 bytes: longer messages make publish() return false
String id = "flashready-" + String((uint32_t)ESP.getEfuseMac(), HEX); // unique per board
bool ok = *MQTT_USER ? mqtt.connect(id.c_str(), MQTT_USER, MQTT_PASS) : mqtt.connect(id.c_str());
Serial.printf("MQTT connect as %s: rc=%s\n", id.c_str(), stateName(mqtt.state()));
if (!ok) return;
// Step 3: publish a 300-byte message - fits only because of setBufferSize(512).
char msg[301];
memset(msg, 'x', 300);
msg[300] = 0;
bool sent = mqtt.publish("flashready/test", msg);
Serial.printf("publish 300 bytes: %s (buffer %u bytes)\n", sent ? "OK" : "FAILED", (unsigned)mqtt.getBufferSize());
}
void loop() {
mqtt.loop();
delay(10);
}// MQTT check for the ESP32 (PubSubClient): tests the TCP port first, then the MQTT login,
// and prints what each rc= code means. Also shows why a long publish() returns false.
#include <WiFi.h>
#include <PubSubClient.h>
#include "fr_settings.h"
const char* WIFI_SSID = "YOUR_WIFI_NAME";
const char* WIFI_PASS = "YOUR_WIFI_PASSWORD";
const char* MQTT_HOST = "192.168.1.10"; // your broker's IP or name - never "localhost"
const uint16_t MQTT_PORT = 1883; // plain MQTT; 8883 is TLS and needs WiFiClientSecure
const char* MQTT_USER = ""; // leave empty if the broker allows anonymous
const char* MQTT_PASS = "";
// Filled in from the FlashReady flasher over USB (kept on the board), or type the values above.
FrSetting SETTINGS[] = {
{"wifi_ssid", &WIFI_SSID, true},
{"wifi_pass", &WIFI_PASS, true},
{"mqtt_host", &MQTT_HOST, true},
{"mqtt_user", &MQTT_USER, false},
{"mqtt_pass", &MQTT_PASS, false},
};
WiFiClient net;
PubSubClient mqtt(net);
const char* stateName(int s) {
switch (s) {
case MQTT_CONNECTION_TIMEOUT: return "-4 timeout: broker stopped answering";
case MQTT_CONNECTION_LOST: return "-3 connection lost";
case MQTT_CONNECT_FAILED: return "-2 TCP connect failed: wrong IP/port, broker down or firewall";
case MQTT_DISCONNECTED: return "-1 disconnected";
case MQTT_CONNECTED: return "0 connected";
case MQTT_CONNECT_BAD_PROTOCOL: return "1 broker refused the protocol version";
case MQTT_CONNECT_BAD_CLIENT_ID: return "2 client ID rejected";
case MQTT_CONNECT_UNAVAILABLE: return "3 broker unavailable";
case MQTT_CONNECT_BAD_CREDENTIALS: return "4 bad user name or password";
case MQTT_CONNECT_UNAUTHORIZED: return "5 not authorised: login needed or not allowed";
}
return "unknown";
}
void setup() {
Serial.begin(115200);
frSettingsBegin(SETTINGS);
if (!*WIFI_SSID || !*MQTT_HOST) {
Serial.println("MQTT check off - no Wi-Fi name or broker address set. Flash from flashready.in or type them into the code.");
return;
}
WiFi.begin(WIFI_SSID, WIFI_PASS);
unsigned long t0 = millis();
while (WiFi.status() != WL_CONNECTED && millis() - t0 < 20000) delay(300);
if (WiFi.status() != WL_CONNECTED) {
Serial.println("Wi-Fi did not connect - fix that first.");
return;
}
Serial.printf("Wi-Fi OK, IP %s\n", WiFi.localIP().toString().c_str());
// Step 1: can we reach the port at all? If not, MQTT will report rc=-2.
WiFiClient probe;
if (probe.connect(MQTT_HOST, MQTT_PORT, 5000)) {
Serial.printf("TCP %s:%u open\n", MQTT_HOST, MQTT_PORT);
probe.stop();
} else {
Serial.printf("TCP %s:%u closed - broker not running, wrong address, or a firewall (rc=-2)\n", MQTT_HOST, MQTT_PORT);
return;
}
// Step 2: MQTT login.
mqtt.setServer(MQTT_HOST, MQTT_PORT);
mqtt.setBufferSize(512); // default 256 bytes: longer messages make publish() return false
String id = "flashready-" + String((uint32_t)ESP.getEfuseMac(), HEX); // unique per board
bool ok = *MQTT_USER ? mqtt.connect(id.c_str(), MQTT_USER, MQTT_PASS) : mqtt.connect(id.c_str());
Serial.printf("MQTT connect as %s: rc=%s\n", id.c_str(), stateName(mqtt.state()));
if (!ok) return;
// Step 3: publish a 300-byte message - fits only because of setBufferSize(512).
char msg[301];
memset(msg, 'x', 300);
msg[300] = 0;
bool sent = mqtt.publish("flashready/test", msg);
Serial.printf("publish 300 bytes: %s (buffer %u bytes)\n", sent ? "OK" : "FAILED", (unsigned)mqtt.getBufferSize());
}
void loop() {
mqtt.loop();
delay(10);
}Why it happens
What exactly is rc=-2?
MQTT_CONNECT_FAILED in PubSubClient: client.connect() could not open a TCP socket to host:port. No MQTT packet was sent, so user name and password are not involved yet.
Phone on mobile data can reach my broker, the ESP32 can’t
Guest Wi-Fi networks and some routers isolate clients (AP isolation). The board and broker must be on the same LAN, or the broker must be reachable from the internet.
It connects, then returns -2 after hours
The broker’s IP changed (DHCP). Give the Raspberry Pi/PC a reserved IP in the router, or use a public broker by name.
Other codes
-4 timeout, -3 lost, -1 disconnected, 1–3 protocol/ID/unavailable, 4 bad credentials, 5 not authorised.
Board-specific notes
ESP8266: Same codes. Use WiFiClient from ESP8266WiFi.h; TLS (8883) on the ESP8266 needs BearSSL::WiFiClientSecure and lots of RAM.
ESP32: The diagnostic below tests the TCP port on its own before MQTT, so you can tell a network problem from a login problem.
Project: A complete working node: MQTT sensor node.
Did this fix it?
Thanks — that helps us keep this page right.