ESP32:Примеры/MQTT-клиент на базе ESP32

Материал из Онлайн справочника
Перейти к навигации Перейти к поиску

Перевод: Максим Кузьмин (Cubewriter) Перевел 364226 статей для сайта.</br>Контакты:</br>* Skype: cubewriter</br>* E-mail: cubewriter@gmail.com</br>* Максим Кузьмин на freelance.ru
Проверка/Оформление/Редактирование: Мякишев Е.А.


Pixel Art Mini Meow Animated.gif Черновик


Содержание

MQTT-клиент на базе ESP32

В этом примере мы продемонстрируем, как использовать MQTT для обмена данными между двумя платами ESP32. Мы сконструируем простой проект, чтобы проиллюстрировать самые важные концепты MQTT.

Рисунок ниже схематически показывает, как будет устроен этот проект. Он будет использовать две ESP32-платы: ESP32 #1 и ESP32 #2.

Esp32 mqtt exmp 1.PNG
  • ESP32 #1 подключена к светодиоду и считывает температуру с датчика DS18B20;
  • ESP32 #2 подключена к кнопке, и нажатие на нее будет включать/выключать светодиод, подключенный к ESP32 #1;
  • ESP32 #2 подключена к LCD-дисплею (передающему данные по I2C), на котором будут печататься данные о температуре, полученные от ESP32 #1;

На схеме ниже показано, какие функции в этом проекте выполняет MQTT-брокер.

Two ESP32s and broker.png
  • ESP32 #1 подписана на топик «esp32/led» и публикует данные о температуре в топик «esp32/temperature»;
  • При нажатии на кнопку, подключенную к ESP32 #2, эта плата будет публиковать соответствующее сообщение в топик «esp32/led», с помощью которого управляется светодиод, подключенный к ESP32 #1;

Справочная информация

Использование MQTT с ESP32: введение

Этот Раздел будет введением в тему MQTT и то, как ее можно использовать вместе с ESP32.

Аббревиатура MQTT означает «message queuing telemetry transport», что можно перевести как «передача сообщений о телеметрии при помощи очереди». Это упрощенная система публикации и подписки, позволяющая публиковать сообщения и, будучи клиентом, получать их.

Publish-subscribe.png

MQTT – это простой протокол передачи данных, предназначенный для устройств с низкой пропускной способностью. Это делает его идеальным для проектов в области интернета вещей. MQTT позволяет отправлять команды для управления устройствами вывода данных, считывать и публиковать данные от датчиков и многое другое.

Базовые концепты MQTT

В MQTT есть несколько базовых концептов, которые нужно знать и понимать:

  • Публикация/подписка;
  • Сообщения;
  • Топики;
  • Брокер;

Публикация/подписка

Первый концепт – это система публикации и подписки. Ее суть в том, что устройство может публиковать сообщения в топик или быть подписано на него, чтобы получать его сообщения.

Publish-subscribe 2.png

Для примера представьте такую ситуацию:

  • Устройство 1 публикует данные в топик;
  • Устройство 2 подписано на топик, куда публикует свои данные Устройство 1;
  • Это позволяет Устройству 2 получать сообщения Устройства 1;

Сообщения – это фрагменты информации, переходящие от одного устройства к другому. Это могут быть и данные, и команды.

Топики

Еще один важный концепт MQTT – это топики. Для устройства-отправителя это место, куда оно может публиковать свои сообщения, а для устройства-получателя это место, к сообщениям которого оно может проявить заинтересованность.

Топики представляются в виде строк, разбитых на части при помощи прямых слешей («/»). Каждый слеш означает уровень топика. Ниже – пример топика для лампы, находящейся кабинете, который в свою очередь находится у вас дома.

Topic levels.png

Примечание: Топики чувствительны к регистру, поэтому топики на изображении ниже – это разные топики.

Topic levels2.PNG

Итак, если представить сценарий, при котором вы управляете лампой в своем домашнем кабинете при помощи ESP32 и MQTT, то он будет выглядеть примерно так:

MQTT home-office-lamp.png
  1. У нас есть устройство, которое публикует сообщения «вкл» и «выкл» в топик «home/office/lamp»;
  2. На этот топик подписана ESP32, управляющая включением/выключением лампы;
  3. Таким образом, когда в этом топике публикуется новое сообщение, ESP32 получает команду «вкл» или «выкл» и, соответственно, включает либо выключает лампу;

Первым устройством может быть ESP32, ESP8266 или контроллер, на который установлена платформа для домашней автоматизации вроде Node-RED, Home Assistant, Domoticz или OpenHAB.

Брокер

Наконец, вам также нужно понимать, что такое «брокер». Он в основном ответственен за получение всех сообщений, фильтрацию сообщений, принятие решений о том, кто в них заинтересован, и отправку этих сообщений всем подписанным клиентам.

Mqtt broker.png

Брокеры бывают разными. Есть, к примеру, брокер Mosquitto, установленный на Raspberry Pi, или бесплатный облачный брокер CloudMQTT.

Мы будем использовать брокер Mosquitto, установленный на Raspberry Pi.

Итого

Итак, в этом Разделе мы научились следующему:

  • MQTT – это коммуникационный протокол, хорошо подходящий для проектов в области интернета вещей;
  • В MQTT устройства могут публиковать сообщения в топики и быть подписаны на другие топики, чтобы получать их сообщения;
  • При использовании MQTT необходим брокер. Он получает все сообщения и отправляет их подписанным клиентам;

Установка MQTT-брокера Mosquitto на Raspberry Pi

В этом Разделе мы установим брокер Mosquitto на Raspberry Pi. Как уже говорилось в предыдущем разделе, брокер в основном ответственен за получение всех сообщений, фильтрацию сообщений, принятие решение о том, кто в них заинтересован, и публикацию этих сообщений для всех подписанных клиентов.

Mqtt broker.png

Брокеры бывают разными. В этом примере мы будем использовать брокер Mosquitto, установленный на Raspberry Pi.

Примечание: Вы также можете воспользоваться бесплатным брокером CloudMQTT (он поддерживает до 5 подключенных устройств). О том, как использовать его вместе с ESP32, можно почитать тут.

Откройте новое окно терминала Raspberry Pi.

Чтобы установить брокер Mosquitto, введите следующие команды:

1 sudo apt update
2 sudo apt install -y mosquitto mosquitto-clients

Чтобы Mosquitto автоматически запускался при загрузке, впишите следующее:

sudo systemctl enable mosquitto.service

Проверяем, установлен ли Mosquitto

Отправляем команду:

mosquitto -v

Эта команда возвращает версию Mosquitto, установленную на Raspberry Pi. Она должна быть 1.4.x или выше.

Примечание: Иногда после введения команды «mosquitto -v» может появиться сообщение-предупреждение «Error: Address already in use». Оно значит, что брокер Mosquitto уже запущен, поэтому ничего страшного в нем нет.

IP-адрес Raspberry Pi

Чтобы узнать IP-адрес своей Raspberry Pi, впишите в терминал следующую команду:

