Adafruit MQTT: how to create an array of subscriptions?

I am working with the Adafruit MQTT library, and I have the sample programs working fine.

In the MQTT_2subs_esp8266 example sketch two subscriptions are set up as follows:

Adafruit_MQTT_Subscribe onoffbutton = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/onoff");
Adafruit_MQTT_Subscribe slider = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/slider");

In my application, I won't be hardcoding the subscriptions. I need to create subscriptions based on run-time user data. So I think that I need to create an array of subscriptions, and populate them as needed maintaining an index of which array element is which.

To incrementally explore moving from hard coded variables (such as slider and onoffbutton above), I tried to modify the code as follows:

Adafruit_MQTT_Subscribe MQSubs[2];
MQSubs[0] = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/onoff");
MQSubs[1] = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/slider");

Unfortunately this results in a compilation error (on the first of the 3 lines above) of:

error: no matching function for call to 'Adafruit_MQTT_Subscribe::Adafruit_MQTT_Subscribe()'

I am confused by what the Adafruit_MQTT_Subscribe object/ type/class is, and how to create an array of them.

In the public section of the class Adafruit_MQTT in Adafruit_MQTT.h I find:

Adafruit_MQTT_Subscribe *readSubscription(int16_t timeout = 0);
Adafruit_MQTT_Subscribe *handleSubscriptionPacket(uint16_t len);

also:

class Adafruit_MQTT_Subscribe; // forward decl

and

class Adafruit_MQTT_Subscribe {
public:
  Adafruit_MQTT_Subscribe(Adafruit_MQTT *mqttserver, const char *feedname,
                          uint8_t q = 0);
...

I believe this latter definition is what I am dealing with, a Class with a constructor that expects a pointer an Adafruit_MQTT object, a pointer to a char array and an integer. I think I am trying to create an array of uninitialized Class objects. Perhaps there needs to be a second constructer that does not demand any parameters...?
I suspect that my lack of understanding of how to use the correct syntax to create an array of whatever these things are is the problem. I can't work out what these 'objects' are as the variables are declared as it they are a 'type' yet their initial values are assigned using a function/constructor using the identical name.

What approach should I take?
I am happy to initially create them with null values and empty string parameters and reset them at runtime when the topics to be subscribed to are known.

I also understand that subscriptions need to be declared before connecting to the MQTT server, and I plan on disconnecting and reconnecting if the subscriptions need to change.

I am trying to run on ESP8266.

It would have been really helpful if you had actually posted complete code so I could test compile my proposed answer. Lacking that, I'll I can say is try this:

Adafruit_MQTT_Subscribe MQSubs[] = {
  {&mqtt, AIO_USERNAME "/feeds/onoff"},
  {&mqtt, AIO_USERNAME "/feeds/slider"}
};

I don't understand what you mean by 'hardcoding', why is the array approach with indexing mentioned here:

Adafruit_MQTT_Subscribe MQSubs[2];
MQSubs[0] = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/onoff");
MQSubs[1] = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/slider");

is not considered 'hardcoding'?

I am not familiar with the MQTT library but in general I would try to do something like this:

Adafruit_MQTT_Subscribe MQSubs[] = {
   {&mqtt, AIO_USERNAME "/feeds/onoff"},
   {&mqtt, AIO_USERNAME "/feeds/slider"}
};

Also, I am wandering what AIO_NAME "/feeds/onoff"
actually represents... is it string concatenation? maybe it results in string expression? if that is the case try using c_str() method on the above.

thanks, yes your suggestion does compile, many thanks! If I wanted the array size to be 100, am I forced to create all 100 initial values? Perhaps I can call it once and then assign that value to all of them.

Adafruit_MQTT_Subscribe * MQSubs[2];

And later

MQSubs[0] = new Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/onoff");

But be careful, using pointers and dynamic memory can be dangerous, if you don't know what you are doing. Unfortunately, that library doesn't give another choice

Why? unreasonable use of new

  1. I never noticed that there is no concatenation operator in their (working) sample code. I have no idea why that works! AIO_USERNAME is a string define.
    #define AIO_USERNAME "MFred"

  2. I was removing the hardcoding of the variable name, first, one thing at a time.

Now that I have the array compiling as:

Adafruit_MQTT_Subscribe MQSubs[] = {
  {Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/")},
  {Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/")}
 };

I find I can reassign elements like this:

MQSubs[0] = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/onoff");
(Temporary hard code to test)

if you had 100 items and you are sure about it, you could do something like this

constexpr uint32_t subscriptions = 100;
Adafruit_MQTT_Subscribe MQSubs[subscriptions];
//... some code 

for (auto i = 0; i < subscriptions; i++)
 MQSubs[i] = //whatever you want.

if your number of subscriptions can change than consider using a vector

You could create them dynamically at run-time with the 'new' command. Your array would then be an array of pointers to Adafruit_MQTT_Subscribe objects.

1 Like

it compiles because it is a macro...

In my understanding using macros as constants is a bad idea, your code works but I would suggest separating it as to const char* words.

I am not so sure it will work:
MQSubs[0] = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/onoff");

if it will than fine but I think

MQSubs[0] = {&mqtt, AIO_USERNAME "/feeds/onoff"};

will be a better way.

1 Like

posting the complete code would still need you to install the whole MQTT library and two others it thinks it is dependent on, and then you also get the full example sketch I mentioned. Your code was spot-on though!

Just to be clear that code of the macro concatenation is not my code it is the sample code that comes with the Adafruit library.

In my code the user ID will be user supplied data along with the actual topic as a single string anyway. i.e. "Joe/feeds/slider"

I am not accusing you :wink: good luck with the rest of your project.

1 Like

"Complete Code" doesn't necessarily mean your entire project. It means the smallest possible complete code that compiles and demonstrates the problem at hand. In this case it would have been the smallest possible code that only requires the Adafruit MQTT Library (which I already have installed). Nothing other than that should be required to demonstrate the compiler error you were getting, and only that error. Don't need all the clutter of your entire project for that.

This is called an MRE.

1 Like

Both:

Adafruit_MQTT_Subscribe MQSubs[] = {
  {Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/")},
  {Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/")}
 };

and

 Adafruit_MQTT_Subscribe MQSubs[] = {
  {&mqtt, ""},
  {&mqtt, ""}
 };

compile and function correctly, probably because I overwrite their initial values later with real data:

MQSubs[0] = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/onoff");
MQSubs[1] = Adafruit_MQTT_Subscribe(&mqtt, AIO_USERNAME "/feeds/slider");

Is there a risk that the memory size of objects initially created are smaller than the later size and will result in memory corruption?

I hoped this would work... but in their sample code at least, they refer to the variables with &xxxx and &xxxx.lastread and these pointers make my head spin...

I am going to try it though....

Edit: yup, member of pointers don't exist, MQSubs[0].lastread had to be changed to
MQSubs[0]->lastread and it worked...

Many thanks.

thanks everyone, tremendously fast and good responses!

Is there a risk that the memory size of objects initially created are smaller than the later size and will result in memory corruption?

Not in the way described - the objects can't change their size in memory, the problematic way is when they are referring to heap buffers. In your case I would not be worried because it is on the library creator to supply usable code, so if you allocate your objects on the stack (i.e. without the 'new keyword) you are most probably fine. You do have to keep in mind that your system does not have unlimited RAM and you have to make as little allocations as possible.

1 Like

if I don't use the new when assigning I get:

error: cannot convert 'Adafruit_MQTT_Subscribe' to 'Adafruit_MQTT_Subscribe*' in assignment

That's because a pointer to an object is a different datatype than the object itself.