MQTT problem, only two subscriptions??

I am using a Wemos D1 Mini to monitor my vegetable garden moisture to automate the watering cycle.
Since it will be solar powered and outside, I set it up with OTA and to respond to MQTT messages.
That all works except for one small issue. I wanted to add a third topic.

The three MQTT topics that I want my ESP to subscribe to are defined:

#define NODENAME "garden"
const char *sleepTopic = NODENAME "/sleepTime";           //How long in seconds to sleep
const char *otaTopic = NODENAME "/ota";                   //Sets a flag to ut the ESP into OTA mode.
const char *tCorrectTopic = NODENAME "/tcorrect";         //Sends the temperature correction value.

And here is where I subscribe to the topics:

void mqttConnect() {
  while (!client.connected()) {
    Serial.print(F("MQTT connection..."));
    if (client.connect(connectName)) {
      Serial.println(F("connected"));
    } else {
      Serial.print(F("failed, rc="));
      Serial.print(client.state());
      Serial.println(F("- trying again in 5-seconds."));
      delay(5000);
    }

    Serial.print(F("Subscribing to "));
    Serial.println(tCorrectTopic);
    client.subscribe(tCorrectTopic);
    delay(pubsubDelay);

    Serial.print(F("Subscribing to "));
    Serial.println(otaTopic);
    client.subscribe(otaTopic);
    delay(pubsubDelay);

    Serial.print(F("Subscribing to "));
    Serial.println(sleepTopic);
    client.subscribe(sleepTopic);
    delay(pubsubDelay);

  }
}

But, when I send a message to the topics, only the first two subscriptions trigger the callback function.
What’s strange is it doesn’t matter the sequence. I can move them about in the sketch, but
only the first two subscribed topics trigger a callback.

// ************************  mqtt callback ***************************
void callback(String topic, byte * message, unsigned int length) {

  Serial.println();
  Serial.print(F("Message arrived on topic: "));
  Serial.println(topic);

  // Convert the character array to a string
  String messageString;
  for (int i = 0; i < length; i++) {
    messageString += (char)message[i];
  }
  messageString.trim();
  messageString.toUpperCase();          //Make the string upper-case


  Serial.print("messageString: ");
  Serial.print(messageString);
  Serial.println();


  if (topic == sleepTopic) {
    //Time in ms to sleep
    sleepSeconds = messageString.toInt();
    Serial.print(F("New sleepTime= "));
    Serial.print(sleepSeconds);
    Serial.println(F(" seconds"));
  }

  if (topic == otaTopic) {
    otaFlag = false;
    if (messageString == "TRUE") {
      otaFlag = true;
    }
  }
 
  if (topic == tCorrectTopic) {
    tCorrection = messageString.toInt();
    Serial.print(F("New Temperature Correction= "));
    Serial.print(tCorrection);
    Serial.println(F(" degrees"));
  }

} //callback

The entire sketch is spread over 5 tabs and I am willing to share it as a zip file if anyone wants to see the whole thing.

If anyone has a clue why I can only subscribe to two topics, I would appreciate a hint.

The entire sketch is spread over 5 tabs and I am willing to share it as a zip file if anyone wants to see the whole thing.

Does the same problem occur with a much simpler program ?

I cannot see where the problem is but why not subscribe to all NODENAME pubs like I do in the below example.

bool mqttConnect()
{
  if(DEBUG){ Serial.print(("Attempting MQTT connection... "));}
  if (mqttClient.connect("", mqttUsername, mqttPassword)) 
  {
    if(DEBUG){ Serial.println(F("Connected"));}
    String scratch = (String)myDeviceName + "/#";           // Substribe to all MQTT topics for this device
    mqttClient.subscribe(scratch.c_str());                  // Subscribe to MQTT input
    if(DEBUG){ Serial.print(F("Subscribe: "));}
    if(DEBUG){ Serial.println(scratch);}
  } 
  else 
  {
    Serial.print(F("Failed, rc="));
    Serial.println(mqttClient.state());
  }
  return mqttClient.connected();
}