hostname -I
Rpi hostname i terminal 1.PNG

В нашем случае IP-адрес Raspberry Pi – это «192.168.1.2». Сохраните его, т.к. он понадобится нам далее, чтобы ESP32 можно было подключить к MQTT-брокеру Mosquitto.

Итого

Итак, в этом Разделе мы научились устанавливать брокер Mosquitto на Raspberry Pi.

Устанавливаем библиотеки

Нам нужно установить в IDE Arduino необходимые библиотеки, которые понадобятся для того, чтобы использовать вместе с ESP32 протокол MQTT – «AsyncTCP» и «async MQTT client».

Устанавливаем библиотеку «AsyncTCP»

  1. Кликните тут, чтобы загрузить ZIP-архив библиотеки;
  2. Распакуйте скачанный архив. У вас должна получиться папка «AsyncTCP-master»;
  3. Переименуйте ее на «AsyncTCP»;
  4. Переместите папку «AsyncTCP» в папку «libraries», которая находится в папке, куда установлена ваша IDE Arduino;
  5. Перезапустите IDE Arduino если она была запущена;

Устанавливаем библиотеку «async MQTT client»

  1. Кликните тут, чтобы загрузить ZIP-архив библиотеки;
  2. Распакуйте скачанный архив. У вас должна получиться папка «async-mqtt-client-master»;
  3. Переименуйте ее на «async_mqtt_client»;
  4. Переместите папку «async_mqtt_client» в папку «libraries», которая находится внутри папки, куда установлена ваша IDE Arduino;
  5. Перезапустите IDE Arduino если она была запущена;

Нам также нужно установить библиотеки «OneWire» (автор – Пол Стоффреген) и «Arduino Temperature Control» – они необходимы для использования датчика DS18B20.

Устанавливаем библиотеку «OneWire»

  1. Кликните тут, чтобы скачать ZIP-архив библиотеки;
  2. Распакуйте скачанный архив. У вас должна получиться папка «OneWire-master»;
  3. Переименуйте папку «OneWire-master» на «OneWire»;
  4. Переместите папку «OneWire» в папку «libraries», которая находится внутри папки, куда установлена ваша IDE Arduino;
  5. Перезапустите IDE Arduino если она была запущена;

Устанавливаем библиотеку «Arduino Temperature Control»

  1. Кликните тут, чтобы скачать ZIP-архив библиотеки;
  2. Распакуйте скачанный архив. У вас должна получиться папка «Arduino-Temperature-Control-Library-master»;
  3. Переименуйте папку «Arduino-Temperature-Control-Library-master» на «DallasTemperature»;
  4. Переместите папку «DallasTemperature» в папку «libraries», находящуюся внутри папки, куда установлена ваша IDE Arduino;
  5. Перезапустите IDE Arduino если она была запущена;

Устанавливаем библиотеку «LiquidCrystal I2C»

  1. Кликните тут, чтобы скачать ZIP-архив библиотеки;
  2. Распакуйте скачанный архив. У вас должна получиться папка под названием «LiquidCrystal_I2C-master»;
  3. Переименуйте ее на «LiquidCrystal_I2C»;
  4. Переместите папку «LiquidCrystal_I2C» в папку «libraries», которая находится внутри папки, куда установлена ваша IDE Arduino;
  5. Перезапустите IDE Arduino если она была запущена;

ESP32 #1

Что нужно сделать перед загрузкой кода

Чтобы этот код заработал сразу, безо всяких дополнительных настроек, впишите в строчках ниже SSID и пароль для своей WiFi-сети:

1 #define WIFI_SSID "REPLACE_WITH_YOUR_SSID"
2 #define WIFI_PASSWORD "REPLACE_WITH_YOUR_PASSWORD"

Вам также нужно будет ввести IP-адрес MQTT-брокера Mosquitto. О том, как найти IP-адрес Raspberry Pi, рассказывается в предыдущем Разделе.

#define MQTT_HOST IPAddress(192, 168, 1, XXX)

Теперь вы можете загрузить этот код как есть, и он сразу начнет работать. Но мы рекомендуем также ознакомиться с текстом ниже, в котором объясняется, как именно он работает.

Как работает этот код

Фрагмент кода ниже импортирует в скетч все необходимые библиотеки:

1 #include <WiFi.h>
2 extern "C" {
3 #include "freertos/FreeRTOS.h"
4 #include "freertos/timers.h"
5 }
6 #include <AsyncMqttClient.h>
7 #include <OneWire.h>
8 #include <DallasTemperature.h>

Как уже было сказано выше, вам также нужно будет вписать в скетч SSID и пароль для своей WiFi-сети, а также указать IP-адрес своей Raspberry Pi. Это делается в этом фрагменте:

1 // поменяйте SSID и пароль в двух строчках ниже,
2 // чтобы ESP32 могла подключиться к вашему роутеру:
3 #define WIFI_SSID "REPLACE_WITH_YOUR_SSID"
4 #define WIFI_PASSWORD "REPLACE_WITH_YOUR_PASSWORD"
5 
6 // вставьте в переменную «MQTT_HOST» IP-адрес своей Raspberry Pi,
7 // чтобы она могла подключиться к MQTT-брокеру Mosquitto:
8 #define MQTT_HOST IPAddress(192, 168, 1, XXX)
9 #define MQTT_PORT 1883

Создаем объект для управления MQTT-клиентом и таймеры, которые понадобятся для повторного подключения к MQTT-брокеру или WiFi-роутеру, если связь вдруг оборвется.

1 // создаем объекты для управления MQTT-клиентом:
2 AsyncMqttClient mqttClient;
3 TimerHandle_t mqttReconnectTimer;
4 TimerHandle_t wifiReconnectTimer;

Далее объявляем еще несколько переменных: одну для хранения данных о температуре и две вспомогательные таймерные переменные – для чтения опубликованных данных каждые 5 секунд.

1 String temperatureString = "";     // переменная для хранения
2                                    // данных о температуре
3 unsigned long previousMillis = 0;  // здесь хранится информация о том, 
4                                    // когда в последний раз 
5                                    // была опубликована температура
6 const long interval = 5000;        // интервал между публикациями 
7                                    // данных от датчика

Затем задаем контакт, к которому подключен светодиод, и начальное состояние для него.

1 const int ledPin = 25;             // GPIO-контакт, к которому
2                                    // подключен светодиод
3 int ledState = LOW;                // текущее состояние
4                                    // выходного контакта

Наконец, задаем контакт для датчика DS18B20 и создаем объекты для его работы.

1 // GPIO-контакт, к которому подключен датчик DS18B20:
2 const int oneWireBus = 32;          
3 // делаем так, чтобы объект «oneWire»
4 // коммуницировал с любыми OneWire-устройствами:
5 OneWire oneWire(oneWireBus);
6 // передаем объект «oneWire» датчику температуры: 
7 DallasTemperature sensors(&oneWire);

MQTT-функции: подключение к WiFi, подключение к MQTT, WiFi-события

