rjwats / esp8266-react

A framework for ESP8266 & ESP32 microcontrollers with a React UI
GNU Lesser General Public License v3.0
471 stars 146 forks source link

MQTT: dynamic topics #207

Closed king2 closed 3 years ago

king2 commented 3 years ago

Is there any possibility to create topics on-fly? I have many topics that should be published (like 4 types of device voltages, device temperature, GSM module status and balance, and similar). I know all these topics before compilation, so I can add all these topics before compilation.

This requires (as I understand) many calls to _mqttPubSub in service initialization, many read() functions and so on, but it can be done at least. I have around 10-15 such topics in my device.

But I have also 1-wire temperature, humidity and light sensors that can be connected and configured by users, so I cannot know its quantity (from zero to ~20-30) and IDs at compilation time.

Can you give ma an advice how to do this in a right way? All such topics are 'output only', I need just to publish them.

Thank you in advance!

rjwats commented 3 years ago

It sounds like you want to get your hands on the MQTTClient and use it directly. You can get the client from the framework and pass it into your own component, then you are free to do whatever you want with it:

https://github.com/rjwats/esp8266-react#accessing-settings-and-services

The abstraction is only useful if you have a 1-to-1 mapping between a stateful entity and a topic, like you say.

Here are the API docs for the client library the framework uses:

https://github.com/marvinroger/async-mqtt-client

On Sat, Nov 28, 2020 at 2:17 PM Oleg King notifications@github.com wrote:

Is there any possibility to create topics on-fly? I have many topics that should be published (like 4 types of device voltages, device temperature, GSM module status and balance, and similar). I know all these topics before compilation, so I can add all these topics before compilation.

This requires (as I understand) many calls to _mqttPubSub in service initialization, many read() functions and so on, but it can be done at least. I have around 10-15 such topics in my device.

But I have also 1-wire temperature, humidity and light sensors that can be connected and configured by users, so I cannot know its quantity (from zero to ~20-30) and IDs at compilation time.

Can you give ma an advice how to do this in a right way? All such topics are 'output only', I need just to publish them.

Thank you in advance!

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/rjwats/esp8266-react/issues/207, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAKE4VBIM2WFI5DOZRBC5ZTSSEA7PANCNFSM4UF2HCRQ .

king2 commented 3 years ago

I have ESP8266 connected to another MCU via UART, so most of values will come into ESP from external world, big struct for known values, and something like array with variable length - for dynamic.

And I would like to use StatefulService for all of them - to be able to show live values on webpages via websockets and export to MQTT, maybe even export values changed by webpages back to primary MCU.

I can add struct with all values to class, but how to create many separate MQTT topics from one struct? What is I will call _mqttClient->publish()/_mqttPubSub.configureTopics many times with different configs? Will it overwrite old config or will publish a new one?

Should I create as many '_mqttClient's as many topics I want to have (hardcoded in classes)?

rjwats commented 3 years ago

It should be fairly easy to publish multiple topics using the client directly. You can get the AsyncMqttClient from the framework and pass it into your stateful service instance when you construct it. You don't need to be concerned with managing it's connection or settings, the framework will do that.

Something like this should work, within the state change callback of your stateful service:

// publish with QOS=0 and retain off
if (_mqttClient->connected()) {
  _mqttClient->publish("some/topic1", 0, false, "payload");
  _mqttClient->publish("some/topic2", 0, false, "payload");
  _mqttClient->publish("some/topic3", 0, false, "payload");
  _mqttClient->publish("some/topic4", 0, false, "payload");
}

There are also callbacks available to perform actions on various events, such as connect, disconnect, publish, subscribe etc...

// do somthing when the mqtt client estabilishes a connection
_mqttClient->onConnect([&](bool sessionPresent) { 
 publishStuff();
});
rjwats commented 3 years ago

I'm assuming you've solved your problem here :) re-open this if you need further help.

king2 commented 3 years ago

Thank you for answer!

I have tried different approaches, ended with collecting multiple variables inside one StatefulService. Idea is to receive many variables, mark each value as changed/unchanged and return StateUpdateResult::CHANGED if any of values was changed, just to propagate them. On page React will detect real changes by itself, so I need just use 'changed' attribute of each variable to decide will I publish it to MQTT or not.

Sorry, but this is my first try to write on C++, and I afraid that I can make dumb mistakes when writing this code :(

For example:

  1. Maybe this code can be optimized using C++ templates magic (especially in 'switch by variable type' sections)?
  2. I'm adding sensors as unsigned short[] array, but what should I use in types.ts to define such array? What should I use as name of such array variables?
  3. Where can I add MQTT publishing routine, where I can loop through all variables and publish changed ones?

Maybe you can give me an advice?

Thank you in advance!

example.zip