ESP32: Subscribing to MQTT topic

The objective of this post is to explain how to connect to a MQTT broker and subscribe to a topic, using the ESP32 and the Arduino IDE libraries.


Introduction

The objective of this post is to explain how to connect to a MQTT broker and subscribe to a topic, using the ESP32 and the Arduino IDE libraries.

We will assume that the broker will be hosted on CloudMQTT. We are also going to use a MQTT library, called PubSubClient, which will expose the functionality needed to connect to the broker and subscribe to a topic.

Since we have already covered most of the coding needed to connect to the broker in this previous post, we will do a shorter explanation here.

 

The code

First, we start by including the libraries needed for all the functionality. We need the WiFi library, in order to be able to connect the ESP32 to a WiFi network, and the PubSubClient library, which will make available the MQTT related functionalities.

After that, we declare some global variables for holding the credentials of the connections. We need the WiFi credentials, to connect to the WiFi network. We will also need the information and credentials of the MQTT server. We will need the server address, the port, the username and the password, which can be obtained in the instance information page of CloudMQTT.

Then, we will declare an object of class WiFiClient, which will allow us to create a connection to a certain IP and port. We will also declare an object of class PubSubClient, which receives as input of the constructor the previously defined WiFiClient object.

#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 = "yourMQTTuser";
const char* mqttPassword = "yourMQTTpassword";

WiFiClient espClient;
PubSubClient client(espClient);

Now, in the setup function, we will open a Serial connection, to output the results of the program. We will also establish the connection to the WiFi network.

Next, we need to specify the address and the port of the MQTT server.  To do so, we call the setServer method on the PubSubClient object. This method will receive as first argument the address and as second the port, both defined early in global variables.

Then, we use the setCallback method on the same object to specify a handling function. This handling function will be executed when a MQTT message is received on a subscribed topic. We will leave the code of this function for latter.

Serial.begin(115200);

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);
client.setCallback(callback);

Next, we will connect to the MQTT server. We will do it in a loop until we get success. You can check this previous post for a more detailed explanation of the methods used.

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);

    }
}

Finally, we will subscribe to the topic we want. This way, we will receive messages published on that topic from other clients. To do so, we call the subscribe method, which receives as input the name of the topic to which we want to subscribe. The topic for this tutorial will be “esp/test”.

client.subscribe("esp/test");


The callback function

As said before, we still need to specify the callback function, to execute when a message is received for a subscribed topic. The arguments of this callback function are the name of the topic, the payload (in bytes) and the length of the message received. The message should also return void.

As can be seen in the code bellow, we will first print the topic name and then each byte of the message received. In the end we will also print some separator characters, to differentiate messages received.

void callback(char* topic, byte* payload, unsigned int length) {

  Serial.print("Message arrived in topic: ");
  Serial.println(topic);

  Serial.print("Message:");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }

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

}


The main loop

In the main loop function, we will need to call the loop method of the PubSubClient. As indicated in the documentation of the library, the function should be called on a regular basis,  in order to allow the client to process incoming messages and maintain the connection to the MQTT server.

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

Check the full code bellow.

#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 = "yourMQTTuser";
const char* mqttPassword = "yourMQTTpassword";

WiFiClient espClient;
PubSubClient client(espClient);

void callback(char* topic, byte* payload, unsigned int length) {

  Serial.print("Message arrived in topic: ");
  Serial.println(topic);

  Serial.print("Message:");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
  }

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

}

void setup() {

  Serial.begin(115200);

  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);
  client.setCallback(callback);

  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);

    }
  }

  client.subscribe("esp/test");

}

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

 

Testing the code

To test the code, just upload it to the ESP32 and open the Arduino IDE serial monitor.

As usual, we are also going to use MQTTLens for the tests. So, we just need to open it and publish a message to the post to which the ESP32 has subscribed, as shown in figure 1.

MQTTLens Sending message to ESP32 topic