В коде нет комментариев для функций, о которых пойдет речь ниже. Эти функции – часть библиотеки «async MQTT client», и их названия, собственно, говорят сами за себя. К примеру, функция connectToWiFi() подключает ESP32 к WiFi-роутеру:

1 void connectToWifi() {
2 Serial.println("Connecting to Wi-Fi...");
3            //  "Подключаемся к WiFi..."
4 WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
5 }

Функция connectToMqtt() подключает ESP32 к MQTT-брокеру.

1 void connectToMqtt() {
2 Serial.println("Connecting to MQTT...");
3            //  "Подключаемся к MQTT..."
4 mqttClient.connect();
5 }

Функция WiFiEvent() ответственна за управление WiFi-событиями. К примеру, после успешного подключения к роутеру и MQTT-брокеру она напечатает IP-адрес ESP32. Кроме того, при обрыве соединения она запустит таймер и попытается возобновить подключение.

 1 void WiFiEvent(WiFiEvent_t event) {
 2   Serial.printf("[WiFi-event] event: %d\n", event);
 3   switch(event) {
 4     case SYSTEM_EVENT_STA_GOT_IP:
 5       Serial.println("WiFi connected");  //  "Подключились к WiFi"
 6       Serial.println("IP address: ");  //  "IP-адрес: "
 7       Serial.println(WiFi.localIP());
 8       connectToMqtt();
 9       break;
10     case SYSTEM_EVENT_STA_DISCONNECTED:
11       Serial.println("WiFi lost connection");
12                  //  "WiFi-связь потеряна"
13       // делаем так, чтобы ESP32
14       // не переподключалась к MQTT
15       // во время переподключения к WiFi:
16       xTimerStop(mqttReconnectTimer, 0);
17       xTimerStart(wifiReconnectTimer, 0);
18       break;
19   }
20 }

Подписываемся на MQTT-топик

Задача функции onMqttConnect() – подписка ESP32 на топики. Вы можете добавить в эту функцию дополнительные топики, на которые в дальнейшем подпишется ESP32. Но если оставить код без изменений, ESP32 подпишется только на топик «esp32/led».

1 void onMqttConnect(bool sessionPresent) {
2     Serial.println("Connected to MQTT.");                           //  "Подключились к MQTT."
3     Serial.print("Session present: ");                              //  "Текущая сессия: "
4     Serial.println(sessionPresent);
5     // ESP32 подписывается на топик esp32/led
6     uint16_t packetIdSub = mqttClient.subscribe("esp32/led", 0);
7     Serial.print("Subscribing at QoS 0, packetId: ");               //  "Подписка при QoS 0, ID пакета: "
8     Serial.println(packetIdSub);
9 }

Следующая строчка – важная часть этого фрагмента. Она подписывает ESP32 на MQTT-топик при помощи метода subscribe().

uint16_t packetIdSub = mqttClient.subscribe("esp32/led", 0);

Параметрами у этого метода служат MQTT-топик, на который нужно подписать ESP32, и уровень качества обслуживания (англ. «quality of service» или просто «QoS»). Более подробно о том, что это такое, можно почитать тут.

Еще MQTT-функции: отключение, подписка, отписка и публикация

Если ESP32 потеряет соединение с MQTT-брокером, функция onMqttDisconnect() напечатает в мониторе порта соответствующее сообщение:

1 void onMqttDisconnect(AsyncMqttClientDisconnectReason reason) {
2   Serial.println("Disconnected from MQTT.");
3              //  "Отключились от MQTT."
4   if (WiFi.isConnected()) {
5     xTimerStart(mqttReconnectTimer, 0);
6   }
7 }

Если ESP32 подпишется на MQTT-топик, функция onMqttSubscribe() напечатает ID пакета и уровень качества обслуживания (QoS):

1 void onMqttSubscribe(uint16_t packetId, uint8_t qos) {
2   Serial.println("Subscribe acknowledged.");
3              //  "Подписка подтверждена."
4   Serial.print("  packetId: ");
5   Serial.println(packetId);
6   Serial.print("  qos: ");
7   Serial.println(qos);
8 }

Если ESP32 отпишется от топика, функция onMqttUnsubscribe() напечатает сообщение об этом.

1 void onMqttUnsubscribe(uint16_t packetId) {
2   Serial.println("Unsubscribe acknowledged.");
3             //  "Отписка подтверждена."
4   Serial.print("  packetId: ");
5   Serial.println(packetId);
6 }

И если в MQTT-топике будет опубликовано новое сообщение, функция onMqttPublish() напечатает в мониторе порта ID этого пакета.

1 void onMqttPublish(uint16_t packetId) {
2   Serial.println("Publish acknowledged.");
3             //  "Публикация подтверждена."
4   Serial.print("  packetId: ");
5   Serial.println(packetId);
6 }

Получаем MQTT-сообщения

Если в топик, на который подписана ESP32 (в данном случае – в топик «esp32/led»), придет новое сообщение, будет выполнена функция onMqttMessage(). В ней задается то, что произойдет при появлении в этом топике нового сообщения, и вы эти события можете отредактировать.

 1 // этой функцией управляется то, что происходит
 2 // при получении того или иного сообщения в топике «esp32/led»;
 3 // (если хотите, можете ее отредактировать): 
 4 void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
 5   String messageTemp;
 6   for (int i = 0; i < len; i++) {
 7     //Serial.print((char)payload[i]);
 8     messageTemp += (char)payload[i];
 9   }
10   // проверяем, получено ли MQTT-сообщение в топике «esp32/led»:
11   if (strcmp(topic, "esp32/led") == 0) {
12     // если светодиод выключен, включаем его (и наоборот):
13     if (ledState == LOW) {
14       ledState = HIGH;
15     } else {
16       ledState = LOW;
17     }
18     // задаем светодиоду значение переменной «ledState»:
19     digitalWrite(ledPin, ledState);
20   }
21  
22   Serial.println("Publish received.");
23              //  "Опубликованные данные получены."
24   Serial.print("  message: ");  //  "  сообщение: "
25   Serial.println(messageTemp);
26   Serial.print("  topic: ");  //  "  топик: "
27   Serial.println(topic);
28   Serial.print("  qos: ");  //  "  уровень обслуживания: "
29   Serial.println(properties.qos);
30   Serial.print("  dup: ");  //  "  дублирование сообщения: "
31   Serial.println(properties.dup);
32   Serial.print("  retain: ");  //  "сохраненные сообщения: "
33   Serial.println(properties.retain);
34   Serial.print("  len: ");  //  "  размер: "
35   Serial.println(len);
36   Serial.print("  index: ");  //  "  индекс: "
37   Serial.println(index);
38   Serial.print("  total: ");  //  "  суммарно: "
39   Serial.println(total);
40 }

Во фрагменте выше при помощи оператора if() проверяется, было ли в топике «esp32/led» опубликовано новое сообщение. Вот эта строчка:

if (strcmp(topic, "esp32/led") == 0) {

Внутри этого оператора if() можно задать собственную логику. Например, еще один оператор if(), который включает/выключает светодиод каждый раз, когда в этот топик приходит соответствующее сообщение (в скетче реализован как раз такой вариант).

1 // если светодиод выключен, включаем его (и наоборот):
2     if (ledState == LOW) {
3       ledState = HIGH;
4     } else {
5       ledState = LOW;
6     }
7     // задаем светодиоду значение переменной «ledState»:
8     digitalWrite(ledPin, ledState);
9   }

Все эти функции, которые мы рассмотрели выше – это функции обратного вызова и потому выполняются асинхронно.

setup()

Теперь давайте перейдем к блоку setup(). Запускаем датчик DS18B20, делаем контакт светодиода выходным, задаем ему значение «LOW» и запускаем последовательную коммуникацию.

1 // Запускаем датчик DS18B20:
2 sensors.begin();
3 // задаем контакт, к которому подключен светодиод,
4 // как выходной контакт, и присваиваем ему значение «LOW»:
5 pinMode(ledPin, OUTPUT);
6 digitalWrite(ledPin, LOW);
7   
8 Serial.begin(115200);

В двух строчках ниже создаются таймеры для повторного подключения к MQTT-брокеру и WiFi-роутеру в случае, если соединение будет потеряно.

1 mqttReconnectTimer = xTimerCreate("mqttTimer", pdMS_TO_TICKS(2000), pdFALSE, (void*)0, reinterpret_cast<TimerCallbackFunction_t>(connectToMqtt));
2 wifiReconnectTimer = xTimerCreate("wifiTimer", pdMS_TO_TICKS(2000), pdFALSE, (void*)0, reinterpret_cast<TimerCallbackFunction_t>(connectToWifi));

Строчка ниже задает, чтобы при подключении ESP32 к WiFi-сети была запущена функция обратного вызова WiFiEvent(), которая напечатает данные о WiFi-связи (какие именно – см. выше).

WiFi.onEvent(WiFiEvent);

Наконец, присваиваем все функции обратного вызова, созданные выше. Это значит, что все эти функции будут вызваны автоматически – в случае, если они понадобятся. К примеру, когда ESP32 подключится к брокеру, автоматически будет вызвана функция onMqttConnect() и т.д.

1 mqttClient.onConnect(onMqttConnect);
2 mqttClient.onDisconnect(onMqttDisconnect);
3 mqttClient.onSubscribe(onMqttSubscribe);
4 mqttClient.onUnsubscribe(onMqttUnsubscribe);
5 mqttClient.onMessage(onMqttMessage);
6 mqttClient.onPublish(onMqttPublish);
7 mqttClient.setServer(MQTT_HOST, MQTT_PORT);

loop()

В блоке loop() создаем таймер, позволяющий публиковать новые температурные данные в топике «esp32/temperature» каждые 5 секунд.

 1 unsigned long currentMillis = millis();
 2   // каждые X секунд («interval» = 5 секунд) 
 3   // в топик «esp32/temperature»
 4   // будет публиковаться новое MQTT-сообщение:
 5   if (currentMillis - previousMillis >= interval) {
 6     // сохраняем в переменную «previousMillis» время,
 7     // когда в последний раз были опубликованы новые данные:
 8     previousMillis = currentMillis;
 9     // новые данные о температуре:
10     sensors.requestTemperatures(); 
11     temperatureString = " " + String(sensors.getTempCByIndex(0)) + "C  " +  
12                         String(sensors.getTempFByIndex(0)) + "F";     
13     Serial.println(temperatureString);
14     // публикуем MQTT-сообщение в топике «esp32/temperature»
15     // с температурой в градусах Цельсия и Фаренгейта:
16     uint16_t packetIdPub2 = mqttClient.publish("esp32/temperature", 2, true, temperatureString.c_str());
17     Serial.print("Publishing on topic esp32/temperature at QoS 2, packetId: ");  
18              //  "Публикация в топик «esp32/temperature»
19              //   при QoS 2, ID пакета: "
20     Serial.println(packetIdPub2);
21   }

Добавление новых топиков для публикации/подписки

Это очень простой пример, но он иллюстрирует, как работает публикация и подписка в MQTT.

Если вы хотите добавить в код дополнительные топики для публикации, просто продублируйте в loop() следующие три строчки (но не забудьте заменить название топика). Собственно, за публикацию данных в топик здесь отвечает только строчка с методом publish(), а две остальные просто печатают в монитор порта информацию о публикации и публикуемых данных.

1 uint16_t packetIdPub2 = mqttClient.publish("esp32/temperature", 2, true, temperatureString.c_str());
2 Serial.print("Publishing on topic esp32/temperature at QoS 2, packetId: ");
3              //  "Публикация в топик «esp32/temperature»
4              //   при QoS 2, ID пакета: "
5 Serial.println(packetIdPub2);

Если вам нужно задать в коде дополнительные топики для подписки, перейдите к функции onMqttConnect(), продублируйте строчку ниже, но также не забудьте поменять в ней название топика на тот, на который хотите подписаться.

uint16_t packetIdSub = mqttClient.subscribe("esp32/led", 0);

Наконец, в функции onMqttMessage() можно задать, что произойдет, когда в заданный топик придет новое сообщение.

Загружаем код

Запитайте Raspberry Pi, убедитесь, что на ней запущен MQTT-брокер Mosquitto, и загрузите этот код на ESP32.

Примечание: Если вы еще не настроили MQTT-брокер Mosquitto, об этом можно почитать в предыдущем Разделе.

Откройте монитор порта на скорости 115200 бод и проверьте, подключилась ли ESP32 к WiFi-роутеру и MQTT-брокеру.

Esp32 mqtt esp32 1 publising on topic serial monitor 1.PNG

Как видите, все работает, как надо!

А это значит, что пора переходить к следующем Разделу, где мы настроим вторую часть этого проекта – плату ESP32 #2.

ESP32 #2

Что нужно сделать перед загрузкой кода

Как и в прошлый раз, чтобы код сразу же заработал, вам нужно вписать в строчках ниже SSID и пароль для WiFi-сети, а также IP-адрес MQTT-брокера.

1 // вставьте в строчках ниже SSID и пароль для своей WiFi-сети,
2 // чтобы ESP32 могла подключиться к вашему WiFi-роутеру:
3 #define WIFI_SSID "REPLACE_WITH_YOUR_SSID"
4 #define WIFI_PASSWORD "REPLACE_WITH_YOUR_PASSWORD"
5 
6 // задайте в переменной «MQTT_HOST» IP-адрес своей Raspberry Pi,
7 // чтобы ESP32 могла подключиться к MQTT-брокеру Mosquitto:
8 #define MQTT_HOST IPAddress(192, 168, 1, XXX)

Как работает этот код

Большую часть скетча мы пропустим, т.к. она уже объяснялась в Разделе выше.

В строчках ниже вписываем SSID и пароль для своей WiFi-сети, а также IP-адрес MQTT-брокера.

