ESP32 MQTT Client Publish and Subscribe using ESP-IDF

In this tutorial, we will learn to use ESP32 as an MQTT client using ESP-IDF mqtt_client library. We will learn to publish and subscribe to MQTT topics with ESP32. Firstly, we will see how to make an ESP32 connection with a mosquito MQTT broker as a client. Secondly, we will learn to use mqtt_client library available in ESP-IDF to connect ESP32 with the MQTT broker, subscribe to different topics, and also how to publish messages on different topics. For demonstration, we will use the MQTTx client Desktop application. We will subscribe to MQTT topics and publish messages using MQTTx. We will install Mosquitto MQTT Broker on Raspberry Pi but you can install it on your Windows and Linux machine also.

ESP32 MQTT Client Publish and Subscribe using ESP-IDF

Before we move ahead, make sure you have the latest version of VS Code installed on your system with the ESP-IDF extension configured.

ESP32 MQTT Publish and Subscribe with ESP-IDF

Lets create a sample project in ESP-IDF and program our ESP32 board with the MQTT sketch. We will discuss the mqtt_client.h APIs that will be required for this project as well.

Create Example Project

Open your VS Code and head over to View > Command Palette. Type ESP-IDF: New Project in the search bar and press enter.

Specify the project name and directory, along with the ESP-IDF board and target. For the ESP-IDF board, we have chosen the custom board option. For ESP-IDF target, we have chosen ESP32 module. Click ‘Choose Template’ button to proceed forward.

In the Extension, select ESP-IDF option:

ESP-IDF in VS Code New Project 2

We will click the ‘sample_project’ under the get-started tab. Now click ‘Create project using template sample_project.’

ESP-IDF in VS Code New Project 3

You will get a notification that the project has been created. To open the project in a new window, click ‘Yes.’

This opens our project that we created inside the EXPLORER tab. There are several folders inside our project folder. This is the same for every project which you will create through ESP-IDF Explorer.

ESP32 ESP-IDF MQTT Code

The main folder contains the source code meaning the main.c file will be found here. Go to main > main.c and open it. Copy the code given below and save it. The following code uses event handlers to check various Wi-Fi and MQTT events, connect ESP32 with the local Wi-Fi and the MQTT broker, subscribe to two topics (/topic/test1, /topic/test2) and publish to the topic (/topic/test3) after every few seconds.

#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include "esp_wifi.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "protocol_examples_common.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"

#include "lwip/sockets.h"
#include "lwip/dns.h"
#include "lwip/netdb.h"

#include "esp_log.h"
#include "mqtt_client.h"

static const char *TAG = "MQTT_EXAMPLE";
#define  EXAMPLE_ESP_WIFI_SSID "YOUR_SSID"
#define  EXAMPLE_ESP_WIFI_PASS "YOUR_PASSWORD"
 
uint32_t MQTT_CONNEECTED = 0;

static void mqtt_app_start(void);

static esp_err_t event_handler(void *ctx, system_event_t *event)
{
    switch (event->event_id)
    {
    case SYSTEM_EVENT_STA_START:
        esp_wifi_connect();
        ESP_LOGI(TAG, "Trying to connect with Wi-Fi\n");
        break;

    case SYSTEM_EVENT_STA_CONNECTED:
        ESP_LOGI(TAG, "Wi-Fi connected\n");
        break;

    case SYSTEM_EVENT_STA_GOT_IP:
        ESP_LOGI(TAG, "got ip: startibg MQTT Client\n");
        mqtt_app_start();
        break;

    case SYSTEM_EVENT_STA_DISCONNECTED:
        ESP_LOGI(TAG, "disconnected: Retrying Wi-Fi\n");
        esp_wifi_connect();
        break;

    default:
        break;
    }
    return ESP_OK;
}

void wifi_init(void)
{
    ESP_ERROR_CHECK(esp_netif_init());

    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_create_default_wifi_sta();

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));

    wifi_config_t wifi_config = {
        .sta = {
            .ssid = EXAMPLE_ESP_WIFI_SSID,
            .password = EXAMPLE_ESP_WIFI_PASS,
	     .threshold.authmode = WIFI_AUTH_WPA2_PSK,
        },
    };
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
    ESP_ERROR_CHECK(esp_wifi_start());
}