And then separate the topics in your callback…

Sorry had to attach as post to big else.

callback.ino (8.58 KB)

Riva:
I cannot see where the problem is but why not subscribe to all NODENAME pubs like I do in the below example.

Thanks, I hadn't thought of subscribing to NODNAME/#. Would save me some coding time. Subscribe isn't the problem, it is that only two topics will receive a message.

Here's a tip for you.

Put this before setup()

#define DEBUG true  //set to true for debug output, false for no debug output
#define Serial if(DEBUG)Serial

This way you don't have to put if (DEBUG) in front of your Serial.print statements.

SteveMann:
Here's a tip for you.

Put this before setup()

#define DEBUG true  //set to true for debug output, false for no debug output

#define Serial if(DEBUG)Serial




This way you don't have to put if (DEBUG) in front of your Serial.print statements.

Thanks for the tip but some Serial.prints always need to happen the DEBUG ones are mostly for testing.

UKHeliBob:
Does the same problem occur with a much simpler program ?

Yes. I reduced the sketch to demonstrate the problem.

I’ve used the same code on other projects and never any problems with MQTT messaging. But, this is the first time I used MQTT and OTA on a project that also goes into deep sleep.

Here’s my test process.

  1. Send retained messages to each topic, topic1, topic2 and topic3
  2. When the ESP wakes up, it should receive the retained messages, but it only gets topic1 and topic2.

If I publish a null, retained, to topic2, then topic1 and topic3 are received.
If I publish a message, retained, to topic2, then topic1 and topic2 are received.
If I publish a null message, retained, to topic1, then topic2 and topic3 are received.

(From hiveMQ: “The standard MQTT mechanism to clean up retained messages is sending a retained message with an empty payload to a topic. This will remove the retained message.”)

If you have an ESP board, this code should compile for you if you want to replicate the problem. If you don’t want to wire D0 to RST to wake the board, just press reset when the board goes to sleep.

#define sketchName "pubsub_test.ino, V1.0"
/*
   IDE:
     Board: Lolin(Wemos) D1 R2 & Mini

   --Version 1.0 --
     05/08/20 - Troubleshooting pubsub problem where only two subscriptions trigger a callback response.
*/


#include <ESP8266WiFi.h>        // Connect (and reconnect) an ESP8266 to the a WiFi network.
#include <PubSubClient.h>       // connect to a MQTT broker and publish/subscribe messages in topics.

#ifndef Kaywinnet
#include "D:\River Documents\Arduino\libraries\Kaywinnet.h"  // WiFi credentials (my_ssid, my_password)
#endif


// ****************************** Globals  ******************************
#define NODENAME "ps_test"

#define hostPrefix NODENAME     // For setupWiFi()
char macBuffer[24];             // Holds the last three digits of the MAC, in hex.

const char *topic1 = NODENAME "/topic1";
const char *topic2 = NODENAME "/topic2";
const char *topic3 = NODENAME "/topic3";

const char *connectName =  NODENAME "xyzzy";            //Must be unique on the network
const int mqttPort = 1883;

int sleepSeconds = 30;
const int pubsubDelay = 20;           //Time between publishes
long rssi;                            //Used in the WiFi tab


static const char *mqttSubs[] = {
  topic1,
  topic2,
  topic3
};



// Declare an object of class WiFiClient, which allows to establish a connection to a specific IP and port
// Declare an object of class PubSubClient, which receives as input of the constructor the previously defined WiFiClient.

WiFiClient psClient;                // The constructor MUST be unique on the network.
PubSubClient client(psClient);




