В этом уроке мы научимся использовать ESP32 в качестве MQTT-клиента, используя библиотеку mqtt_client из ESP-IDF. Мы научимся публиковать и подписываться на MQTT-темы с помощью ESP32. Сначала мы рассмотрим, как установить соединение ESP32 с MQTT-брокером Mosquitto в качестве клиента. Затем мы научимся использовать библиотеку mqtt_client, доступную в ESP-IDF, для подключения ESP32 к MQTT-брокеру, подписки на различные темы, а также для публикации сообщений в различных темах. Для демонстрации мы будем использовать настольное приложение MQTTx client. Мы будем подписываться на MQTT-темы и публиковать сообщения с помощью MQTTx. Мы установим MQTT-брокер Mosquitto на Raspberry Pi, но вы также можете установить его на свой компьютер с Windows и Linux.

Также на нашем сайте вы можете посмотреть и другие статьи, связанные с использованием MQTT:
- двунаправленный счетчик посетителей на NodeMCU ESP8266 и MQTT;
- метеостанция на ESP32/ESP8266 и BME680 с мониторингом качества воздуха по MQTT;
- подключение NodeMCU ESP8266 к MQTT брокеру с помощью Arduino IDE.
Публикация и подписка по протоколу MQTT на ESP32 с помощью ESP-IDF
Давайте создадим пример проекта в ESP-IDF и запрограммируем нашу плату ESP32 с помощью скетча MQTT. Мы также обсудим API из файла mqtt_client.h, которые потребуются для этого проекта.
Создание примера проекта
Откройте VS Code и перейдите в меню View > Command Palette («Вид» > «Палитра команд»). Введите в строке поиска ESP-IDF: New Project и нажмите Enter.
Укажите название проекта и каталог, а также плату и целевое устройство ESP-IDF. Для платы ESP-IDF мы выбрали опцию custom board ("пользовательская плата"). Для целевого устройства ESP-IDF мы выбрали модуль ESP32. Нажмите кнопку ‘Choose Template’ ("Выбрать шаблон"), чтобы продолжить.
В разделе «Расширение» выберите опцию ESP-IDF:
На вкладке get-started («Начало работы») мы щелкнем по пункту «sample_project». Затем нажмем ‘Create project using template sample_project’) («Создать проект, используя шаблон sample_project»).
Вы получите уведомление о создании проекта. Чтобы открыть проект в новом окне, нажмите «Да».
Это открывает наш проект, который мы создали во вкладке «Проводник». Внутри папки проекта находится несколько папок. Это справедливо для каждого проекта, который вы будете создавать с помощью ESP-IDF Explorer.
Код ESP32 для работы с MQTT
В папке main находится исходный код, то есть файл main.c будет расположен именно здесь. Перейдите в папку main > main.c и откройте его. Скопируйте приведенный ниже код и сохраните его. Следующий код использует обработчики событий для проверки различных событий Wi-Fi и MQTT, подключения ESP32 к локальной сети Wi-Fi и брокеру MQTT, подписки на две темы (/topic/test1, /topic/test2) и публикации в тему (/topic/test3) каждые несколько секунд.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
#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); } |
Данный код вы также можете скачать с Github.
Как работает код?
Для начала мы подключим необходимые библиотеки, включая библиотеки FreeRTOS для генерации задержек, esp_wifi.h для включения Wi-Fi-соединения, esp_event.h для мониторинга событий Wi-Fi и MQTT, mqtt_client.h для настройки протокола MQTT для публикации/подписки и esp_log.h в качестве библиотеки для ведения журналов.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#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" |
Этот код использует информационное логирование. Функция логирования принимает два аргумента. Первый аргумент — это тег, а второй — отформатированная строка. Поэтому эта глобальная переменная будет полезна при вызове функций ESP_LOGI(). Здесь «MQTT_EXAMPLE» — это тег, который будет использоваться при логировании.
|
1 |
static const char *TAG = "MQTT_EXAMPLE"; |
Далее мы определили параметры для SSID и пароля Wi-Fi. Укажите свои собственные учетные данные сети, чтобы успешно подключить плату ESP32 к сети Wi-Fi.
|
1 2 |
#define EXAMPLE_ESP_WIFI_SSID "YOUR_SSID" #define EXAMPLE_ESP_WIFI_PASS "YOUR_PASSWORD" |
Обработка событий Wi-Fi
У нас есть функция event_handler(), которая обрабатывает события Wi-Fi. Она действует как функция обратного вызова при возникновении любого события Wi-Fi. Драйвер Wi-Fi отправляет в задачу обработки событий различные события, в том числе: SYSTEM_EVENT_STA_START, SYSTEM_EVENT_STA_CONNECTED, SYSTEM_EVENT_STA_GOT_IP и SYSTEM_EVENT_STA_DISCONNECTED. Эти события происходят, если Wi-Fi подключен, станция подключается к точке доступа, Wi-Fi получает IP-адрес или станция отключается от точки доступа соответственно.
В нашей функции event_handler() мы проверим, соответствует ли идентификатор события Wi-Fi одному из этих четырех событий. Если да, то на ESP-IDF будет выведено соответствующее сообщение в соответствии с типом события, и будет вызвана соответствующая функция. Если произойдет событие SYSTEM_EVENT_STA_START или SYSTEM_EVENT_STA_DISCONNECTED, будет вызвана функция esp_wifi_connect(). Она будет отвечать за подключение станции Wi-Fi ESP32 к точке доступа. Если произойдет событие SYSTEM_EVENT_STA_GOT_IP, то будет вызвана функция mqtt_app_start() для инициирования подключения MQTT-клиента.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
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; } |
Инициализация Wi-Fi
Функция wifi_init() будет вызываться внутри функции app_main() для инициализации Wi-Fi в режиме станции для ESP32.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
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()); } |
ESP-IDF предоставляет полезный макрос ESP_ERROR_CHECK(), который используется для проверки наличия или отсутствия ошибки. Если функция, переданная в него, не возвращает ESP_OK, то мы проверяем наличие ошибки и записываем её в лог.
Здесь мы инициализируем lwIP с помощью функции esp_netif_init() и создаём задачу IwIP, также известную как задача TCP/IP. lwIP — это стек библиотек TCP/IP, предоставляемый ESP-IDF, который используется для выполнения различных протоколов, таких как TCP, UDP, DHCP и т. д.
|
1 |
ESP_ERROR_CHECK(esp_netif_init()); |
Далее мы создаём цикл обработки событий по умолчанию, который позволяет отправлять системные события в задачу обработки событий.
|
1 |
ESP_ERROR_CHECK(esp_event_loop_create_default()); |
Кроме того, следующая строка используется для инициализации Wi-Fi по умолчанию в режиме станции. Этот API используется для инициализации, а также регистрации обработчиков событий для интерфейса по умолчанию, которым в нашем случае является станция. Он создает экземпляр сетевого интерфейса, связывающий драйвер Wi-Fi и стек TCP/IP. Когда станция находится в процессе подключения к точке доступа, различные процессы автоматически обрабатываются с помощью этой функции.
|
1 |
esp_netif_create_default_wifi_sta(); |
Затем мы инициализируем ресурс выделения Wi-Fi для драйвера Wi-Fi. Он отвечает за запуск задачи Wi-Fi. Он принимает единственный параметр cfg, который является указателем на уже инициализированную структуру конфигурации Wi-Fi, установленную в значение WIFI_INIT_CONFIG_DEFAULT(), так что инициализация конфигурации происходит со значениями по умолчанию. Здесь wifi_init_config_t — это структура, обозначающая параметры конфигурации стека Wi-Fi, которые передаются в функцию esp_wifi_init().
|
1 2 |
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&cfg)); |
Затем вызывается функция esp_event_loop_init(), которая инициализирует цикл событий, используемый для отправки обратных вызовов событий, связанных с Wi-Fi, IP и Ethernet.
|
1 |
ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL)); |
В этой части мы задаём SSID и пароль сети, к которой хотим подключить наш ESP32, а также назначаем параметры безопасности.
|
1 2 3 4 5 6 7 |
wifi_config_t wifi_config = { .sta = { .ssid = EXAMPLE_ESP_WIFI_SSID, .password = EXAMPLE_ESP_WIFI_PASS, .threshold.authmode = WIFI_AUTH_WPA2_PSK, }, }; |
Кроме того, мы устанавливаем режим Wi-Fi как «станция». Для этого мы используем функцию esp_wifi_set_mode() и передаем в качестве параметра параметр WiFI_MODE_STA.
|
1 |
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) ); |
Затем мы назначаем SSID и пароль нашей сети. Таким образом, драйвер Wi-Fi настраивается с использованием учетных данных сети точки доступа и режима Wi-Fi. После этого мы запускаем Wi-Fi-соединение с заданными параметрами.
|
1 2 |
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) ); ESP_ERROR_CHECK(esp_wifi_start() ); |
Обработка событий MQTT
Далее у нас есть функция mqtt_event_handler(), которая обрабатывает события MQTT. Эта функция вызывается циклом обработки событий MQTT-клиента. Мы проверим, соответствует ли идентификатор события одному из семи возможных событий: MQTT_EVENT_CONNECTED, MQTT_EVENT_DISCONNECTED, MQTT_EVENT_SUBSCRIBED, MQTT_EVENT_UNSUBSCRIBED, MQTT_EVENT_PUBLISHED, MQTT_EVENT_DATA или MQTT_EVENT_ERROR. Если да, то на ESP-IDF будет выведено соответствующее сообщение в зависимости от типа события, и будет вызвана соответствующая функция.
В таблице ниже приведены описания событий MQTT, которые будут использоваться в этом примере.
| MQTT_EVENT_CONNECTED | Это событие отправляется, когда клиент успешно подключается к брокеру и готов отправлять/получать данные. |
| MQTT_EVENT_DISCONNECTED | Это событие регистрируется, когда клиент разрывает соединение с брокером из-за недоступности сервера. В этом случае клиент не может отправлять/получать данные. |
| MQTT_EVENT_SUBSCRIBED | Это событие регистрируется, когда брокер принимает запрос на подписку от клиента. Данные события также содержат идентификатор сообщения подписки. |
| MQTT_EVENT_UNSUBSCRIBED | В этом случае брокер принимает запрос клиента на отписку. Данные события содержат идентификатор сообщения об отписке. |
| MQTT_EVENT_PUBLISHED | Это событие отправляется, когда брокер подтверждает получение клиентом опубликованного сообщения. Данные события содержат идентификатор опубликованного сообщения. Обратите внимание, что если уровень QoS установлен на 0, то это событие не будет отправлено, поскольку подтверждения не используются. Оно будет отправлено только для уровней QoS 1 или 2. |
| MQTT_EVENT_DATA | Это событие отправляется, когда клиент получает сообщение о публикации. В этом случае данные события содержат идентификатор сообщения, имя темы, в которую оно было опубликовано, полученные данные и длину данных. |
| MQTT_EVENT_ERROR | Это событие отправляется клиентом при возникновении ошибки. |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
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; } } |
Если клиент отправляет событие MQTT_EVENT_CONNECTED, переменная «MQTT_CONNECTED» устанавливается в значение 1, тогда как изначально она имела значение 0. После этого вызывается функция esp_mqtt_client_subscribe() для подписки клиента на заданную тему с заданным QoS. Эта функция принимает три параметра: дескриптор MQTT-клиента, тему и значение QoS. В случае успеха она возвращает message_id, который сохраняется в переменной 'msg_id'.
В нашем случае мы придерживаемся двух тем:
- /topic/topic1 с qos 0
- /topic/topic2 с QoS 1
Терминал ESP-IDF выводит идентификатор сообщения вместе с сообщением об успешной отправке подписки.
|
1 2 3 4 5 6 7 8 9 10 |
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; |
Если клиент отправляет событие MQTT_EVENT_DISCONNECTED, переменная MQTT_CONNECTED устанавливается в значение 0, и терминал выводит сообщение о том, что это событие произошло. Аналогично, для остальных событий на терминал выводится соответствующее сообщение вместе с message_id. Для события MQTT_EVENT_DATA также выводится имя темы вместе с данными.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
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()
Внутри функции mqtt_app_start() создается дескриптор MQTT-клиента в соответствии с параметрами конфигурации, заданными с помощью функции esp_mqtt_client_init(). Эта функция принимает один параметр — указатель на структуру конфигурации MQTT. Структура конфигурации MQTT содержит IP-адрес MQTT-брокера, который выступает в качестве MQTT-сервера. Убедитесь, что вы указали IP-адрес вашего Raspberry Pi, на котором запущен брокер Mosquitto, в структуре конфигурации MQTT.
После этого мы вызовем функцию esp_mqtt_client_register_event(), которая используется для регистрации события MQTT. Она принимает четыре параметра. Первый параметр — это дескриптор MQTT-клиента. Второй параметр — это тип события. Третий параметр — это функция обратного вызова обработчика, а последний параметр — это контекст дескриптора, который в нашем случае равен mqtt_client_handle, 'client'. Наконец, мы запустим MQTT-клиент с помощью функции esp_mqtt_client_start() и укажем уже созданный дескриптор клиента в качестве параметра внутри неё.
|
1 2 3 4 5 6 7 8 9 10 11 |
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); } |
Внутри функции Publisher_Task(), если отправляется событие MQTT_CONNECTED, клиент публикует сообщение «Hello World» брокеру каждые 15 секунд. Функция esp_mqtt_client_publish() отвечает за публикацию сообщения. Она принимает шесть параметров, включая дескриптор MQTT-клиента, строку темы, данные для публикации, длину данных, QoS и флаг сохранения.
|
1 2 3 4 5 6 7 8 9 10 11 |
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()
Внутри функции app_main() сначала инициализируется NVS путем вызова функции nvs_flash_init(). Затем проверяется, не содержит ли раздел NVS пустых страниц или содержит ли он данные в формате, отличном от текущей версии кода. Если выполняется хотя бы одно из этих условий, то стирается флэш-память NVS с помощью nvs_flash_erase() и инициализируется заново. Затем вызывается функция wifi_init(), которая инициализирует Wi-Fi в режиме станции для ESP32. После этого используется xTaskCreate() для создания задачи Publisher_Task, которая будет публиковать сообщение Hello World брокеру каждые 15 секунд.
|
1 2 3 4 5 6 7 8 9 10 11 |
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); } |
Компиляция скетча
Для прошивки микросхемы введите следующую команду в последовательном терминале. Не забудьте заменить COM-порт на тот, через который подключена ваша плата.
|
1 |
idf.py -p COMX flash monitor |
После успешной прошивки кода на терминале ESP-IDF можно будет увидеть различные сообщения. Сначала успешно установится соединение с Wi-Fi.
Публикация и подписка с использованием MQTT X Client
Для тестирования мы будем использовать клиентский инструмент MQTT 5.0 – MQTT X. Чтобы скачать это приложение, перейдите по ссылке: https://mqttx.app/
После завершения установки откройте приложение MQTT X. Нажмите кнопку ‘+ New Connection’ («+ Новое соединение»).
статья в разработке
1 просмотров