/*
 * @brief Event handler registered to receive MQTT events
 *
 *  This function is called by the MQTT client event loop.
 *
 * @param handler_args user data registered to the event.
 * @param base Event base for the handler(always MQTT Base in this example).
 * @param event_id The id for the received event.
 * @param event_data The data for the event, esp_mqtt_event_handle_t.
 */
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
    ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%d", base, event_id);
    esp_mqtt_event_handle_t event = event_data;
    esp_mqtt_client_handle_t client = event->client;
    int msg_id;
    switch ((esp_mqtt_event_id_t)event_id)
    {
    case MQTT_EVENT_CONNECTED:
        ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
        MQTT_CONNEECTED=1;
        
        msg_id = esp_mqtt_client_subscribe(client, "/topic/test1", 0);
        ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);

        msg_id = esp_mqtt_client_subscribe(client, "/topic/test2", 1);
        ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
        break;
    case MQTT_EVENT_DISCONNECTED:
        ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
        MQTT_CONNEECTED=0;
        break;

    case MQTT_EVENT_SUBSCRIBED:
        ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
        break;
    case MQTT_EVENT_UNSUBSCRIBED:
        ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
        break;
    case MQTT_EVENT_PUBLISHED:
        ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
        break;
    case MQTT_EVENT_DATA:
        ESP_LOGI(TAG, "MQTT_EVENT_DATA");
        printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
        printf("DATA=%.*s\r\n", event->data_len, event->data);
        break;
    case MQTT_EVENT_ERROR:
        ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
        break;
    default:
        ESP_LOGI(TAG, "Other event id:%d", event->event_id);
        break;
    }
}

esp_mqtt_client_handle_t client = NULL;
static void mqtt_app_start(void)
{
    ESP_LOGI(TAG, "STARTING MQTT");
    esp_mqtt_client_config_t mqttConfig = {
        .uri = "mqtt://192.168.1.4:1883"};
    
    client = esp_mqtt_client_init(&mqttConfig);
    esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, client);
    esp_mqtt_client_start(client);
}

void Publisher_Task(void *params)
{
  while (true)
  {
    if(MQTT_CONNEECTED)
    {
        esp_mqtt_client_publish(client, "/topic/test3", "Helllo World", 0, 0, 0);
        vTaskDelay(15000 / portTICK_PERIOD_MS);
    }
  }
}

void app_main(void)
{
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
      ESP_ERROR_CHECK(nvs_flash_erase());
      ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);
    wifi_init();
    xTaskCreate(Publisher_Task, "Publisher_Task", 1024 * 5, NULL, 5, NULL);
}

How does the Code Works?

Firstly, we will start by including the necessary libraries that includes the FreeRTOS libraries to generate delays, esp_wifi.h to enable Wi-Fi connectivity, esp_event.h to monitor the Wi-Fi and MQTT events, mqtt_client.h to setup MQTT protocol for publish/subscribe and esp_log.h as the logging library.

#include <stdio.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include "esp_wifi.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "protocol_examples_common.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"

#include "lwip/sockets.h"
#include "lwip/dns.h"
#include "lwip/netdb.h"

#include "esp_log.h"
#include "mqtt_client.h"

This code uses Informational Logging. The log function takes in two arguments. The first argument is the tag and the second argument is a formatted string. Therefore this global variable will be useful while calling the ESP_LOGI() functions. Here, “MQTT_EXAMPLE” is the tag that will be used while logging.

static const char *TAG = "MQTT_EXAMPLE";

Next, we have defined the parameters for the Wi-Fi SSID and password. Specify your own network credentials to successfully connect your ESP32 board with the Wi-Fi network.

#define  EXAMPLE_ESP_WIFI_SSID "YOUR_SSID"
#define  EXAMPLE_ESP_WIFI_PASS "YOUR_PASSWORD"

Handling Wi-Fi Events

We have the event_handler() function which handles the Wi-Fi events. This acts as a callback function, when any Wi-Fi event occurs. There are a different events that the Wi-Fi driver posts to the event task. These include: SYSTEM_EVENT_STA_START, SYSTEM_EVENT_STA_CONNECTED, SYSTEM_EVENT_STA_GOT_IP and SYSTEM_EVENT_STA_DISCONNECTED. These events occur if Wi-Fi is connected, a station gets connected to the AP, Wi-Fi gets IP or if the station disconnects from the AP respectively.

