MQTT client.isConnected() always returns true

I use the Particle Boron to send data to my MQTT broker. The problem is that once the connection is established, the value of the connection status remains true so it does not try to reconnect despite the fact that the server stops the connection due to keepalive expired.

2022-02-16 16:37:33.498 [warning] <0.16958.4>@vmq_mqtt_fsm:connected:
449 client {[],<<"ParticleClient">>} 
with username <<"colcaIOT">> stopped due to keepalive expired

When I use client.disconnect(); then the connection status changes to false and a new connection is established.

Any idea why the function client.isConnected() returns true after the server is no longer connected?

This is the code that I am using to test the connection.

#include <MQTT.h>

String mqttUser = "colcaIOT";
String mqttPassword = "my_password";

char const *IOT_HOST = "test_server.com";

uint16_t messageid = 0;

void callback(char* topic, byte* payload, unsigned int length);


MQTT client( IOT_HOST, 28985, callback );
//MQTT client( IOT_HOST, 28985, 256, 30, callback );

  void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i=0; i< length;i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();
}

void setup() {
    Serial.begin(115200);
    Serial.println("Serial is set to 115200");
    client.connect("ParticleClient", mqttUser, mqttPassword);
    delay(500);    
}

void loop() {
  Serial.print("Client Connection State ..");
  Serial.printlnf(client.isConnected() ? "true" : "false"); 
if (client.isConnected()) {

   client.publish("GuestRoom/Data", "In the main loop QOS1" , MQTT::QOS1, &messageid);

       Serial.printf("The message id %u: ", messageid);

  delay(500);
}
else
    {
        Serial.println("Not Connected!!");
        client.connect("ParticleClient", mqttUser, mqttPassword);
        
        delay(500);
    }

delay(35000);
 
Serial.printlnf("Turning off MQTT..");
client.disconnect();
delay(500);
Serial.print("Client Connection State ..");
Serial.printlnf(client.isConnected() ? "true" : "false");
client.connect("ParticleClient", mqttUser, mqttPassword);

}

Since you are using the default non-threaded automatic mode, the loop function stops executing when the Particle cloud connection is lost, which is also probably when the MQTT connection is lost.

You should add:

SYSTEM_THREAD(ENABLED);

near the top, outside of any function, so loop will continue to run regardless of cloud connection status.

Thank you for the quick response.
After I added SYSTEM_THREAD(ENABLED) at the beginning of the code, there was no output to serial from the setup loop and the connection to the MQTT broker was not established.
I added a delay (3000), then I received the message to serial but still no connection to the broker.
I added a while loop and then a connection was established. However, just like before, the server timed out and the code continued to return true so the broker received only one message.

What am I missing?

See the revised code below.

#include <MQTT.h>

SYSTEM_THREAD(ENABLED);

String mqttUser = "colcaIOT";
String mqttPassword = "my_password";
char const *IOT_HOST = "test_server.com";

uint16_t messageid = 0;

void callback(char* topic, byte* payload, unsigned int length);


MQTT client( IOT_HOST, 28985, callback );
//MQTT client( IOT_HOST, 28985, 256, 30, callback );

  void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i=0; i< length;i++) {
    Serial.print((char)payload[i]);
  }
  Serial.println();
}

void setup() {
  delay(3000);
    Serial.begin(115200);//debug
    Serial.println("Serial is set to 115200");
    
    while (!client.isConnected()) {
    delay(500);
    client.connect("ParticleClient", mqttUser, mqttPassword);
    Serial.print(".");
  }
    delay(500);    
}

void loop() {

  Serial.print("Client Connection State ..");
  Serial.printlnf(client.isConnected() ? "true" : "false");
  
if (client.isConnected()) {
   client.publish("GuestRoom/Data", "In the main loop QOS1" , MQTT::QOS1, &messageid);

       Serial.printf("The message id is %u: ", messageid);
  delay(500);
}
else
    {
        Serial.println("Not Connected!!");
        client.connect("ParticleClient", mqttUser, mqttPassword);  
        delay(500);
    }

 delay(35000);

//Serial.printlnf("Turning off MQTT..");
//client.disconnect();
//delay(500);
//Serial.print("Client Connection State ..");
//Serial.printlnf(client.isConnected() ? "true" : "false");
//client.connect("ParticleClient", mqttUser, mqttPassword);

}

Try adding before the client.isConnected() line:

waitUntil(Cellular.ready);

It’s possible that the MQTT library does not deal with the situation where the underlying network is not ready yet. In automatic mode, setup and loop don’t run until you’ve connected to cellular, but in threaded mode they run immediately.

Hi,
try this one:

and consider to use millis() instead of any long delay() like this:

Thank you Rick and Arek,

I understand that the setup loop runs before the connection is established. This is the reason why I added the while loop.

I added the 4-th parameter so the line reads:

MQTT client( IOT_HOST, 28985, callback, true);

Arek, you are correct about the long delay. I used it for debugging the connection. I modified the code and now I use millis()

I still don’t understand why the function client.isConnected() always returns true despite the fact that the broker log indicates that the client is no longer connected.

So far the only workaround was to add client.disconnect(); and then reconnect.

Serial monitor opened successfully:
Serial is set to 115200
.........................................Client Connection State ..true
The message id is 2: Client Connection State ..true
The message id is 4: Client Connection State ..true
The message id is 6: Client Connection State ..true

Rick, I forgot to mention that I added waitUntil(Cellular.ready); in the setup loop. It eliminated the need for the while.

I added waitUntil(Cellular.ready); to the main loop but it did not make any difference. Still client.isConnected() always returns true.