Particle not responding to MQTT topic

Using Mosquitto on my RPi and this library to control a GPIO that turns on a siren. I got this working the other day and now it stopped working as in the photon stopped responding to my command. How do I debug? Where should I look? I tested my broker and it can receive the topics and it’s connected.

Nvm I got it. Realized that I just needed to be publishing a heartbeat signal so that the particle board is able to receive a subscribed payload

Actually it just happened again! I think that the particle somewhere down the line times out. I am not certain why this is happening? Under what conditions does the particle disconnect from the broker? Could this be an issue related to the wifi signal? maybe the sensor get’s disconnected, but then why won’t the particle board get reconnected once it establishes a reconnection on the wifi signal?

I see two issues here …

  1. What is causing your specific outage. That is anyone’s guess without more information. I normally start by monitoring the Mosquitto log with the command: “sudo tail /var/log/mosquitto/mosquitto.log -f”.

  2. The second issue is “How do you recover from any outage?”. In short, the client is responsible for reconnecting and resubscribing to topics after a disconnect. I clipped this code from one of my apps. I’m not claiming it to be the “best solution”, but I have found it to be very reliable.

Global variables

    // MQTT VARIABLES
    const unsigned int MAX_MQTT_PACKET_LENGTH = 512;    // sets maximum MQTT packet size (in bytes)
    byte mqttBrokerIP[] = {192,168,86,53};   
    bool mqttSessionActive = false;
    time_t mqttLastConnectTime = 0;
    MQTT* mqtt = new MQTT(mqttBrokerIP, 1883, 30, mqttCallback, MAX_MQTT_PACKET_LENGTH);

This runs in loop()

/*
--------------------------------------------------------------------------------------------------------------------------------------------------
MQTT Session Manager
 *      - Establishes and maintains communications with the MQTT broker
 *          State logic is used, and each state is processed in a separate loop() cycle to minimize
 *                  the performance impact on the primary application.
 *      - Includes a callback function to accept messages from the MQTT broker
 * ------------------------------------------------------------------------------------------------------------
 *	State 1: An MQTT session has been established and the connection still exists.
 *	State 2: An MQTT session has been established but the connection has dropped.
 *	State 3: An MQTT session has not been established, but a connection with the broker is now present
 *	State 4: An MQTT session has not been established, and it is time to perform a connection attempt
 *	State 5: An MQTT session has not been established, and it is too soon to perform a connection attempt
 * ------------------------------------------------------------------------------------------------------------------------------------------------*/

 void mqttSessionManager(void) {
    static const unsigned int MQTT_CONNECT_DELAY = 30;         // Sets number of seconds between MQTT connect attempts
	const char mqttClientName[] = "edison";                    // MQTT Client Name
    static time_t mqttLastDropTime = 0;
    static time_t mqttNextConnectTime = 0;

    if (mqttSessionActive) { // we are in an active session ...
        if (mqtt->isConnected()) { // and we are still connected ...								// State 1
            mqtt->loop();
        } else { // we lost our connection															// State 2
            mqttSessionActive = false;
            mqttLastDropTime = Time.now();
            delete mqtt;
            mqtt = new MQTT(mqttBrokerIP, 1883, 30, mqttCallback, MAX_MQTT_PACKET_LENGTH);
        }
    }else if (mqtt->isConnected()) { // we have a brand new connection ...							// State 3
        mqttSessionActive = true;
        mqttLastConnectTime = Time.now();
        mqttNextConnectTime = 0;
        // ---------- INSERT MQTT SUBSCRIPTIONS HERE -----------
			mqtt->subscribe("time/zone");
			mqtt->subscribe("edison/relay/set");
		// -----------------------------------------------------
    }else if (Time.now() > mqttNextConnectTime) { // it's time to make a connection attempt ...		// State 4
        mqtt->connect(mqttClientName);																
        mqttNextConnectTime = Time.now() + MQTT_CONNECT_DELAY;
    }   																							// State 5
    return;
}

PS … I just noticed that the comments refer to the " callback function" that is not included in this snippet. You can tell from the last line in the global variables that the function is named mqttCallback(). The code in that function parses/processes messages received from the broker related to the two subscriptions listed in the code. I intentionally omitted this function because the code is application specific and irrelevant to this conversation.

1 Like

Just out of interest: Have you had any issues with using a global or a “permanent” dynamic MQTT instance that “forced” you to repeatedly use new and delete?
new and delete are also increasing the risk of heap fragmentation.

I used a permanent instance when I developed the initial version of this code. That version would often get stuck in the recovery cycle. I don’t remember the details, but I do remember feeling like the root problem was a “dirty” MQTT object. That’s when I decided to try the nuclear new/delete approach. Since then, I’ve seen nothing but clean recoveries.

Is it possible that something else was going on? Yes, it is definitely possible. Every time I see the new/delete I grimace and think: “I should go back and retest this”. Then I think about the work to set up a test MQTT broker and how infrequently real recoveries take place. The risk seems small, so I end up kicking the can down the road. You can probably hear it : thud, clang, clang , rattle rattle.

1 Like