In our event_handler() function, we will check if the Wi-Fi event id corresponds to either of these four events. If it does, then it will print a relevant message on the ESP-IDF according to the event type and call an appropriate function. If either SYSTEM_EVENT_STA_START event or SYSTEM_EVENT_STA_DISCONNECTED occurs, the function esp_wifi_connect() will be called. This will be responsible for connecting the ESP32 Wi-Fi station to the access point. If SYSTEM_EVENT_STA_GOT_IP event occurs, then the function mqtt_app_start() will be called to initiate the MQTT client connection.

static esp_err_t event_handler(void *ctx, system_event_t *event)
{
    switch (event->event_id)
    {
    case SYSTEM_EVENT_STA_START:
        esp_wifi_connect();
        ESP_LOGI(TAG, "Trying to connect with Wi-Fi\n");
        break;

    case SYSTEM_EVENT_STA_CONNECTED:
        ESP_LOGI(TAG, "Wi-Fi connected\n");
        break;

    case SYSTEM_EVENT_STA_GOT_IP:
        ESP_LOGI(TAG, "got ip: startibg MQTT Client\n");
        mqtt_app_start();
        break;

    case SYSTEM_EVENT_STA_DISCONNECTED:
        ESP_LOGI(TAG, "disconnected: Retrying Wi-Fi\n");
        esp_wifi_connect();
        break;

    default:
        break;
    }
    return ESP_OK;
}

Initialize Wi-Fi

The wifi_init() function will be called inside the app_main() function to initialize Wi-Fi in station mode for ESP32.

void wifi_init(void)
{
    ESP_ERROR_CHECK(esp_netif_init());

    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_create_default_wifi_sta();

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));

    ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));

    wifi_config_t wifi_config = {
        .sta = {
            .ssid = EXAMPLE_ESP_WIFI_SSID,
            .password = EXAMPLE_ESP_WIFI_PASS,
	     .threshold.authmode = WIFI_AUTH_WPA2_PSK,
        },
    };
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
    ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
    ESP_ERROR_CHECK(esp_wifi_start());
}

The ESP-IDF provides a useful macro called ESP_ERROR_CHECK() that will be used to check whether an error occurred or not. If the function that we pass inside it does not return ESP_OK, then we assert and log the line.

Here we are initializing lwIP through the function esp_netif_init() and create an IwIP task which is also known as a TCP/IP task. lwIP is a TCP/IP library stack provided by ESP-IDF that is used to perform various protocols such as TCP, UDP, DHCP etc.

ESP_ERROR_CHECK(esp_netif_init());

Next, we create a default event loop which enables the system events to be sent to the event task.

ESP_ERROR_CHECK(esp_event_loop_create_default());

Moreover, the following line is used for Wi-Fi default initialization for station mode. This API is used to initialize as well as register event handlers for the default interface which is station in our case. It creates a network interface instance binding Wi-Fi driver and TCP/IP stack. When a station is in the process of connecting to an AP, various processes automatically get handled through this function.

esp_netif_create_default_wifi_sta();

Then we initialize the Wi-Fi allocate resource for the Wi-Fi driver. It is responsible for initiating the Wi-Fi task. It takes in a single parameter cfg which is a pointer to the already initialized Wi-Fi configuration structure which is set to WIFI_INIT_CONFIG_DEFAULT() so that the initialization of the configuration is at default values. Here wifi_init_config_t is a structure that denotes the Wi-Fi stack configuration parameters that are passed inside the esp_wifi_init() function.

wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));

The esp_event_loop_init() function is then called which initializes the event loop used to dispatch event callbacks for Wi-Fi, IP, and Ethernet related events.

ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));

In this part, we set up the SSID and password of the network we want our ESP32 to connect with, as well as assign the security setting.

  wifi_config_t wifi_config = {
        .sta = {
            .ssid = EXAMPLE_ESP_WIFI_SSID,
            .password = EXAMPLE_ESP_WIFI_PASS,
	     .threshold.authmode = WIFI_AUTH_WPA2_PSK,
        },
    };

Moreover, we set Wi-Fi mode as station. To set the Wi-Fi mode as station, we use the function esp_wifi_set_mode() and pass the WiFI_MODE_STA as a parameter inside it.

ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );

