How to use ArduinoJson with PubSubClient?
This page explains how to use ArduinoJson with PubSubClient, a popular MQTT library for Arduino. It shows how to use the JSON format in MQTT messages, but you can easily adapt the examples to use MessagePack instead.
Deserializing a JSON document in MQTT message
Once your program has subscribed to an MQTT topic, you can call deserializeJson()
from the callback
function.
void callback(char* topic, byte* payload, unsigned int length) {
JsonDocument doc;
deserializeJson(doc, payload, length);
// use the JsonDocument as usual...
}
Avoiding dangling pointers
Even if you don’t use a global JsonDocument
, the callback is a great place to create dangling pointers. Make sure you never store a pointer that you acquired in the callback:
// BAD EXAMPLE: DON'T DO THAT!!!
const char *name = NULL;
void callback(char* topic, byte* payload, unsigned int length) {
JsonDocument doc;
deserializeJson(doc, payload, length);
name = doc["name"]; // 💀 pointer is invalid as soon as function returns
}
Instead, make a copy of the string:
const char name[32] = "";
void callback(char* topic, byte* payload, unsigned int length) {
JsonDocument doc;
deserializeJson(doc, payload, length);
strlcpy(name, doc["name"] | "default", sizeof(name));
}
Of course, you can use a String
if you prefer.
Serializing a JSON document into an MQTT message
To publish a JSON document to an MQTT topic, you need to serialize it to a temporary buffer:
char buffer[256];
serializeJson(doc, buffer);
client.publish("outTopic", buffer);
You can save a few CPU cycles by passing the size of the payload to publish()
:
char buffer[256];
size_t n = serializeJson(doc, buffer);
client.publish("outTopic", buffer, n);
By default, PubSubClient limits the message size to 256 bytes (including header); see the documentation.
Can we avoid the temporary buffer?
It’s tempting to remove the temporary buffer to save some memory. For example, we can write:
client.beginPublish(topic, measureJson(doc), retained);
serializeJson(doc, client);
client.endPublish();
However, this code is much slower than the one with a temporary buffer (100 to 200 times slower from our experience).
This slowness is due to the Client
class that sends bytes one by one. To improve the speed, we need to insert a small buffer as shown in How to improve (de)serialization speed?:
client.beginPublish(topic, measureJson(doc), retained);
BufferingPrint bufferedClient(client, 32);
serializeJson(doc, bufferedClient);
bufferedClient.flush();
client.endPublish();
Notice that I used a BufferingPrint
instead of WriteBufferingClient
because, despite its name, the PubSubClient
class doesn’t implement the Client
but the Print
interface. For more information on BufferingPrint
, see the README of the StreamUtils library.
This works, but as you can see, it’s much more complicated than the original code, so I don’t think it’s worth the effort.