knolleary / pubsubclient

A client library for the Arduino Ethernet Shield that provides support for MQTT.
http://pubsubclient.knolleary.net/
MIT License
3.79k stars 1.47k forks source link

MQTT Disconnect and Reconnects at very short intervals #372

Open anujmattoo opened 6 years ago

anujmattoo commented 6 years ago

I am using the pubsub client library to publish and to subscribe data to MQTT Broker. I am using Cloud MQTT free version which allows up to 10 connection limit. I don't find any issue with Cloud MQTT. I am connected to the broker using MQTT Lens without any disconnection. But ESP12E Disconnects and reconnects every 30-35 seconds.

I am using the demo example for ESP8266. What can be the issue?

knolleary commented 6 years ago

You are probably not using a unique client id, so getting kicked off by another client that is also using that id.

anujmattoo commented 6 years ago

Can you please explain that in detail. I don't understand it exactly. But I see, when I use only one ESP to connect to MQTT it doesn't get disconnected. When multiple devices are connected to the same broker with same host address, port and username and password and same topic, they start connecting and disconnecting. How can this be resolve?

knolleary commented 6 years ago

Every client connecting to a broker must have a unique client id. This is the string you pass to the connect function: https://pubsubclient.knolleary.net/api.html#connect1

If you have multiple ESP using the same client id they will start kicking each other off.

anujmattoo commented 6 years ago

boolean connect (clientID, username, password) I am using this function and was using same client ID for all the devices. I will fix it and will keep unique ID for everyone.

PaulWieland commented 6 years ago

I am using unique client IDs yet I am still having this issue. Using MOSCA as my mqtt broker, here's the log which shows the same client connect and disconnect over and over.

The sketch is really basic, its a copy of the sample ESP sketch and it reads and publishes a temperature value with a 1 minute delay in the loop.

{"pid":59321,"hostname":"mini.local","name":"mosca","level":30,"time":1520794383902,"msg":"client connected","client":"woodstoveClient","v":1}
{"pid":59321,"hostname":"mini.local","name":"mosca","level":30,"time":1520794406485,"msg":"keepalive timeout","client":"woodstoveClient","v":1}
{"pid":59321,"hostname":"mini.local","name":"mosca","level":30,"time":1520794406485,"msg":"closed","client":"woodstoveClient","v":1}
{"pid":59321,"hostname":"mini.local","name":"mosca","level":30,"time":1520794418720,"msg":"unsubscribed","topic":"home/#","client":"arduinoClient","v":1}
{"pid":59321,"hostname":"mini.local","name":"mosca","level":30,"time":1520794418720,"msg":"closed","client":"arduinoClient","v":1}
{"pid":59321,"hostname":"mini.local","name":"mosca","level":30,"time":1520794418745,"msg":"client connected","client":"arduinoClient","v":1}
{"pid":59321,"hostname":"mini.local","name":"mosca","level":30,"time":1520794418759,"msg":"subscribed to topic","topic":"home/#","qos":0,"client":"arduinoClient","v":1}
{"pid":59321,"hostname":"mini.local","name":"mosca","level":30,"time":1520794443991,"msg":"client connected","client":"woodstoveClient","v":1}
{"pid":59321,"hostname":"mini.local","name":"mosca","level":30,"time":1520794466570,"msg":"keepalive timeout","client":"woodstoveClient","v":1}
{"pid":59321,"hostname":"mini.local","name":"mosca","level":30,"time":1520794466573,"msg":"closed","client":"woodstoveClient","v":1}
{"pid":59321,"hostname":"mini.local","name":"mosca","level":30,"time":1520794504075,"msg":"client connected","client":"woodstoveClient","v":1}
{"pid":59321,"hostname":"mini.local","name":"mosca","level":30,"time":1520794526649,"msg":"keepalive timeout","client":"woodstoveClient","v":1}
{"pid":59321,"hostname":"mini.local","name":"mosca","level":30,"time":1520794526649,"msg":"closed","client":"woodstoveClient","v":1}

Here's the sketch:

/*
 *  This sketch sends data via HTTP GET requests to data.sparkfun.com service.
 *
 *  You need to get streamId and privateKey at data.sparkfun.com and paste them
 *  below. Or just customize this script to talk to other HTTP servers.
 *
 */

#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include "max6675.h"

/* Settings for the MAX6675 thermocouple board */
int thermoDO = D3;
int thermoCS = D4;
int thermoCLK = D2;

MAX6675 thermocouple(thermoCLK, thermoCS, thermoDO);