1 // вставьте в строчках ниже SSID и пароль для своей WiFi-сети,
2 // чтобы ESP32 могла подключиться к вашему WiFi-роутеру:
3 #define WIFI_SSID "REPLACE_WITH_YOUR_SSID"
4 #define WIFI_PASSWORD "REPLACE_WITH_YOUR_PASSWORD"
5 
6 // задайте в переменной «MQTT_HOST» IP-адрес своей Raspberry Pi,
7 // чтобы ESP32 могла подключиться к MQTT-брокеру Mosquitto:
8 #define MQTT_HOST IPAddress(192, 168, 1, XXX)

В двух строчках ниже задаем количество столбцов и рядов LCD-дисплея. Мы используем LCD-дисплей, у которого экран размером 16х2 символов.

1 // задаем количество столбцов и рядов для LCD-дисплея:
2 const int lcdColumns = 16;
3 const int lcdRows = 2;

Байтовый массив ниже предназначен для того, чтобы показать на LCD-дисплее иконку термометра:

 1 // иконка термометра:
 2 byte thermometerIcon[8] = {
 3   B00100,
 4   B01010,
 5   B01010,
 6   B01010,
 7   B01010,
 8   B10001,
 9   B11111,
10   B01110
11 };

Затем задаем контакт, к которому подключена кнопка, переменную для текущего состояния кнопки, переменную для последнего состояния кнопки и вспомогательные переменные для создания таймера антидребезга (чтобы избежать «фейковых» нажатий на кнопку).

 1 const int buttonPin = 32;            // GPIO-контакт, к которому 
 2                                      // подключена кнопка
 3 int buttonState;                     // текущее значение 
 4                                      // входного контакта (кнопки) 
 5 int lastButtonState = LOW;           // предыдущее значение
 6                                      // входного контакта (кнопки) 
 7 unsigned long lastDebounceTime = 0;  // время, когда в последний раз 
 8                                      // был переключен 
 9                                      // выходной контакт               
10 unsigned long debounceDelay = 50;    // время антидребезга 
11                                      // (увеличьте, если
12                                      //  выходное значение «прыгает»)

MQTT-функции

О функциях connectToWiFi(), connectToMQTT() и WiFiEvent() уже рассказывалось в Разделе выше.

В функции onMqttConnect() добавляются топики для подписки.

 1 // в эту функцию можно добавить новые топики для подписки:
 2 void onMqttConnect(bool sessionPresent) {
 3   Serial.println("Connected to MQTT.");  //  "Подключились к MQTT."
 4   Serial.print("Session present: ");     //  "Текущая сессия: "
 5   Serial.println(sessionPresent);
 6   uint16_t packetIdSub = mqttClient.subscribe("esp32/temperature", 0);
 7   Serial.print("Subscribing at QoS 0, packetId: ");
 8            //  "Подписка при QoS 0, ID пакета: "
 9   Serial.println(packetIdSub);
10 }

В нашем случае ESP32 подписана только на топик «esp32/temperature», но функцию onMqttConnect() можно отредактировать и добавить в нее новые топики для подписки.

 1 // эта функция управляет тем, что произойдет
 2 // при получении топиком «esp32/temperature» того или иного сообщения
 3 // (эту функцию можно отредактировать):
 4 void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
 5   String messageTemp;
 6   for (int i = 0; i < len; i++) {
 7     //Serial.print((char)payload[i]);
 8     messageTemp += (char)payload[i];
 9   }
10   if (strcmp(topic, "esp32/temperature") == 0) {
11     lcd.clear();
12     lcd.setCursor(1, 0);
13     lcd.write(0);
14     lcd.print(" Temperature");  //  " Температура"
15     lcd.setCursor(0, 1);
16     lcd.print(messageTemp);
17   }
18 (...)

Как уже рассказывалось в предыдущем Разделе, в функции onMqttMessage() мы задаем, что произойдет, когда в топик, на который подписана ESP32, придет новое сообщение. В эту функцию можно добавить дополнительные операторы if() – для проверки других топиков и содержимого сообщений.

В нашем случае, когда в топик «esp32/temperature» придет новое сообщение, ESP32 покажет его на LCD-дисплее, который к ней подключен.

setup()

В блоке setup() мы запускаем LCD-дисплей, включаем подсветку и создаем иконку термометра, которую затем покажем на LCD-дисплее.

1 // инициализируем LCD-дисплей:
2 lcd.init();
3 // включаем подсветку LCD-дисплея:
4 lcd.backlight();
5 // создаем иконку термометра:
6 lcd.createChar(0, thermometerIcon);

Делаем контакт, к которому подключена кнопка, входным.

1 // делаем контакт, к которому подключена кнопка, входным контактом: 
2 pinMode(buttonPin, INPUT);

Затем создаем таймеры для повторного подключения.

1 mqttReconnectTimer = xTimerCreate("mqttTimer", pdMS_TO_TICKS(2000), pdFALSE, (void*)0, reinterpret_cast<TimerCallbackFunction_t>(connectToMqtt));
2 wifiReconnectTimer = xTimerCreate("wifiTimer", pdMS_TO_TICKS(2000), pdFALSE, (void*)0, reinterpret_cast<TimerCallbackFunction_t>(connectToWifi));

И задаем все функции обратного вызова для WiFi- и MQTT-событий, созданных нами ранее.

1 WiFi.onEvent(WiFiEvent);
2 mqttClient.onConnect(onMqttConnect);
3 mqttClient.onDisconnect(onMqttDisconnect);
4 mqttClient.onSubscribe(onMqttSubscribe);
5 mqttClient.onUnsubscribe(onMqttUnsubscribe);
6 mqttClient.onMessage(onMqttMessage);
7 mqttClient.onPublish(onMqttPublish);
8 mqttClient.setServer(MQTT_HOST, MQTT_PORT);

loop()

В блоке loop() публикуем MQTT-сообщение в топик «esp32/led», если кнопка была нажата.

Мы также используем здесь фрагмент из скетча-примера «Debounce», предназначенного для того, чтобы избежать «дребезга» («фальшивых» нажатий на кнопку).

Если кнопка была нажата, оператор if() ниже вернет «true», а в топике «esp32/led» будет опубликовано сообщение «toogle».

1 if (buttonState == HIGH) {
2     mqttClient.publish("esp32/led", 0, true, "toggle");
3     Serial.println("Publishing on topic esp32/led topic at QoS 0");
4                    //  "Публикация в топик «esp32/led» при QoS 0"
5 }

Вот и все. Можете загрузить этот код на ESP32.

Демонстрация

Запитайте Raspberry Pi, убедитесь, что на ней запущен MQTT-брокер Mosquitto, откройте монитор порта и задайте в нем скорость передачи данных на 115200 бод.

Проверьте, подключилась ли ESP32 к WiFi-роутеру и MQTT-брокеру. В моем случае, как видно на скриншоте ниже, все работает как надо:

Esp32 mqtt esp32 1 publising on topic serial monitor 1.PNG

Теперь подключите питание к обеим платам и оставьте включенным MQTT-брокер Mosquitto.

ESP32 #2 мгновенно получит данные о температуре от ESP32 #1 в градусах Цельсия и Фаренгейта.