Figure 1 – Sending message to MQTT topic, from MQTTLens.

In the Arduino IDE serial monitor, we should get a result similar to figure 2, where the message previously sent to the topic is printed. In this case, I’ve sent a couple of them.

ESP32 reading message from subscribed MQTT topic

Figure 2 – Getting messages from subscribed MQTT topic.


Related Content


Related Posts


Technical details

  • PubSubClient library: v2.6.0

16 thoughts on “ESP32: Subscribing to MQTT topic”

  1. Pingback: LinkIt Smart 7688 Duo: Subscribing to MQTT topic | techtutorialsx

  2. Pingback: LinkIt Smart 7688 Duo: Subscribing to MQTT topic | techtutorialsx

  3. Pingback: LinkIt Smart 7688 Duo: Publishing messages to MQTT topic | techtutorialsx

  4. Pingback: LinkIt Smart 7688 Duo: Publishing messages to MQTT topic | techtutorialsx

  5. Pingback: ESP32: Sending JSON messages over MQTT | techtutorialsx

  6. Pingback: ESP32: Sending JSON messages over MQTT | techtutorialsx

  7. Pingback: mosquitto stuff | KitShop experience

  8. Pingback: mosquitto stuff | KitShop experience

  9. Timothy Malche

    This is good example but can you explain how to store value from client.subscribe(“esp/test”) to a global variable?

    1. Hi! Thanks for the feedback 🙂

      I’m not sure if I understood your question, do you want to store the argument of that method call on a global variable, or the value that is published to the subscribed topic?

      Let me know so I can try to help 🙂

      Best regards,
      Nuno Santos

      1. Timothy Malche

        No. I want to store the value read from the subscribed topic in a variable and display value using variable to serial monitor.

        1. Hi. One simple solution is declaring a byte buffer as global variable and copy the content returned in the topic callback function to that buffer. Note that you need to know the maximum message size beforehand to declare a buffer big enough.

          You can also keep the last message size on a variable.

          For example, below the credentials you can declare something like:

          byte payloadBuffer[100];
          int lastMessageSize;

          Then in the callback function:

          for(int i = 0; i<length; i++) payloadBuffer[i] = payload[i];
          lastMessageSize = length;

          Finally, you can print the global buffer where you need:
          for(int i = 0; i<lastMessageSize; i++) Serial.print(payloadBuffer[i]);

          This is just one of the many ways you can do it, not necessarily the best one for your use case, but I think it shows the idea 🙂

          Hope this helps.
          Best regards,
          Nuno Santos

  10. Timothy Malche

    This is good example but can you explain how to store value from client.subscribe(“esp/test”) to a global variable?

    1. Hi! Thanks for the feedback 🙂
      I’m not sure if I understood your question, do you want to store the argument of that method call on a global variable, or the value that is published to the subscribed topic?
      Let me know so I can try to help 🙂
      Best regards,
      Nuno Santos

      1. Timothy Malche

        No. I want to store the value read from the subscribed topic in a variable and display value using variable to serial monitor.

        1. Hi. One simple solution is declaring a byte buffer as global variable and copy the content returned in the topic callback function to that buffer. Note that you need to know the maximum message size beforehand to declare a buffer big enough.
          You can also keep the last message size on a variable.
          For example, below the credentials you can declare something like:
          byte payloadBuffer[100];
          int lastMessageSize;
          Then in the callback function:
          for(int i = 0; i<length; i++) payloadBuffer[i] = payload[i];
          lastMessageSize = length;
          Finally, you can print the global buffer where you need:
          for(int i = 0; i<lastMessageSize; i++) Serial.print(payloadBuffer[i]);
          This is just one of the many ways you can do it, not necessarily the best one for your use case, but I think it shows the idea 🙂
          Hope this helps.
          Best regards,
          Nuno Santos

Leave a Reply

Discover more from techtutorialsx

Subscribe now to keep reading and get access to the full archive.

Continue reading