/* Settings for the ESP8266 version of this sketch  */
const char* ssid = "*****************";
const char* password = "*************";
const char* mqtt_server = "10.0.1.2"; // Mac mini
long lastReconnectAttempt = 0;

WiFiClient espClient;
PubSubClient mqttClient(espClient);

void setup_wifi() {
  delay(10);
  // We start by connecting to a WiFi network
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);

//  WiFi.setSleepMode(WIFI_NONE_SLEEP); // Suggested to help with led flickering issue
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}

boolean reconnect() {
  Serial.println("Reconnecting to mqtt");
  if (mqttClient.connect("woodstoveClient")) {
    // Once connected, publish an announcement...
    mqttClient.publish("home/Living Room/Woodstove/state","\"online\"");
  }
  return mqttClient.connected();
}

void setup() {
  Serial.begin(115200);

  Serial.println("Starting up");
  /* ESP8266 Stuff */
  setup_wifi();

  Serial.println("Connect to mqtt server");
  mqttClient.setServer(mqtt_server, 1883);
//  mqttClient.setCallback(mqttCallback);

  lastReconnectAttempt = 0;
  /* End ESP8266 Stuff */

  Serial.println("Setup done");
}

void loop() {
  if (!mqttClient.connected()) {
    Serial.println("mqtt NOT connected");
    long now = millis();
    if (now - lastReconnectAttempt > 5000) {
      lastReconnectAttempt = now;
      // Attempt to reconnect
      if (reconnect()) {
        lastReconnectAttempt = 0;
      }
    }
  } else {
    // Client connected
    mqttClient.loop();
  }

  float ftemp = thermocouple.readFahrenheit();

  mqttClient.publish("home/Living Room/Woodstove/temp", String(ftemp).c_str());
  Serial.println("Temp: " + (String) thermocouple.readFahrenheit());

  delay(60000);
}
PaulWieland commented 6 years ago

I solved the above problem. Unbeknownst to me, the use of delay() causes the mqtt connection to break. I switched the code to use a non blocking interval counter and now it works fine.

The problem I'm going to have is with another project that uses the emonlib for reading current sensors. The execution time of that code in my loop is causing the mqtt connection to disconnect even though there are no delay() calls in it.

Hmm...

dattasaurabh82 commented 5 years ago

I've a similar issue. My hardware setup: 2 MQTT clients (1 Arduino Mega + 1 Arduino Ethernet shield and Adafruit feather MO with ethernet wing (I've also tried another Arduino Mega and ethernet shield combo)). They all have unique Client IDs. Using non blocking reconnecting code as well and DHCP IP as well. My mosquitto broker is latest and is running from a latest raspberry Pi 3B+ . I've even tried with 3 different brokers from the same machine with 3 different ports (and single port). The cleints keep on reconnecting at some intervals but works like charm when only one is let to run. Other multiple MQTT software clients from different machines have no problem.

One reason may be my router has some issues to support or else I don't know.

HELP :( .. Can buy you a coffee. :)

dattasaurabh82 commented 5 years ago

Silly mistake. Had same MAC addresses.

Cloolalang commented 5 years ago

Check your ISP, I have two NodeMCU's with identical sketches connected to Adafruit MQTT, one on a capped 10mb fibre line, the other on uncapped same ISP 10mb fibre. The one on the capped (200G/m) hardly ever disconnects from the Ada MQTT server, the one on the capped line disconnects/reconnects up to 50 times per day.. I put this down to some sort of ISP shaping/thottling so perhaps test on a local MQTT server.

Cloolalang commented 5 years ago

Check your ISP, I have two NodeMCU's with identical sketches connected to Adafruit MQTT, one on a capped 10mb fibre line, the other on uncapped same ISP 10mb fibre. The one on the capped (200G/m) hardly ever disconnects from the Ada MQTT server, the one on the capped line disconnects/reconnects up to 50 times per day.. I put this down to some sort of ISP shaping/thottling so perhaps test on a local MQTT server.

I have been testing through a tethered 3g smartphone, only MQTT Pings and publishing every 10 mins or so ...tons of connect/disconnect cycles per hour..OMG!

Edited Adafruit_MQTT.h :

_#define CONNECT_TIMEOUT_MS 6000

define PUBLISH_TIMEOUT_MS 6000 //set from 500 to 1000 to see if its better for discons/hr

define PING_TIMEOUT_MS 6000 //set from 500 to 1000 to see if its better for discons/hr

define SUBACK_TIMEOUTMS 6000 //set from 500 to 1000 to see if its better for discons/hr