Если нажать на кнопку, это тут же переключит состояние светодиода, подключенного к ESP32 #1.

Эту систему можно легко расширить, добавив дополнительные платы ESP32. Кроме того, вы можете создать дополнительные MQTT-топики, через которые можно отправлять команды и данные от датчиков. Также ко всей этой системе можно добавить панель управления при помощи платформы домашней автоматизации вроде Node-RED, Home Assistant, Domoticz или OpenHAB.

Итого

В этом примере мы показали вам, как передавать данные между двумя ESP32 при помощи протокола MQTT.

Необходимое оборудование

Схема

ESP32 #1

Esp32 mqtt exmp esp32 1 1.PNG

ESP32 #2

  • Подключение кнопки: одну ножку подключите к 3.3-вольтовому контакту, а другую – (через стягивающий резистор на 110 кОм) к контакту GPIO32;
  • Подключение LCD-дисплея: подключите контакт SDA к GPIO21, а SCL – к GPIO22. LCD-дисплей работает на 5 вольтах, поэтому его нужно подключить к контактам Vin и GND;

В качестве подсказки также можно использовать схему ниже:

Esp32 mqtt exmp esp32 2 2.PNG

Код

ESP32 #1

  1 /*********
  2   Руи Сантос
  3   Более подробно о проекте на: https://randomnerdtutorials.com  
  4 *********/
  5 
  6 #include <WiFi.h>
  7 extern "C" {
  8   #include "freertos/FreeRTOS.h"
  9   #include "freertos/timers.h"
 10 }
 11 #include <AsyncMqttClient.h>
 12 #include <OneWire.h>
 13 #include <DallasTemperature.h>
 14 
 15 // поменяйте SSID и пароль в двух строчках ниже,
 16 // чтобы ESP32 могла подключиться к вашему роутеру:
 17 #define WIFI_SSID "REPLACE_WITH_YOUR_SSID"
 18 #define WIFI_PASSWORD "REPLACE_WITH_YOUR_PASSWORD"
 19 
 20 // вставьте в переменную «MQTT_HOST» IP-адрес своей Raspberry Pi,
 21 // чтобы ESP32 могла подключиться к MQTT-брокеру Mosquitto:
 22 #define MQTT_HOST IPAddress(192, 168, 1, XXX)
 23 #define MQTT_PORT 1883
 24 
 25 // создаем объекты для управления MQTT-клиентом:
 26 AsyncMqttClient mqttClient;
 27 TimerHandle_t mqttReconnectTimer;
 28 TimerHandle_t wifiReconnectTimer;
 29 
 30 String temperatureString = "";     // переменная для хранения
 31                                    // данных о температуре
 32 unsigned long previousMillis = 0;  // здесь хранится информация о том, 
 33                                    // когда в последний раз 
 34                                    // была опубликована температура
 35 const long interval = 5000;        // интервал между публикациями 
 36                                    // данных от датчика
 37 
 38 const int ledPin = 25;             // GPIO-контакт, к которому
 39                                    // подключен светодиод
 40 int ledState = LOW;                // текущее состояние
 41                                    // выходного контакта
 42 
 43 // GPIO-контакт, к которому подключен датчик DS18B20:
 44 const int oneWireBus = 32;          
 45 // делаем так, чтобы объект «oneWire»
 46 // коммуницировал с любыми OneWire-устройствами:
 47 OneWire oneWire(oneWireBus);
 48 // передаем объект «oneWire» датчику температуры: 
 49 DallasTemperature sensors(&oneWire);
 50 
 51 void connectToWifi() {
 52   Serial.println("Connecting to Wi-Fi...");
 53              //  "Подключаемся к WiFi..."
 54   WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
 55 }
 56 
 57 void connectToMqtt() {
 58   Serial.println("Connecting to MQTT...");
 59              //  "Подключаемся к MQTT... "
 60   mqttClient.connect();
 61 }
 62 
 63 void WiFiEvent(WiFiEvent_t event) {
 64   Serial.printf("[WiFi-event] event: %d\n", event);
 65   switch(event) {
 66     case SYSTEM_EVENT_STA_GOT_IP:
 67       Serial.println("WiFi connected");  //  "Подключились к WiFi"
 68       Serial.println("IP address: ");  //  "IP-адрес: "
 69       Serial.println(WiFi.localIP());
 70       connectToMqtt();
 71       break;
 72     case SYSTEM_EVENT_STA_DISCONNECTED:
 73       Serial.println("WiFi lost connection");
 74                  //  "WiFi-связь потеряна"
 75       // делаем так, чтобы ESP32
 76       // не переподключалась к MQTT
 77       // во время переподключения к WiFi:
 78       xTimerStop(mqttReconnectTimer, 0);
 79       xTimerStart(wifiReconnectTimer, 0);
 80       break;
 81   }
 82 }
 83 
 84 // в этом фрагменте добавляем топики, 
 85 // на которые будет подписываться ESP32:
 86 void onMqttConnect(bool sessionPresent) {
 87   Serial.println("Connected to MQTT.");  //  "Подключились по MQTT."
 88   Serial.print("Session present: ");  //  "Текущая сессия: "
 89   Serial.println(sessionPresent);
 90   // подписываем ESP32 на топик «esp32/led»:
 91   uint16_t packetIdSub = mqttClient.subscribe("esp32/led", 0);
 92   Serial.print("Subscribing at QoS 0, packetId: ");
 93          //  "Подписываемся при QoS 0, ID пакета: "
 94   Serial.println(packetIdSub);
 95 }
 96 
 97 void onMqttDisconnect(AsyncMqttClientDisconnectReason reason) {
 98   Serial.println("Disconnected from MQTT.");
 99              //  "Отключились от MQTT."
100   if (WiFi.isConnected()) {
101     xTimerStart(mqttReconnectTimer, 0);
102   }
103 }
104 
105 void onMqttSubscribe(uint16_t packetId, uint8_t qos) {
106   Serial.println("Subscribe acknowledged.");
107              //  "Подписка подтверждена."
108   Serial.print("  packetId: ");  //  "  ID пакета: "
109   Serial.println(packetId);
110   Serial.print("  qos: ");  //  "  Уровень качества обслуживания: "
111   Serial.println(qos);
112 }
113 
114 void onMqttUnsubscribe(uint16_t packetId) {
115   Serial.println("Unsubscribe acknowledged.");
116             //  "Отписка подтверждена."
117   Serial.print("  packetId: ");
118   Serial.println(packetId);
119 }
120 
121 void onMqttPublish(uint16_t packetId) {
122   Serial.println("Publish acknowledged.");
123             //  "Публикация подтверждена."
124   Serial.print("  packetId: ");
125   Serial.println(packetId);
126 }
127 
128 // этой функцией управляется то, что происходит
129 // при получении того или иного сообщения в топике «esp32/led»;
130 // (если хотите, можете ее отредактировать):
131 void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
132   String messageTemp;
133   for (int i = 0; i < len; i++) {
134     //Serial.print((char)payload[i]);
135     messageTemp += (char)payload[i];
136   }
137   // проверяем, получено ли MQTT-сообщение в топике «esp32/led»:
138   if (strcmp(topic, "esp32/led") == 0) {
139     // если светодиод выключен, включаем его (и наоборот):
140     if (ledState == LOW) {
141       ledState = HIGH;
142     } else {
143       ledState = LOW;
144     }
145     // задаем светодиоду значение из переменной «ledState»:
146     digitalWrite(ledPin, ledState);
147   }
148  
149   Serial.println("Publish received.");
150              //  "Опубликованные данные получены."
151   Serial.print("  message: ");  //  "  сообщение: "
152   Serial.println(messageTemp);
153   Serial.print("  topic: ");  //  "  топик: "
154   Serial.println(topic);
155   Serial.print("  qos: ");  //  "  уровень обслуживания: "
156   Serial.println(properties.qos);
157   Serial.print("  dup: ");  //  "  дублирование сообщения: "
158   Serial.println(properties.dup);
159   Serial.print("  retain: ");  //  "сохраненные сообщения: "
160   Serial.println(properties.retain);
161   Serial.print("  len: ");  //  "  размер: "
162   Serial.println(len);
163   Serial.print("  index: ");  //  "  индекс: "
164   Serial.println(index);
165   Serial.print("  total: ");  //  "  суммарно: "
166   Serial.println(total);
167 }
168 
169 void setup() {
170   // запускаем датчик DS18B20:
171   sensors.begin();
172   // делаем контакт, к которому подключен светодиод,
173   // выходным контактом, и присваиваем ему значение «LOW»:
174   pinMode(ledPin, OUTPUT);
175   digitalWrite(ledPin, LOW);
176   
177   Serial.begin(115200);
178 
179   mqttReconnectTimer = xTimerCreate("mqttTimer", pdMS_TO_TICKS(2000), pdFALSE, (void*)0, reinterpret_cast<TimerCallbackFunction_t>(connectToMqtt));
180   wifiReconnectTimer = xTimerCreate("wifiTimer", pdMS_TO_TICKS(2000), pdFALSE, (void*)0, reinterpret_cast<TimerCallbackFunction_t>(connectToWifi));
181 
182   WiFi.onEvent(WiFiEvent);
183 
184   mqttClient.onConnect(onMqttConnect);
185   mqttClient.onDisconnect(onMqttDisconnect);
186   mqttClient.onSubscribe(onMqttSubscribe);
187   mqttClient.onUnsubscribe(onMqttUnsubscribe);
188   mqttClient.onMessage(onMqttMessage);
189   mqttClient.onPublish(onMqttPublish);
190   mqttClient.setServer(MQTT_HOST, MQTT_PORT);
191 
192   connectToWifi();
193 }
194 
195 void loop() {
196   unsigned long currentMillis = millis();
197   // каждые X секунд («interval» = 5 секунд) 
198   // в топик «esp32/temperature»
199   // будет публиковаться новое MQTT-сообщение:
200   if (currentMillis - previousMillis >= interval) {
201     // сохраняем в переменную «previousMillis» время,
202     // когда в последний раз были опубликованы новые данные:
203     previousMillis = currentMillis;
204     // новые данные о температуре:
205     sensors.requestTemperatures(); 
206     temperatureString = " " + String(sensors.getTempCByIndex(0)) + "C  " + String(sensors.getTempFByIndex(0)) + "F";     
207     Serial.println(temperatureString);
208     // публикуем MQTT-сообщение в топике «esp32/temperature»
209     // с температурой в градусах Цельсия и Фаренгейта:
210     uint16_t packetIdPub2 = mqttClient.publish("esp32/temperature", 2, true, temperatureString.c_str());
211     Serial.print("Publishing on topic esp32/temperature at QoS 2, packetId: ");  
212              //  "Публикация в топик «esp32/temperature»
213              //   при QoS 2, ID пакета: "
214     Serial.println(packetIdPub2);
215   }
216 }

