ESP32: Sending JSON messages over MQTT

The objective of this post is to explain how to send JSON messages over MQTT using the ESP32.


Introduction

The objective of this post is to explain how to send JSON messages over MQTT using the ESP32. To do so, we will be using two libraries that handle the low level details and expose us both the JSON encoding and the MQTT publishing functionalities in easy to use interfaces. These libraries are the PubSubClient, for the MQTT related functionality, and the ArduinoJson, for the JSON related functionality.

Both of the libraries work with the ESP8266 and the ESP32 and have some examples to help us getting started, which I encourage you to try. Also, I’ve been covering their use with both devices in previous posts, which are listed in the related posts section bellow.

As MQTT broker, we will use CloudMQTT, which offers a free plan that allows us to create an account and test an instance of a broker. If you haven’t done it yet, please register and create an instance. We will need the instance information (address, port, username and password) latter.


The code

The first thing we need to do is including both the ArduinoJson.h and the PubSubClient.h libraries, in order to be able to access all the classes needed. We will also need to include the WiFi.h library, so we can connect to a WiFi network and thus be able to publish the messages to a MQTT topic.

In order to make the code more readable and easy to modify, we will declare some global variables to hold the credentials needed to connect to the WiFi network and to the MQTT broker. For the connection to the MQTT broker, we will need the information about the instance created, which is available in CloudMQTT instance information page.

#include <ArduinoJson.h>
#include <WiFi.h>
#include <PubSubClient.h>

const char* ssid = yourNetworkName";
const char* password = "yourNetworkPassword";

const char* mqttServer = "m11.cloudmqtt.com";
const int mqttPort = 12948;
const char* mqttUser = "yourInstanceUsername";
const char* mqttPassword = "yourInstancePassword";

After that, we will declare an object of class WiFiClient. We will also declare an object of class PubSubClient, which receives as input of the constructor the previously declared WiFiClient instance. Note that we are not going to use the WiFiClient in our code, since it will be used by the PubSubClient library. So, we don’t need to worry about the implementation details.

WiFiClient espClient;
PubSubClient client(espClient);

After declaring the global variables, we will define the setup function. The first thing we do is opening a serial connection. Then, we connect to the WiFi network. Check here a detailed post which explains how to connect to a WiFi network with the ESP32.

WiFi.begin(ssid, password);

while (WiFi.status() != WL_CONNECTED) {
  delay(500);
  Serial.println("Connecting to WiFi..");
}

Serial.println("Connected to the WiFi network");

Next, we need to specify the address and the port of the MQTT broker we want to connect to.  To do so, we call the setServer method on the PubSubClient object, passing as inputs the address and the port. These arguments were the ones specified as global variables and obtained from the CloudMQTT instance page information.

Now, we will connect to the MQTT server by calling the connect method. This method receives as input a string which corresponds to the unique identifier of the client (we will use “ESP32Client”). It also receives both the username and password needed to connect to the broker,  available at the CloudMQTT instance page information.

This method call will return true on connection success and false otherwise. We will do the connection attempt in a loop.

while (!client.connected()) {
    Serial.println("Connecting to MQTT...");

    if (client.connect("ESP32Client", mqttUser, mqttPassword )) {

      Serial.println("connected");

    } else {

      Serial.print("failed with state ");
      Serial.print(client.state());
      delay(2000);

    }
  }

In the previous loop, we used two additional methods. The connected method returns true if the connection is established or false if not. Thus, we use it as stopping condition for our connection loop.

The state method will return a code with information about why the connection failed. So, we use the value to print the reason of failure when it is not possible to connect to the broker.

After this, we should already be connected to the MQTT broker and we can start publishing messages to topics. Nevertheless, we will do this on the main loop function, and thus we don’t need more code on the setup function.

Now, in the main loop function, we declare an object of class StaticJsonBuffer, which will be used for creating the JSON message to be sent over MQTT. We will need to specify the capacity of the buffer in bytes as a template parameter. Then, we get a reference to a JsonObject from the StaticJsonBuffer object we created, by calling the createObject method.

StaticJsonBuffer<300> JSONbuffer;
JsonObject& JSONencoder = JSONbuffer.createObject();