Then we assign our network’s SSID and password. This way the Wi-Fi driver gets configured with the AP network credentials and Wi-Fi mode. Then, we start the Wi-Fi connection at our assigned configuration.

ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
ESP_ERROR_CHECK(esp_wifi_start() );

Handling MQTT Events

Next, we have the mqtt_event_handler() function which handles the MQTT events. This function is called by the MQTT client event loop. We will check if the event id corresponds to either of the seven events which can be MQTT_EVENT_CONNECTED, MQTT_EVENT_DISCONNECTED, MQTT_EVENT_SUBSCRIBED, MQTT_EVENT_UNSUBSCRIBED, MQTT_EVENT_PUBLISHED, MQTT_EVENT_DATA or MQTT_EVENT_ERROR. If it does, then it will print a relevant message on the ESP-IDF according to the event type and call an appropriate function.

The table below shows the descriptions of the MQTT events that will be used in this example.

MQTT_EVENT_CONNECTEDThis event is posted when the client successfully connects to the broker and is ready to send/receive data.
MQTT_EVENT_DISCONNECTEDThis event is posted when the client cancels the connection between the broker as the server is not available. The client is not able to send/receive data in this scenario.
MQTT_EVENT_SUBSCRIBEDThis event is posted when the broker accepts the subscribe request of the client. The event data also holds the message ID of the subscribe message.
MQTT_EVENT_UNSUBSCRIBEDIn this case, the broker accepts the unsubscribe request of the client. The event data holds the message ID of the unsubscribe message.
MQTT_EVENT_PUBLISHEDThis event is posted when the broker acknowledges the publish message of the client. The event data holds the message ID of the publish message.
Note that if the qos level is set as 0, then this will not be posted as it does not use acknowledgements. It will only be posted for qos level 1 or 2.
MQTT_EVENT_DATAThis event is posted when the client receives a publish message. In this case, the event data holds the message ID, name of the topic it was published to, received data and data length.
MQTT_EVENT_ERRORThis events gets posted by the client when it experiences an error.
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
    ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%d", base, event_id);
    esp_mqtt_event_handle_t event = event_data;
    esp_mqtt_client_handle_t client = event->client;
    int msg_id;
    switch ((esp_mqtt_event_id_t)event_id)
    {
    case MQTT_EVENT_CONNECTED:
        ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
        MQTT_CONNEECTED=1;
        
        msg_id = esp_mqtt_client_subscribe(client, "/topic/test1", 0);
        ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);

        msg_id = esp_mqtt_client_subscribe(client, "/topic/test2", 1);
        ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
        break;
    case MQTT_EVENT_DISCONNECTED:
        ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
        MQTT_CONNEECTED=0;
        break;

    case MQTT_EVENT_SUBSCRIBED:
        ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
        break;
    case MQTT_EVENT_UNSUBSCRIBED:
        ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
        break;
    case MQTT_EVENT_PUBLISHED:
        ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
        break;
    case MQTT_EVENT_DATA:
        ESP_LOGI(TAG, "MQTT_EVENT_DATA");
        printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
        printf("DATA=%.*s\r\n", event->data_len, event->data);
        break;
    case MQTT_EVENT_ERROR:
        ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
        break;
    default:
        ESP_LOGI(TAG, "Other event id:%d", event->event_id);
        break;
    }
}

If the event MQTT_EVENT_CONNECTED is posted by the client, the “MQTT_CONNECTED” variable is set to the value 1, which initially held the value 0. After that, the esp_mqtt_client_subscribe() function is called to subscribe the client to the defined topic with defined qos. This function takes in three parameters which are the MQTT client handle, the topic and the qos value respectively. It returns the message_id on success which gets saved in the variable ‘msg_id.’

In our case, we are subscribing to two topics:

  • /topic/topic1 with qos 0
  • /topic/topic2 with qos 1

The ESP-IDF terminal prints the message ID along with a message that a successful subscribe was sent.

    case MQTT_EVENT_CONNECTED:
        ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
        MQTT_CONNEECTED=1;
        
        msg_id = esp_mqtt_client_subscribe(client, "/topic/test1", 0);
        ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);

        msg_id = esp_mqtt_client_subscribe(client, "/topic/test2", 1);
        ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
        break;