ESP32 #2

  1 /*********
  2   Руи Сантос
  3   Более подробно о проекте на: https://randomnerdtutorials.com  
  4 *********/
  5 
  6 #include <WiFi.h>
  7 extern "C" {
  8   #include "freertos/FreeRTOS.h"
  9   #include "freertos/timers.h"
 10 }
 11 #include <AsyncMqttClient.h>
 12 #include <LiquidCrystal_I2C.h>
 13 
 14 // вставьте в строчках ниже SSID и пароль для своей WiFi-сети,
 15 // чтобы ESP32 могла подключиться к вашему WiFi-роутеру:
 16 #define WIFI_SSID "REPLACE_WITH_YOUR_SSID"
 17 #define WIFI_PASSWORD "REPLACE_WITH_YOUR_PASSWORD"
 18 
 19 // задайте в переменной «MQTT_HOST» IP-адрес своей Raspberry Pi,
 20 // чтобы ESP32 могла подключиться к MQTT-брокеру Mosquitto:
 21 #define MQTT_HOST IPAddress(192, 168, 1, XXX)
 22 #define MQTT_PORT 1883
 23 
 24 // создаем объекты для управления MQTT-клиентом:
 25 AsyncMqttClient mqttClient;
 26 TimerHandle_t mqttReconnectTimer;
 27 TimerHandle_t wifiReconnectTimer;
 28 
 29 // задаем количество столбцов и рядов для LCD-дисплея:
 30 const int lcdColumns = 16;
 31 const int lcdRows = 2;
 32 
 33 // создаем объект LCD-дисплея,
 34 // присваивая ему адрес, а также количество столбцов и рядов;
 35 // (если вам неизвестен адрес дисплея,
 36 // запустите скетч для сканирования I2C-устройств):
 37 LiquidCrystal_I2C lcd(0x27, lcdColumns, lcdRows);
 38 
 39 // иконка термометра:
 40 byte thermometerIcon[8] = {
 41   B00100,
 42   B01010,
 43   B01010,
 44   B01010,
 45   B01010,
 46   B10001,
 47   B11111,
 48   B01110
 49 };
 50 
 51 const int buttonPin = 32;            // GPIO-контакт, к которому 
 52                                      // подключена кнопка
 53 int buttonState;                     // текущее значение 
 54                                      // входного контакта (кнопки) 
 55 int lastButtonState = LOW;           // предыдущее значение
 56                                      // входного контакта (кнопки) 
 57 unsigned long lastDebounceTime = 0;  // время, когда в последний раз 
 58                                      // был переключен 
 59                                      // выходной контакт               
 60 unsigned long debounceDelay = 50;    // период антидребезга 
 61                                      // (увеличьте, если
 62                                      //  выходное значение «прыгает»)
 63 
 64 void connectToWifi() {
 65   Serial.println("Connecting to Wi-Fi...");
 66              //  "Подключаемся к WiFi..."
 67   WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
 68 }
 69 
 70 void connectToMqtt() {
 71   Serial.println("Connecting to MQTT...");
 72              //  "Подключаемся к MQTT..."
 73   mqttClient.connect();
 74 }
 75 
 76 void WiFiEvent(WiFiEvent_t event) {
 77   Serial.printf("[WiFi-event] event: %d\n", event);
 78   switch(event) {
 79     case SYSTEM_EVENT_STA_GOT_IP:
 80       Serial.println("WiFi connected");  //  "Подключились к WiFi"
 81       Serial.println("IP address: ");  //  "IP-адрес: "
 82       Serial.println(WiFi.localIP());
 83       connectToMqtt();
 84       break;
 85     case SYSTEM_EVENT_STA_DISCONNECTED:
 86       Serial.println("WiFi lost connection");
 87                  //  "WiFi-связь потеряна"
 88       // делаем так, чтобы ESP32
 89       // не переподключалась к MQTT
 90       // во время переподключения к WiFi:
 91       xTimerStop(mqttReconnectTimer, 0);
 92       xTimerStart(wifiReconnectTimer, 0);
 93       break;
 94   }
 95 }
 96 
 97 // в эту функцию можно добавить новые топики для подписки:
 98 void onMqttConnect(bool sessionPresent) {
 99   Serial.println("Connected to MQTT.");  //  "Подключились к MQTT."
100   Serial.print("Session present: ");  //  "Текущая сессия: "
101   Serial.println(sessionPresent);
102   uint16_t packetIdSub = mqttClient.subscribe("esp32/temperature", 0);
103   Serial.print("Subscribing at QoS 0, packetId: ");
104            //  "Подписка при QoS 0, ID пакета: "
105   Serial.println(packetIdSub);
106 }
107 
108 void onMqttDisconnect(AsyncMqttClientDisconnectReason reason) {
109   Serial.println("Disconnected from MQTT.");
110              //  "Отключились от MQTT."
111   if (WiFi.isConnected()) {
112     xTimerStart(mqttReconnectTimer, 0);
113   }
114 }
115 
116 void onMqttSubscribe(uint16_t packetId, uint8_t qos) {
117   Serial.println("Subscribe acknowledged.");
118              //  "Подписка подтверждена."
119   Serial.print("  packetId: ");  //  "  ID пакета: "
120   Serial.println(packetId);
121   Serial.print("  qos: ");  //  "  уровень качества обслуживания: "
122   Serial.println(qos);
123 }
124 
125 void onMqttUnsubscribe(uint16_t packetId) {
126   Serial.println("Unsubscribe acknowledged.");
127              //  "Отписка подтверждена."
128   Serial.print("  packetId: ");
129   Serial.println(packetId);
130 }
131 
132 void onMqttPublish(uint16_t packetId) {
133   Serial.println("Publish acknowledged.");
134              //  "Публикация подтверждена."
135   Serial.print("  packetId: ");
136   Serial.println(packetId);
137 }
138 
139 // эта функция управляет тем, что произойдет
140 // при получении топиком «esp32/temperature» того или иного сообщения
141 // (эту функцию можно отредактировать):
142 void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
143   String messageTemp;
144   for (int i = 0; i < len; i++) {
145     //Serial.print((char)payload[i]);
146     messageTemp += (char)payload[i];
147   }
148   if (strcmp(topic, "esp32/temperature") == 0) {
149     lcd.clear();
150     lcd.setCursor(1, 0);
151     lcd.write(0);
152     lcd.print(" Temperature");  //  " Температура"
153     lcd.setCursor(0, 1);
154     lcd.print(messageTemp);
155   }
156 
157   Serial.println("Publish received.");
158              //  "Опубликованные данные получены."
159   Serial.print("  message: ");  //  "  сообщение: "
160   Serial.println(messageTemp);
161   Serial.print("  topic: ");  //  "  топик: "
162   Serial.println(topic);
163   Serial.print("  qos: ");  //  "  уровень качества обслуживания: "
164   Serial.println(properties.qos);
165   Serial.print("  dup: ");  //  "  дублирование сообщения: "
166   Serial.println(properties.dup);
167   Serial.print("  retain: ");  //  "  сохраненные сообщения: "
168   Serial.println(properties.retain);
169   Serial.print("  len: ");  //  "  размер: "
170   Serial.println(len);
171   Serial.print("  index: ");  //  "  индекс: "
172   Serial.println(index);
173   Serial.print("  total: ");  //  "  суммарно: "
174   Serial.println(total);
175 }
176 
177 void setup() {
178   // инициализируем LCD-дисплей:
179   lcd.init();
180   // включаем подсветку LCD-дисплея:
181   lcd.backlight();
182   // создаем иконку термометра:
183   lcd.createChar(0, thermometerIcon);
184   
185   // делаем контакт, к которому подключена кнопка, входным контактом: 
186   pinMode(buttonPin, INPUT);
187 
188   Serial.begin(115200);
189 
190   mqttReconnectTimer = xTimerCreate("mqttTimer", pdMS_TO_TICKS(2000), pdFALSE, (void*)0, reinterpret_cast<TimerCallbackFunction_t>(connectToMqtt));
191   wifiReconnectTimer = xTimerCreate("wifiTimer", pdMS_TO_TICKS(2000), pdFALSE, (void*)0, reinterpret_cast<TimerCallbackFunction_t>(connectToWifi));
192 
193   WiFi.onEvent(WiFiEvent);
194 
195   mqttClient.onConnect(onMqttConnect);
196   mqttClient.onDisconnect(onMqttDisconnect);
197   mqttClient.onSubscribe(onMqttSubscribe);
198   mqttClient.onUnsubscribe(onMqttUnsubscribe);
199   mqttClient.onMessage(onMqttMessage);
200   mqttClient.onPublish(onMqttPublish);
201   mqttClient.setServer(MQTT_HOST, MQTT_PORT);
202 
203   connectToWifi();
204 }
205 
206 void loop() { 
207   // считываем состояние кнопки
208   // и сохраняем его в локальную переменную:
209   int reading = digitalRead(buttonPin);
210 
211   // если состояние кнопки изменилось
212   // (из-за шума или нажатия), сбрасываем таймер:
213   if (reading != lastButtonState) {
214     // сбрасываем таймер антидребезга:
215     lastDebounceTime = millis();
216   }
217   
218   // если состояние кнопки изменилось после периода антидребезга:
219   if ((millis() - lastDebounceTime) > debounceDelay) {
220     // и если новое значение кнопки
221     // отличается от того, что сейчас хранится в «buttonState»: 
222     if (reading != buttonState) {
223       buttonState = reading;
224       // публикуем MQTT-сообщение в топике «esp32/led»,
225       // чтобы переключить (включить или выключить) светодиод:
226       if (buttonState == HIGH) {
227         mqttClient.publish("esp32/led", 0, true, "toggle");
228         Serial.println("Publishing on topic esp32/led topic at QoS 0");    
229                    //  "Публикация в топик «esp32/led» при QoS 0"
230       }
231     }
232   }
233   // сохраняем текущее значение кнопки в переменную «lastButtonState»;
234   // в результате во время следующего прохода через loop()
235   // оно будет считаться предыдущим значением кнопки:
236   lastButtonState = reading;
237 }

См.также

Внешние ссылки