MQTT Library and SEMI_AUTOMATIC

Hello

I will use my Photon to control the temperature of the Whirlpool.

WLAN isn’t 100% stable there, so it should run also if there is no WLAN.
I also use MQTT and OpenHAB for adjustment.

In Automatic (Normal Mode) everything is fine.

But I tried to use SEMI_AUTOMATIC, so that the code will run (and use default/last values) for regulation and reconnect after WLAN is there again.

The Problem is, that the Photon goes to “Cloud not connected” (Green Breathing) if I try to connect to the MQTT Server.

SYSTEM_MODE(SEMI_AUTOMATIC);

void connect_MQTT(){
    if (!client.isConnected()) {
        client.connect("bunker"); <=== THIS LINE CAUSE THE PROBLEM
        Spark.publish("MQTT","TryToConnectMQTT");
    }
    else
    {
        Spark.publish("MQTT","Connected");
    }
}

Timer connectMQTT(5000, connect_MQTT); 

void setup() {
    WiFi.on();
    Particle.connect();
    connectMQTT.start();
    client.connect("bunker"); <==== USELESS IN SEMI_AUTOMATIC
    if (client.isConnected()) {
       client.subscribe("Soll_Temp_Pool");
       Spark.publish("MQTT","isConnected");
    }
    
}

void connect() {
  if (Spark.connected() == false) {
    Spark.connect();
  }
}

/**void connect() { <===== ALTERNATIVE TEST: NOT WORKING, BUT NO GREEN BREATHING
  if (Spark.connected() == false) {
    Spark.connect();
  }
  if (!client.isConnected()) {
        client.connect("bunker");
        Spark.publish("MQTT","TryToConnectMQTT");
        if (client.isConnected()) {
            client.subscribe("Soll_Temp_Pool");
            Spark.publish("MQTT","isConnected");
        }
    }
    else
    {
        Spark.publish("MQTT","Connected");
    }
}
*/

Try adding a waitUntil(Particle.connected) after Particle.connect()
Additionally wrap your MQTT stuff in if (WiFi.ready()) conditions to not attempt any WiFi action when it’s not there anyway.
You are calling client.connect() - but where is the client declared?

And also avoid using the deprecated Spark.xxxxx() functions, rather go with Particle.xxxxx() all the way through.

Okay, Thanks for the tipps, I have tried different things.

I have only posted the connection codes … not the definitons of the server and so.

So … If I use

SYSTEM_MODE(SEMI_AUTOMATIC);

void setup(){
    WiFi.on();
    Particle.connect();
    waitUntil(Particle.connected);
    client.connect("bunker");
}

everything works

But because I will not be depended on WIFI I tried:

SYSTEM_MODE(SEMI_AUTOMATIC);

Timer connectTimer(5000, connect);

void setup() {
    WiFi.on();
    connectTimer.start();
} 

void connect() {
    if (Particle.connected() == false) {
        Particle.connect();
        //waitUntil(Particle.connected); <=== HERE IT CAUSE RED S.O.S
    }
    if (client.isConnected() == false ) {
        client.connect("bunker"); <=== AGAIN GREEN BREATHING, AFTER BLUE BREATHING AND "device came online"
    }
  
}

You must not use blocking code inside a Timer callback (hence the SOS).

You’d need to set a flag inside your callback and do the waiting in loop() or just don’t wait but make sure to only do WiFi dependent stuff if(WiFi.ready() and cloud stuff if(Particle.connected()).

e.g.

void connect() {
    // first start connecting (and don't retrigger while trying!)
    if (!Particle.connected() && !Particle.connecting())
        Particle.connect();

    // some later visit to this callback (after connection got established)
    if (WiFi.ready() && !client.isConnected()) 
        client.connect("bunker"); 
    }
}

There is no Particle.connecting()
https://docs.particle.io/reference/firmware/core/#cloud-functions
But the Cloud connecting works.

But MQTT not.

I have raised the timer repeat to 20s.
So after 20s it goes cyan breathing and connects to the cloud.
And after 40s (from start) it runs client.connect(); and goes green breathing. :frowning:

A problem with the libary?

My bad, there only is a WiFi.connecting() :blush:
But for MQTT and TCPClient WiFi.ready() (and if you want checking for valid local IP) should be enough.

That seems to be the case.
This would happen, if the function blocks the cloud houskeeping for more than 10sec.
You could try SYSTEM_THREAD(ENABLED) then - as a workaround.

1 Like

Tried … now client.connect() brings RED S.O.S :slight_smile:

You mean adding SYSTEM_THREAD(ENABLED) did cause that? :confused:

Could you post your current code again?

Not directly … SYSTEM_THREAD(ENABLE) itself is no problem.
But if I decomment the client.connect() it (SystemThread) change the failure from green breathing to red s.o.s

This time I post the complete code with all comments, tryings, and so on:

#include "thermistor-library/thermistor-library.h"
#include "MQTT/MQTT.h"
#include <string>

SYSTEM_MODE(SEMI_AUTOMATIC);
SYSTEM_THREAD(ENABLED)

// Analog pin the thermistor is connected to
int thermPin = A0;

// Voltage divider resistor value
int thermRes = 10000;

float TempPool;

// Configure the Thermistor class
Thermistor Thermistor(thermPin, thermRes);


// globals

void callback(char* topic, byte* payload, unsigned int length);
/**
 * if want to use IP address,
 * byte server[] = { XXX,XXX,XXX,XXX };
 * MQTT client(server, 1883, callback);
 * want to use domain name,
 * MQTT client("www.sample.com", 1883, callback);
 **/
byte server[] = { 192,168,184,170 };
MQTT client(server, 1883, callback);