If the event MQTT_EVENT_DISCONNECTED is posted by the client, the MQTT_CONNECTED variable is set to the value 0 and the terminal prints the message indicating that this event occurred. Similarly, for the rest of the events, a relevant message is printed on the terminal along with the message_id. For the event, MQTT_EVENT_DATA, the topic name along with the data is also printed.

case MQTT_EVENT_DISCONNECTED:
        ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
        MQTT_CONNEECTED=0;
        break;

    case MQTT_EVENT_SUBSCRIBED:
        ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
        break;
    case MQTT_EVENT_UNSUBSCRIBED:
        ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
        break;
    case MQTT_EVENT_PUBLISHED:
        ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
        break;
    case MQTT_EVENT_DATA:
        ESP_LOGI(TAG, "MQTT_EVENT_DATA");
        printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
        printf("DATA=%.*s\r\n", event->data_len, event->data);
        break;
    case MQTT_EVENT_ERROR:
        ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
        break;
    default:
        ESP_LOGI(TAG, "Other event id:%d", event->event_id);
        break;

mqtt_app_start()

Inside the mqtt_app_start() function, an MQTT client handle is created according to the configuration parameters set using esp_mqtt_client_init(). This function takes in a single parameter which is the pointer to the MQTT configuration structure. The MQTT configuration structure holds the MQTT Broker IP address which acts as the mqtt server. Make sure to specify the IP address of your Raspberry Pi which has Mosquitto broker running on it, inside the MQTT configuration structure.

After that we will call the function esp_mqtt_client_register_event() which is used to register the MQTT event. It takes in four parameters. The first parameter is the MQTT client handle. The second parameter is the event type. The third parameter is the handler callback and the last parameter is the handle context which is mqtt_client_handle, ‘client’ in our case. Finally, we will start the MQTT client using the function esp_mqtt_client_start() and specify the already created client handle as a parameter inside it.

esp_mqtt_client_handle_t client = NULL;
static void mqtt_app_start(void)
{
    ESP_LOGI(TAG, "STARTING MQTT");
    esp_mqtt_client_config_t mqttConfig = {
        .uri = "mqtt://192.168.1.4:1883"};
    
    client = esp_mqtt_client_init(&mqttConfig);
    esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, client);
    esp_mqtt_client_start(client);
}

Inside the Publisher_Task() function, if MQTT_CONNECTED event is posted, then the client publishes the message “Hello World” to the broker after every 15 seconds. The esp_mqtt_client_publish() function is responsible for publishing the message. It takes in six parameters which include the MQTT client handle, the topic string, the data to be published, data length, qos and the retain flag respectively.

void Publisher_Task(void *params)
{
  while (true)
  {
    if(MQTT_CONNEECTED)
    {
        esp_mqtt_client_publish(client, "/topic/test3", "Helllo World", 0, 0, 0);
        vTaskDelay(15000 / portTICK_PERIOD_MS);
    }
  }
}

app_main()

Inside the app_main() function, Inside the app_main() function, first NVS is initialized by calling the nvs_flash_init() function. Then we will check if the NVS partition does not contain any empty pages or if it contains data in a format which is different from the current version of code. If any of these condition is true then we will erase the NVS flash using nvs_flash_erase() and initialize it again. Then, wifi_init() function is called which will initialize Wi-Fi in station mode for ESP32. After that use xTaskCreate() to create the Publisher_Task that will publish the Hello World message to the broker after every 15 seconds.

void app_main(void)
{
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
      ESP_ERROR_CHECK(nvs_flash_erase());
      ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);
    wifi_init();
    xTaskCreate(Publisher_Task, "Publisher_Task", 1024 * 5, NULL, 5, NULL);
}

Compiling the Sketch

To flash your chip, type the following command in the serial terminal. Remember to replace the COM port with the one through which your board is connected.

idf.py -p COMX flash monitor

After the code is successfully flashed, you can view different messages on the ESP-IDF terminal. First the Wi-Fi gets successfully connected.

Publishing and Subscription using MQTT X Client

For testing purposes we will use the MQTT 5.0 client tool – MQTT X. To download this application follow the link: https://mqttx.app/

After the installation is complete, open MQTT X application. Click ‘+ New Connection’ button.

ESP-IDF MQTT Publish and Subscribe using MQTTX Add connection