We will create a JSON message as indicated bellow. Note that we will create a message with static content just for keeping the code simple, but we could obtain the values from a sensor or other dynamic source.

{
"device": "ESP32",
"sensorType" : "Temperature",
"Value" : [20,21,23]
}

Next, we add the values to our JsonObject. You can check in more detail in this previous post how to do it.

JSONencoder["device"] = "ESP32";
JSONencoder["sensorType"] = "Temperature";
JsonArray& values = JSONencoder.createNestedArray("values");

values.add(20);
values.add(21);
values.add(23);

Now, we will print the JSON message to a char buffer by using the printTo method on the JsonObject reference. This method prints the message in a compact format, so it has the less overhead possible.

char JSONmessageBuffer[100];
JSONencoder.printTo(JSONmessageBuffer, sizeof(JSONmessageBuffer));

Next, we send the JSON message to a topic called “esp/test“. We do it by calling the publish method on our PubSubClient object. This method will receive as inputs both the topic name and the message we want to publish.

As can be seen here, this method returns “true” if the publish is successful and “false” otherwise. We will use this information for error handling.

Finally, we call the loop method on the same object reference. This should be called regularly so our program can process incoming messages and maintain the connection to the MQTT server. Since we are going to be sending the messages in the Arduino main loop function, with a small delay between each message, we call the loop method at the end of each iteration.

Serial.println(JSONmessageBuffer);

if (client.publish("esp/test", JSONmessageBuffer) == true) {
    Serial.println("Success sending message");
} else {
    Serial.println("Error sending message");
}

client.loop();

You can check final source code bellow, with some additional prints to help debugging.

#include <ArduinoJson.h>
#include <WiFi.h>
#include <PubSubClient.h>

const char* ssid = "yourNetworkName";
const char* password =  "yourNetworkPassword";
const char* mqttServer = "m11.cloudmqtt.com";
const int mqttPort = 12948;
const char* mqttUser = "yourInstanceUsername";
const char* mqttPassword = "yourInstancePassword";

WiFiClient espClient;
PubSubClient client(espClient);

void setup() {

  Serial.begin(115200);
  Serial.println();

  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.println("Connecting to WiFi..");
  }

  Serial.println("Connected to the WiFi network");

  client.setServer(mqttServer, mqttPort);

  while (!client.connected()) {
    Serial.println("Connecting to MQTT...");

    if (client.connect("ESP32Client", mqttUser, mqttPassword )) {

      Serial.println("connected");

    } else {

      Serial.print("failed with state ");
      Serial.print(client.state());
      delay(2000);

    }
  }

}

void loop() {

  StaticJsonBuffer<300> JSONbuffer;
  JsonObject& JSONencoder = JSONbuffer.createObject();

  JSONencoder["device"] = "ESP32";
  JSONencoder["sensorType"] = "Temperature";
  JsonArray& values = JSONencoder.createNestedArray("values");

  values.add(20);
  values.add(21);
  values.add(23);

  char JSONmessageBuffer[100];
  JSONencoder.printTo(JSONmessageBuffer, sizeof(JSONmessageBuffer));
  Serial.println("Sending message to MQTT topic..");
  Serial.println(JSONmessageBuffer);

  if (client.publish("esp/test", JSONmessageBuffer) == true) {
    Serial.println("Success sending message");
  } else {
    Serial.println("Error sending message");
  }

  client.loop();
  Serial.println("-------------");

  delay(10000);

}


Testing the code

To test the code, we will use MQTTLens to subscribe to the topic where the ESP32 will be publishing. So, open the application and subscribe to the “esp/test” topic.

After that, upload the code to the ESP32 and open the serial console. You should get an output similar to figure 1.

ESP32 Sending JSON over MQTT

Figure 1 – Output of the program.

Now, go back to MQTTLens. You should get an output similar to figure 2, with the JSON messages being printed. MQTTLens recognizes the JSON format and, although we sent the message in a compact format, it allows us to see it in an indented user friendly format.

MQTT receiving JSON over MQTT

Figure 2 – JSON messages sent by the ESP32.


Related posts


Technical details

  • ArduinoJson library: v5.9.0.
  • PubSubClient library: v2.6.0
Advertisements
This entry was posted in ESP32 and tagged , , , , , , , , , , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s