// recieve message
void callback(char* topic, byte* payload, unsigned int length) {
/**    char p[length + 1];
    memcpy(p, payload, length);
    p[length] = NULL;
    String message(p);
[...]
**/
}

/**
void connect_MQTT(){
    if (WiFi.ready()){
        if (!client.isConnected()) {
            client.connect("bunker");
            Spark.publish("MQTT","TryToConnectMQTT");
        }
        else
        {
            Spark.publish("MQTT","Connected");
        }
    }
}

*/
void TempPoolV(){
    TempPool=Thermistor.getTempC(true);
    Spark.publish("TempPool", String(TempPool));
    client.publish("TempPool1", String(TempPool));
}

Timer TempPoolT(5000, TempPoolV);
Timer connectTimer(20000, connect);

void setup() {
    WiFi.on();
    
    //RGB.control(true);
	
	// Initialize the Thermistor class


    connectTimer.start();
    
    
    Thermistor.begin();
    TempPoolT.start();
}

//Thermistor.getTempC()

void loop() {

    if (client.isConnected())
        client.loop();
    
    //RGB.color(0,0,duty);
    //StatusBWM = digitalRead(PinBWM);
    
}

void connect() {
    // first start connecting (and don't retrigger while trying!)
    if (!Particle.connected())
        Particle.connect();

    // some later visit to this callback (after connection got established)
    if (WiFi.ready() && !client.isConnected()) {
        client.connect("bunker");  //<==== THIS CAUSE THE ERROR!!!

    }
}
/**
void connect() {
    if (Particle.connected() == false) {
        Particle.connect();
        //waitUntil(Particle.connected);
    }
    // connect to the server
    if (client.isConnected() == false ) {
        //client.connect("bunker");
        // publish/subscribe/
        if (client.isConnected()) {
    //        client.subscribe("pool_temp");
            Spark.publish("MQTT","isConnected");
        }
    }
  
}
*/

I'll repeat myself here

The client.connect() is obviously blocking too long and hence causes the SOS.

Try removing this part from your callback completely and put this in loop() instead (and add pinMode(D7, OUTPUT); in setup() for debugging)

void loop() {
  if (WiFi.ready())
  {
    digitalWrite(D7, HIGH);
    if (client.isConnected())
        client.loop();
    else
        client.connect("bunker"); 
    digitalWrite(D7, LOW);
  }
  //RGB.color(0,0,duty);
  //StatusBWM = digitalRead(PinBWM);
  delay(1000);
}

The time the blue onboard LED stays on shows how uncooperative that MQTT library is in case of a missing MQTT server (which I haven't got at your address).
It actually never seems to return from client.connect() if it can't find the server.
I guess this is something you don't want to happen either.

IT WORKS :smiley: The first version, before you edited

Sorry, I am not understand not using blocking code. g I will research.

Thanks very much … I know you have invested many time for me.

Actual Version:

void loop() {
    if (WiFi.ready())
      {
        if (client.isConnected())
            client.loop();
        else
            client.connect("bunker"); 
      }
}

void connect() {
    if (!Particle.connected())
        Particle.connect();
}
1 Like

The problem you still might run into (hence my edit above) is that in case your WiFi is present but your MQTT server isn’t your code will still be blocked and your intended fallback solution will fail due to MQTT library not behaving very cooperative.
There should be some sensible and/or user controlable timeout for any connection attempt.

Just try switching off your MQTT or setting a different IP for it and see how the blue LED never goes out.

You couldn’t do a lot about that, since this would need to be built into the library.

Yes, I understand.

I will try to contact the library author.

My MQTT Server is very stable and the few times in year I restart it, I will restart the particle too. The Wifi in the garden is not so stable g I have also a monitoring for my home-automatic, so I will see it soon.

It is not working over night :frowning:

Cyan Breathing and no cloud connection, but MQTT works.
Than Cyan Breathing and nothing works anymore. Not even Reading and Writing Pins.
Than Cyan blinking like trying to connect and nothing works.

As written yesterday I will contact the library author … Without MQTT everything works fine and with MQTT chaos.
I will also check if there is another way to talk with my OpenHAB Server.

Yup, that is what I experienced. Once the MQTT client.connect() stalls nothing else works - not even OTA flashing.

No good citizen behaviour of the library IMHO :point_up_2:

If you want to use particle functions, here is an example project that works with semi automatic mode.


You can use mobicle.io for an interface. It works great.

I am currently expanding the system and have some slightly newer code.
The DHT sensor is not currently working but is on the list. Do not use the adafruit dht library, I know there are problems with it. I am using the original hot tub thermistors for water temp feedback and control.

Thanks, But I try to use “CURL” to transfer values between Particle Photon and OpenHAB.

Would the work around for that be to wrap all of the MQTT functions in:
waitFor(MQTT_call, 10000);

Do you think that would offer suitable forced timeouts?

I’m not sure what prerequisits a function needs to satisfy for it to work with waitFor() (would need to look into the implementation). But one thing for sure: If the active MQTT_call is blocking and won’t bail out by itselft, your waitFor() will never get the chance to do its waiting. Typically with waitFor() you call the respective active function (e.g. Particle.connect()) first and then waitFor(Particle.connected, timeout) but if Particle.connect() already blocks, the subsequent “outcome-check” won’t ever be reached.
Also note that waitFor() does not allow to pass any parameters to the function to check, that limits the potential to use it to bool MQTT_call(void) functions.

I’d rather go the elaborate route and implement the timeouts the old fashined way - especially when you want to do something related to the library’s tasks while waiting around.

Ahh, that makes sense!

In other words, ask the library developer to adjust the client.connect() code?