Add the details as shown below. Under Host, specify the IP address of your Raspberry Pi which has Mosquito broker running on it. Then click ‘Connect’ to start the connection between the client and the server.

ESP-IDF MQTT Publish and Subscribe using MQTTX Add connection 2

The connection has become active as indicated by the green dot under Connections.

ESP-IDF MQTT Publish and Subscribe using MQTTX connect

We have subscribed our ESP32 board to two topics inside our code. Let us check the topic subscriptions. Add the topic ‘/topic/test1’ and the message that you want to send. Click the send button.

ESP-IDF MQTT Publish and Subscribe using MQTTX check subscription 1

The ESP32 receives the message, as seen on the ESP-IDF terminal below.

ESP-IDF MQTT Publish and Subscribe using MQTTX check subscription terminal 2

Similarly, we will check the subscription for the second topic: /topic/test2 in the same manner.

ESP-IDF MQTT Publish and Subscribe using MQTTX check subscription 2

Now go to ‘+ New Subscription.’ Subscribe the topic: /topic/test3 to which the ESP32 board is publishing a message every few seconds. Click Confirm.

ESP-IDF MQTT Publish and Subscribe using MQTTX Add subscription topic

The ESP32 publishes the message “Hello World” which the server has subscribed to.

ESP-IDF MQTT Publish and Subscribe using MQTTX check publish

In the figure below, you can view that the ESP32 board is publishing the message “Hello World” on the topic: /topic/topic3 after every few seconds.

ESP-IDF MQTT Publish and Subscribe using MQTTX publish and subscribe check

Meanwhile the subscription topics are also working accordingly as the ESP32 receives the messages sent from the application, as seen on the ESP-IDF terminal below.

ESP-IDF MQTT Publish and Subscribe using MQTTX check subscription terminal

You may also like to read:

5 thoughts on “ESP32 MQTT Client Publish and Subscribe using ESP-IDF”

  1. This code doesn’t work:

    unknown type name ‘system_event_t’
    Type ‘system_event_t’ could not be resolved
    Symbol ‘SYSTEM_EVENT_STA_START’ could not be resolved
    Symbol ‘SYSTEM_EVENT_STA_GOT_IP’ could not be resolved
    Symbol ‘SYSTEM_EVENT_STA_DISCONNECTED’ could not be resolved
    Symbol ‘SYSTEM_EVENT_STA_CONNECTED’ could not be resolved
    missing braces around initializer [-Werror=missing-braces]
    implicit declaration of function ‘esp_event_loop_init’; did you mean ‘esp_event_loop_run’? [-Werror=implicit-function-declaration]
    format ‘%d’ expects argument of type ‘int’, but argument 7 has type ‘int32_t’ {aka ‘long int’} [-Werror=format=]
    format ‘%d’ expects argument of type ‘int’, but argument 7 has type ‘int32_t’ {aka ‘long int’} [-Werror=format=]
    Field ‘event_id’ could not be resolved
    ‘event_handler’ undeclared (first use in this function); did you mean ‘xt_handler’?
    ‘esp_mqtt_client_config_t’ has no member named ‘uri’

    I’m using:
    Operating System: windows 10
    Java Runtime Version: 17.0.6+10-LTS
    Eclipse Version: 4.25.0.v20220831-1800
    Eclipse CDT Version: 10.7.1.202208160035
    IDF Eclipse Plugin Version: 2.9.1.202304060814
    ESP-IDF v5.0.1-dirty
    Python set for IDF_PYTHON_ENV: Python 3.11.2

    Reply
  2. I am having esp-idf VSCODE on windows10. The code does not work.
    error: unknown type name ‘system_event_
    32 | static esp_err_t event_handler(void *ctx, system_event_t *event)

    error: implicit declaration of function ‘esp_event_loop_run’? [-Werror=implicit-function-declaration]
    71 | ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));
    error: ‘esp_mqtt_client_config_t’ has
    146 | .uri = “mqtt://192.168.1.4:1883”};
    | ^~~
    D:/espidfproj/mqttnew/main/main.c:145:43: error: missing braces around initializ
    145 | esp_mqtt_client_config_t mqttConfig = {
    | ^
    146 | .uri = “mqtt://192.168.1.4:1883”};
    | {{ }}

    Reply

Leave a Comment