// ========== setup() ==========
void setup(void)
{
  Serial.begin(115200);
  delay(10);
  Serial.println();
  Serial.println();
  Serial.println(sketchName);
  Serial.println();
  Serial.print(F("topic1= "));
  Serial.println(topic1);
  Serial.print(F("topic2= "));
  Serial.println(topic2);
  Serial.print(F("topic3= "));
  Serial.println(topic3);
  Serial.println();


  setup_wifi();


  // Call the PubSubClent setServer method, passing the broker address and the port.
  client.setServer(mqtt_server, mqttPort);
  mqttConnect();
  client.setCallback(callback);


  //Ensure we've sent & received everything MQTT before sleeping
  //This adds time to the wake time.
  for (int i = 0; i < 5; i++)
  {
    client.loop();
    delay(100);
  }


  // ---------- Sleep ----------
  // Connect D0 to RST to wake up
  pinMode(D0, WAKEUP_PULLUP);
  Serial.print(F("Sleeping, "));
  Serial.print(sleepSeconds);
  Serial.println(F(" seconds."));
  ESP.deepSleep(sleepSeconds * 1000000);

}

// ========== loop() ==========
void loop(void) {
}



// ========== Connect the ESP to the router ==========

#ifndef Kaywinnet
#include "D:\River Documents\Arduino\libraries\Kaywinnet.h"  // WiFi credentials
#endif

char hostNamePrefix[] = hostPrefix;
char hostName[12];        // Holds hostNamePrefix + the last three bytes of the MAC address.

void setup_wifi() {
  byte mac[6];                     // the MAC address of your Wifi

  Serial.println(F("\n"));
  Serial.print(F("Connecting to "));
  Serial.println(my_ssid);


  WiFi.mode(WIFI_STA);
  WiFi.enableInsecureWEP();
  WiFi.begin(my_ssid, my_password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(WiFi.status()); Serial.print(F(" "));
  }
  Serial.println(F("\nWiFi connected, "));
  Serial.print(F("MAC Address: "));
  Serial.println(WiFi.macAddress());
  Serial.print(F("IP address: "));
  Serial.println(WiFi.localIP());


  // Get the last three numbers of the mac address.
  // "4C:11:AE:0D:83:86" becomes "0D8386" in macBuffer.
  WiFi.macAddress(mac);
  snprintf(macBuffer, sizeof(macBuffer), "%02X%02X%02X", mac[3], mac[4], mac[5]);

  // Build hostNamePrefix + last three bytes of the MAC address.
  strcpy(hostName, hostNamePrefix);
  strcat(hostName, macBuffer);

  Serial.print(F("hostName = \""));
  Serial.print(hostName);
  Serial.println(F("\""));
}


// ========== mqttConnect() ==========
void mqttConnect() {
  while (!client.connected()) {
    Serial.print(F("MQTT connection..."));
    if (client.connect(connectName)) {
      Serial.println(F("connected"));
    } else {
      Serial.print(F("failed, rc="));
      Serial.print(client.state());
      Serial.println(F("- trying again in 5-seconds."));
      delay(5000);
    }



    char subTopic[40];
    byte numberOfSubs = (sizeof(mqttSubs) / sizeof(mqttSubs[0]));
    Serial.print(numberOfSubs);
    Serial.println(F(" subscriptions: "));
    for (int i = 0 ; i < numberOfSubs; i++) {
      strcpy(subTopic, mqttSubs[i]);
      Serial.print(F("Subscribing to "));
      Serial.println(subTopic);
      client.subscribe(subTopic);
      delay(pubsubDelay);
    }
  }
}



// ********** mqtt callback **********
void callback(String topic, byte * message, unsigned int length) {

  Serial.println();
  Serial.print(F("Message arrived on topic: "));
  Serial.println(topic);


  // Convert the character array to a string
  String messageString;
  for (int i = 0; i < length; i++) {
    messageString += (char)message[i];
  }
  messageString.trim();
  messageString.toUpperCase();          //Make the string upper-case


  Serial.print("messageString: ");
  Serial.print(messageString);
  Serial.println();

}