Yes 6000ms! Now it works really well, hardly any connect/disconnect cycles,,

eadmaster commented 5 years ago

similar problem here, thingsboard keeps disconnecting after some messages sent successfully, while thingspeak works fine... (my sketch is here)

Is there any way to check the error message when a publish fail?

pcbtborges commented 4 years ago

Every client connecting to a broker must have a unique client id. This is the string you pass to the connect function: https://pubsubclient.knolleary.net/api.html#connect1

If you have multiple ESP using the same client id they will start kicking each other off.

Hi, I have an ESP32 with temp and humidity sensors sending data to Ubidots temp and humidity variables. I have a second esp32 observing temp and it keeps reconecting. What do I have to do to solve this problem? I am using the same MQTT_CLIENT_NAME and same TOKEN.

############################################################## Just found the solution. Defined an unique MQTT_CLIENT_NAME on the client script.

Thanks in advance Paulo

knolleary commented 4 years ago

What do I have to do to solve this problem? I am using the same MQTT_CLIENT_NAME and same TOKEN.

Exactly what I say in my comment youve quoted, don't use the same name.

pcbtborges commented 4 years ago

Hi, thanks, I just coded to use the MAC address of the ESP32 as MQTT_CLIENT_NAME and it sorted out the problem. Thanks again

ashutosh-akss commented 4 years ago

I am using mac address of esp32 as client id but still i see connection / disconnection loop

below are my code samples

// code to init

client.setServer(mqtt_server, mqttPort);
client.setCallback(callbackFunction);

reconnet function code

void reconnect(){
  while (!client.connected())
  {
    Serial.print("Attempting MQTT connection...");
    if (client.connect(mac, "username", "password"))
    {
      Serial.println("connected");
      client.subscribe(userId); // write your unique ID here
      client.subscribe(mac);
      client.subscribe(storeId);
    }
    else
    {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      delay(5000);
    }
  }
}

Function which gets called in void loop()

void startMQTT() {
  // MQTT Reconnection Logic
  if (!client.connected())
  {
    reconnect();
  }
  client.loop();
}

Also I have just 1 device ( no multiple devices so no conflcit of name )

pcbtborges commented 4 years ago

Hi, the mac address method I used guarantees that I do not have to worry about duplicated MQTT names or some sort of different names across reboots. It does not address the connection / disconnection loop problem, never had this problem.

Regards Paulo

nico1495 commented 4 years ago

Hi, I have a sketch with pcb board relays and physical buttons on the board. I can turn on the relays with physical buttons or software with the MQTT protocol. When the mqtt server is turned off, the physical buttons of the pcb board also stop working. Is there a way to create a bypass in the sketch and make sure that when the mqtt server is off, the physical buttons count to work?

somanathtv commented 4 years ago

Every client connecting to a broker must have a unique client id. This is the string you pass to the connect function: https://pubsubclient.knolleary.net/api.html#connect1

If you have multiple ESP using the same client id they will start kicking each other off.

you saved my day!! Thanks a lot.

cefranco76 commented 4 years ago

You are probably not using a unique client id, so getting kicked off by another client that is also using that id.

You have just saved my day, thank you!!!!

ssp5zone commented 3 years ago

If you are still having this issues even when using only a single client, I just found that some MQTT providers (like Adafruit, Node Red etc.) keep the connection alive until terminated manually.

Hence if you have had a power failure or a sudden interruption while being hooked up on the queue, chances are the connection was not marked closed. Try with a different device id or restart your local node red instance.

riotgibbon commented 3 years ago

Every client connecting to a broker must have a unique client id. This is the string you pass to the connect function: https://pubsubclient.knolleary.net/api.html#connect1

If you have multiple ESP using the same client id they will start kicking each other off.

this is the new "have you tried turning it off and on again" - I was ready to throw my ESP32 board out of the window!

wiserdivisor commented 3 years ago

Hello creators,

I was using an ESP32 and I have also had the same issue. Connecting and Disconnecting every few seconds. After reading the above posts and following the beautiful documentation written by @knolleary I have discovered that there's a couple of reasons for this behavior :

  1. Non-Unique Client-ID
  2. Not adding client.loop() in your code.

My client ID was unique but I had not added the client.loop() part. This caused my connection to break and reconnect multiple times. Read this : https://pubsubclient.knolleary.net/api#loop

Hope this helps!

EDIT :: You have to add the client.loop() in the main loop of your code. Something like this :

void loop(){
if( !client.connected() ){ reconnect(); }
client.loop();

// And the rest of your code 👍 
}