256dpi / arduino-mqtt

MQTT library for Arduino
MIT License
1.03k stars 237 forks source link

Sending floating point and integer data through MQTT #336

Open mitra42 opened 2 months ago

mitra42 commented 2 months ago

I've got some sensors generating floating point and integer data. I'd like to send this over MQTT, and ideally without converting back and forth to the Ascii.

The docs have bool publish(const char topic[], const String &payload); bool publish(const char topic[], const char payload[]);

I'm wondering if there is an accepted method of sending numbers (floating or int) over MQTT, and if so what it is.... I've tried searching for the answer in all the usual places, but I'm not finding it.

I also think it would be useful to have this in at least one of the examples.

ForrestErickson commented 1 month ago

Here is an answer the might work. Use JSON libraries to make a JSON object and then convert to binary to send.

The top answer here looks promissing. https://stackoverflow.com/questions/23947779/how-to-send-data-as-json-objects-over-to-mqtt-broker

Let us know what does and does not work.

mitra42 commented 1 month ago

I ended up converting to a string - yes you can use JSON but its more overhead (code size) on small processors.

ForrestErickson commented 1 month ago

Regarding, "I ended up converting to a string" Thanks for letting us know you solution. Can you post the code that works so that persons who find this question, as I did, will find an answer?

For what it is worth I have no idea how memory safe the JASON solutions might be. I have read that Strings in Arduino are not safe under some programing methods because of limited memory. Problem is more sever on an UNO of course.

mitra42 commented 1 month ago

My own code is a lot more complex, because its also dealing with things like queuing messages to avoid calling publish within messageReceived (a warning in the library).

The part you want I think is below. It sends a float by converting to a string. There are probably slightly simpler ways to write messageSend(String &topic, String &payload, bool retain, int qos) if you don't want the queuing.

// If retain is set, then the broker will keep a copy 
// TODO implement qos on broker in this library
// qos: 0 = send at most once; 1 = send at least once; 2 = send exactly once
// These are intentionally required parameters rather than defaulting so the coder thinks about the desired behavior
void messageSendInner(Message *m) {
  #ifdef SYSTEM_WIFI_WANT
    client.publish(*m->topic, *m->message, m->retain, m->qos);
  #endif // SYSTEM_WIFI_WANT
}
void messageSend(String &topic, String &payload, bool retain, int qos) {
  // TODO-21-sema also queue if WiFi is down and qos>0 - not worth doing till xWifi::connect is non-blocking
  Message *m = new Message(topic, payload, retain, qos);
  if (inReceived && qos) {
    queued.push(m);
  } else {
    messageSendInner(m);
  }
  // Whether send to net or queue, send loopback and do the retention stuff. 
  #ifdef SYSTEM_MQTT_LOOPBACK
    // This does a local loopback, if anything is listening for this message it will get it twice - once locally and once via server.
    if (m->retain) {
      retained.retain(m); // Keep a copy of outgoing, so local subscribers will see 
    }
    messageReceived(m);
  #endif // SYSTEM_MQTT_LOOPBACK
}
void messageSend(String &topic, float &value, int width, bool retain, int qos) {
  String *foo = new String(value, width);
  messageSend(topic, *foo, retain, qos);

}
void messageSend(String &topic, int value, bool retain, int qos) {
  String *foo = new String(value);
  messageSend(topic, *foo, retain, qos);
}

The full code is in the FrugalIoT repo; but this handles a lot more complexity that most situations won't need, including using MQTT within a single device to communicate between different code modules.

ForrestErickson commented 1 month ago

@mitra42 Thanks for the example. I am some times novice and seeing the simple function

void messageSend(String &topic, float &value, int width, bool retain, int qos) {
  String *foo = new String(value, width);
  messageSend(topic, *foo, retain, qos);
}

helps

Just to be clear, since I see no conditional tests, you are sending every time a float and then an int. Am I right?

And thanks again.

mitra42 commented 1 month ago

Sorry, no the width variable is misnamed, should be "decimalplaces" so String(1.2345678,3) becomes "1.234"

https://docs.arduino.cc/language-reference/en/variables/data-types/stringObject/