MQTT, 2 topics, how to get separate values ?

Hello All,

i am struggeling with Arduino and MQTT to work as i want to.

The good news is, I can see both of my values in the serial monitor, but they are in the same string.
If i display the payload, i`l get 2 sets of numbers (ASCII ?)
I found out that Serial.print((char) payload[i] displays the 2 values i want to see.

But i need those 2 values to be separate from each other and to convert them to variables.
I`ve been searching for hours, but i am not getting anywhere....

this is the code so far:

// publish and subscribe
 client.publish(topic, "p1monitor/smartmeter/consumption_kw");
 client.publish(topic1, "p1monitor/smartmeter/production_kw");
 client.subscribe(topic);
 client.subscribe(topic1);
}

//Callback loop: 
void callback(char *topic, byte *payload, unsigned int length) {            
 for (int i = 0; i < length; i++) {
     Serial.print((char) payload[i]);
     }
   Serial.println();
 }

void loop() {
 client.loop(); 
}

the output in the serial monitor looks like this:
0.151
0.007
0.148
0.007
0.151
0.007
0.146

the first line 0.151 is the energy consumtion (topic) en the following line is the energy production (0.007, topic1).

How do i separate the 2 topics from the incoming data and convert them to a variable ?
I can convert the payload to a float, but that isthe whole payload, not the to values.
I`m stuck...

Welcome to the forum

The topic name is, not surprisingly, in the topic variable in the form of a pointer to an array of chars terminated by a zero. Such an array is commonly referred to as a C style string (NOTE - not a String - capital S)

To compare a string with a value you can use the appropriately name strcmp() function, like this

if (strcmp(topic, "p1monitor/smartmeter/consumption_kw" == 0)
{
   // we got a message from the p1monitor/smartmeter/consumption_kw topic
}

You can turn the payload into a float like this

  char messageBuffer[30];
  memcpy(messageBuffer, payload, length);  copy the message
  messageBuffer[length] = '\0';  //turn it into a string
  float theNumber = atof(messagBuffer);  //convert to a float

Thank you for your quick response.
Does this mean that for each topic i can use strcmp?

I'll try this the next coming days.

I will keep you posted.

Yes

The principle would be that on receipt of a message the topic would be compared to the appropriate string and the associated variable would be extracted from the payload

If you put the topic name strings in an array then you don't even need two sets of code to do the comparison because you could iterate through the array and do the comparison

NOTE that I have not had the opportunity to test the code snippets that I posted. The principles are sound but there may be typos in there

Unfortunately, your suggestion to use strcmpis not compiling.
error message is: Compilation error: cannot convert 'bool' to 'const char*'

is this because in the global variable:s const char *topic = "p1monitor/smartmeter/consumption_kw"; is declared ?

You quoted part of my answer and mentioned an error in another part

Please post your full sketch and the full error message copied from the IDE using the button provided for the purpose. Please use code tags when you post both of them

There's a typo in the (untested) suggested code, a missing parenthesis. But first

Why are you sending the topic names to the topic?

Do you control the format of the messages? If subscribers are expecting bare numbers, publishing text -- as above -- might break them. And if it's just a number, it may be possible to send it directly as a 4-byte float or 8-byte double, which would obviate conversion from string.

Pick better names for variables, especially since the variable name topic is the first argument in the callback function.

If you really are only subscribing to two topics, you only have to check for one.

void callback(const char *topic, byte *payload, unsigned int length) {
  if (strcmp(topic, topicConsumption) == 0) {
    // that one
  } else {
    // the other one
  }
}

Well spotted

if (strcmp(topic, "p1monitor/smartmeter/consumption_kw" == 0)
{
   // we got a message from the p1monitor/smartmeter/consumption_kw topic
}

Should be

if (strcmp(topic, "p1monitor/smartmeter/consumption_kw" == 0))  //added missing )
{
   // we got a message from the p1monitor/smartmeter/consumption_kw topic
}

I`d like to thank UKHelibob for his input !!
After some search i came up with the following solution which works for me:

#include <ESP8266WiFi.h>
#include <PubSubClient.h>

//Global variables
int i = 0;
int Val1 = 0;
int valdif = 0;
int verbruik = 0;
int teruglev = 0;

// WiFi
const char *ssid =  // Enter your WiFi name
const char *password =   // Enter WiFi password

// MQTT Broker
const char *mqtt_broker = "broker";
//const char *topic = "p1monitor/smartmeter/consumption_kw";
//const char *topic1 = "p1monitor/smartmeter/production_kw";
const int mqtt_port = 1883;

float consumption;
float production;

WiFiClient espClient;
PubSubClient client(espClient);

//end of global variables

void setup() {
  // Set software serial baud to 115200;
  Serial.begin(115200);
 // End of setup
  
  // connecting to a WiFi network
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      Serial.println("Connecting to WiFi..");
  }
  Serial.println("Connected to the WiFi network");
  //connecting to a mqtt broker
  client.setServer(mqtt_broker, mqtt_port);
  client.setCallback(callback);
  while (!client.connected()) {
      String client_id = "esp8266-client-";
      client_id += String(WiFi.macAddress());
      Serial.printf("The client %s connects to the public mqtt broker\n", client_id.c_str());
      if (client.connect(client_id.c_str())) {
          Serial.println("broker connected");
      } else {
          Serial.print("failed with state ");
          Serial.print(client.state());
          delay(2000);
      }
  }
  // publish and subscribe
  client.subscribe("p1monitor/smartmeter/consumption_kw");
  client.subscribe("p1monitor/smartmeter/production_kw");
}

//Callback loop: 
void callback(char *topic, byte *payload, unsigned int length) {            //unsigned int kan geen negatieve getallen opslaan
  String strTopic;
  
  strTopic = String((char*)topic);
  if (strTopic  == "p1monitor/smartmeter/consumption_kw") {
    char buffer[2];
    memcpy(buffer, payload, length);  //copy the payload to the buffer
    buffer[length] = '\0';  //terminate the string (NOT a String !
    consumption = String((char*)payload).toFloat();
    Serial.print("Verbruik:");
    Serial.println(consumption);
  }

    strTopic = String((char*)topic);
  if (strTopic  == "p1monitor/smartmeter/production_kw") {
    char buffer[2];
    memcpy(buffer, payload, length);  //copy the payload to the buffer
    buffer[length] = '\0';  //terminate the string (NOT a String !
    production = String((char*)payload).toFloat();
    Serial.print("Teruglevering:");
    Serial.println(production);
  }
  //Boiler control
verbruik = (consumption * 1000);
teruglev = (production * 1000);
Serial.println(verbruik);
Serial.println(teruglev);
}


void loop() {
  client.loop();

There will be (al lot) of things in this code which could be better, but it works.
Now i can start to work with the variables to control some things.

So this problem is solved, onto the next one :slight_smile:

Personally I would not use Strings, but as long as it works, that's what matters

Good luck going forward