ESP32:Примеры/Подключение ESP32 к Node-RED при помощи MQTT

Материал из Онлайн справочника
Версия от 23:50, 28 января 2019; Myagkij (обсуждение | вклад)
(разн.) ← Предыдущая версия | Текущая версия (разн.) | Следующая версия → (разн.)
Перейти к навигацииПерейти к поиску

Перевод: Максим Кузьмин
Проверка/Оформление/Редактирование: Мякишев Е.А.


Черновик


Подключение ESP32 к Node-RED при помощи MQTT

В этом примере мы продемонстрируем, как использовать панель управления Node-RED для управления GPIO-контактами ESP32 и показывать на графике данные, считанные с температурного датчика. Мы создадим простой проект, с помощью которого научимся нескольким важным вещам – тому, как настроить подписку и публикацию при помощи Node-RED.

Вот общая схема проекта, который мы сделаем:

  • ESP32 подключена к кнопке. При нажатии на эту кнопку ESP32 опубликует сообщение в топик «esp32/led/toggle», на который подписана Node-RED. Получив это сообщение, Node-RED переключит состояние у переключателя «LED» на панели управления;
  • Когда состояние у переключателя «LED» на панели управления изменится, сообщение об этом будет опубликовано в топик «esp32/led», на который подписана плата ESP32. Таким образом, получив это сообщение, ESP32 переключит состояние подключенного к ней светодиода (вместо него можно использовать любое другое устройство вывода данных);
  • ESP32 будет считывать данные о температуре с температурного датчика DS18B20 и публиковать их в топик «esp32/temperature»;
  • Node-RED подписана на топик «esp32/temperature» и потому будет получать температурные данные от датчика DS18B20, а затем публиковать их в график.

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

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

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

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

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

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

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

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

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

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

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

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

Топики

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

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

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

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

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

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

Брокер

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

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

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

Итого

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

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

Node-RED

Этот Раздел – краткое введение в тему Node-RED. Здесь мы расскажем о том, что такое Node-RED и как ее установить. Мы также покажем, как установить ноды панели управления Node-RED.

Примечание: Этот Раздел не предназначен для того, чтобы научить вас создавать сложные потоки при помощи платформы Node-RED или объяснять, как работает весь ее функционал. В этом и следующем Разделах мы лишь предоставим необходимую информацию о том, как подключить ESP32 к Node-RED при помощи коммуникационного протокола MQTT. Многие мейкеры используют Node-RED для своих проектов в сфере домашней автоматизации, и им может быть интересно, как наладить коммуникацию между ESP32 и Node-RED. Эти два Раздела предназначены как раз для этой аудитории.

Что такое Node-RED?

Node-RED – это мощный open-source инструмент для создания проектов в области интернета вещей при помощи визуального программирования.

Визуальное программирование – это способ создания программ с помощью подключения друг к другу блоков кода (называемых «нодами»), что сильно упрощает значительную часть программирования. Последовательность подключенных друг к другу нод называется «потоком».

Почему Node-RED – это хорошее решение?

Node-RED – это open-source проект, который разработан IBM и отлично работает на Raspberry Pi.

Node-RED дает возможность быстро делать прототипы сложных систем домашней автоматизации, благодаря чему у вас остается больше времени на проектирование и прочие интересные вещи.

Что можно сделать при помощи Node-RED?

Node-RED упрощает:

  • Доступ к GPIO-контактам Raspberry Pi;
  • Настройку MQTT-коммуникации с другими платами (ESP32, ESP8266, Arduino и т.д.);
  • Создание отзывчивого GUI для проектов (при помощи панели управления Node-RED);
  • Коммуникацию со сторонними сервисами (IFTTT.com, Adafruit.io, Thing Speak и т.д.);
  • Считывание данных из сети (прогнозы погоды, курсы акций, имейлы и т.д.);
  • Создание событий, управляемых с помощью времени;
  • Хранение и считывание информации из базы данных;

Вот библиотека с примерами потоков и нод для Node-RED.

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

Нам нужно установить в 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 если она была запущена;

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

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

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

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

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

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

sudo apt update
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

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

Итого

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

Устанавливаем Node-RED

Node-RED устанавливается на Raspberry Pi очень быстро и просто. Откройте терминал Raspberry Pi и введите туда следующую команду:

bash <(curl -sL https://raw.githubusercontent.com/node-red/raspbian-deb-package/master/resources/update-nodejs-and-nodered)

Через несколько минут установка должна завершиться.

Настраиваем автоматический запуск Node-RED при загрузке

Чтобы Node-RED автоматически запускалась при загрузке Raspberry Pi, вам нужно ввести следующую команду:

sudo systemctl enable nodered.service

Теперь перезапустите Raspberry Pi, чтобы автозапуск вступил в силу:

sudo reboot

Ищем IP-адрес Raspberry Pi

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

hostname -I

В моем случае IP-адрес Raspberry Pi – это «192.168.1.2». Сохраните IP-адрес, который получился у вас, потому что он еще пригодится нам в этом и следующем Разделах.

Проверяем, установилась ли Node-RED

Когда Raspberry Pi снова включится, мы можем протестировать установку Node-RED, введя в браузере IP-адрес Raspberry Pi, который мы узнали выше, а после него – номер порта «1880».

http://IP_АДРЕС_ВАШЕЙ_RPi:1880

В моем случаем он выглядит так:

http://192.168.1.2:1880

В результате в браузере должна загрузиться вот такая страница:

Обзор интерфейса Node-RED

В левой части находится список блоков. Эти блоки называются нодами, и они разделены на группы по функциональности. Выбрав ноду, взгляните на вкладку info – там должна появиться информация о том, как работает выбранная нода.

В центре расположена область для потока – сюда вы будете помещать свои ноды.

Установка панели управления Node-RED

Панель управления Node-RED – это набор нод, предоставляющих инструменты для создания пользовательского интерфейса, который оснащен кнопками, графиками, текстом и так далее.

Более подробно о панели управления Node-RED можно почитать по этим ссылкам:

Чтобы установить панель управления Node-RED, перейдите в меню Settings (это кнопка с тремя горизонтальными полосками в правом верхнем углу страницы) и нажмите на Manage palette:

Откроется меню пользовательских настроек – User Settings. Кликните на вкладку Install, вбейте в поиске «node-red-dashboard» и нажмите на кнопку Install:

Закройте это меню. В левой панели появится новая группа нодов под названием «dashboard»:

Теперь у вас все готово, чтобы встроить Node-RED в систему с ESP32 и создать пользовательский интерфейс для взаимодействия с ESP32.

Создаем поток Node-RED

Перед созданием потока на вашей Raspberry Pi должны быть установлены:

Импортируем поток

После этого импортируем поток Node-RED. Для этого копируем RAW-версию этого потока:

[{"id":"9e58624.7faaba","type":"mqtt out","z":"c02b79b2.501998","name":"","topic":"esp32/led","qos":"","retain":"","broker":"10e78a89.5b4fd5","x":740,"y":520,"wires":[]},{"id":"abf7079a.653be8","type":"mqtt in","z":"c02b79b2.501998","name":"","topic":"esp32/temperature","qos":"2","broker":"10e78a89.5b4fd5","x":430,"y":300,"wires":[["cc79021b.9a751","46e7770d.86d9e8"]]},{"id":"83cf37cf.c76988","type":"ui_switch","z":"c02b79b2.501998","name":"","label":"LED","group":"61285987.c20328","order":0,"width":0,"height":0,"passthru":true,"decouple":"false","topic":"","style":"","onvalue":"on","onvalueType":"str","onicon":"","oncolor":"","offvalue":"off","offvalueType":"str","officon":"","offcolor":"","x":590,"y":520,"wires":[["9e58624.7faaba"]]},{"id":"cc79021b.9a751","type":"debug","z":"c02b79b2.501998","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":630,"y":260,"wires":[]},{"id":"dd25cb97.5921d8","type":"mqtt in","z":"c02b79b2.501998","name":"","topic":"esp32/led/toggle","qos":"2","broker":"10e78a89.5b4fd5","x":420,"y":480,"wires":[["83cf37cf.c76988","b9659d3c.bf3d3"]]},{"id":"b9659d3c.bf3d3","type":"debug","z":"c02b79b2.501998","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":610,"y":440,"wires":[]},{"id":"46e7770d.86d9e8","type":"ui_chart","z":"c02b79b2.501998","name":"","group":"61285987.c20328","order":0,"width":0,"height":0,"label":"Temperature","chartType":"line","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":1,"removeOlderPoints":"","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"colors":["#1f77b4","#aec7e8","#ff7f0e","#2ca02c","#98df8a","#d62728","#ff9896","#9467bd","#c5b0d5"],"useOldStyle":false,"x":630,"y":340,"wires":[[],[]]},{"id":"10e78a89.5b4fd5","type":"mqtt-broker","z":"","name":"","broker":"localhost","port":"1883","clientid":"","usetls":false,"compatmode":true,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""},{"id":"61285987.c20328","type":"ui_group","z":"","name":"Main","tab":"e7c46d5e.a1283","disp":true,"width":"6","collapse":false},{"id":"e7c46d5e.a1283","type":"ui_tab","z":"","name":"Dashboard","icon":"dashboard"}]

Далее в Node-RED жмем на кнопку в правом верхнем углу (с тремя горизонтальными полосками), а затем на Import > Clipboard.

Затем вставляем скопированный поток в поле и жмем на кнопку Import.

В поток должны загрузиться следующие ноды:

Внеся в поток необходимые изменения, кликните на кнопку Deploy, чтобы сохранить их.

Что это за поток

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

Нода MQTT-подписки (mqtt in)

Самая первая нода – это нода MQTT-подписки (mqtt in). Если дважды кликнуть по ней, откроется такое окно:

В этой ноде можно задать топик, на который вы хотите подписаться. В данном случае мы подписываемся на топик «esp32/temperature», чтобы получать от него данные о температуре.

В поле Server вписывается IP-адрес MQTT-брокера. В нашем случае брокер Mosquitto и Node-RED работают на одной и той же Raspberry Pi, что позволяет нам использовать «localhost».

В поле QoS вписывается уровень качества обслуживания MQTT, а в поле Name – название ноды.

Нода графика (chart)

При помощи этой ноды мы, получив из топика «esp32/temperature» данные о температуре, будем показывать их на графике. Дважды кликните по этой ноде – откроется новое окно, где можно отредактировать свойства этой ноды.

Еще одна нода MQTT-подписки (mqtt in)

Это вторая в потоке MQTT-нода для подписки, но теперь – на топик «esp32/led/toggle», в который будут приходить данные об управлении (включении/выключении) светодиодом. В него может прийти сообщение «on» или «off».

Нода переключателя светодиода (switch)

Сконструированная нами система позволит управлять светодиодом двумя способами:

  • При помощи программного переключателя на панели управления Node-RED. В этом случае при изменении положения переключателя в топик «esp32/led» будет опубликовано MQTT-сообщение, дающее команду включить/выключить светодиод;
  • При помощи нажатия на аппаратную кнопку, подключенную к ESP32. После нажатия на нее ESP32 опубликует в топик «esp32/led/toggle» соответствующее сообщение. Поскольку на этот топик подписан программный переключатель на панели управления Node-RED (о котором рассказывалось выше), он автоматически переключится согласно присланному сообщению и, соответственно, изменит состояние светодиода.

Нода MQTT-публикации (mqtt out)

Это нода для публикации MQTT-сообщения от переключателя светодиода в топик «esp32/led».

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

Настройки MQTT-брокера

Все вышесказанное строится на том, что Node-RED и MQTT-брокер установлены на одной и той же Raspberry Pi. Но если вы используете другой MQTT-брокер, то вам нужно будет сделать еще кое-что. Во-первых, дважды кликните по одной из MQTT-нод:

Затем нажмите на кнопку Edit рядом с полем Server:

Впишите туда IP-адрес и номер порта вашего MQTT-сервера:

Примечание: Поскольку в нашем случае брокер Mosquitto работает на Raspberry Pi локально, мы можем оставить в поле Server «localhost», а в поле Port – «1883». В вашем случае эти настройки, возможно, нужно будет поменять (и сохранить внесенные изменения), чтобы Node-RED могла установить MQTT-подключение с вашим брокером.

Панель управления Node-RED

Итак, проект готов. Чтобы открыть панель управления Node-RED и посмотреть, как выглядит наше приложение, откройте любой браузер в вашей локальной сети и впишите туда следующее:

http://IP-адрес-вашей-Raspberry-Pi:1880/ui

Ваше приложение должно выглядеть примерно как на скриншоте ниже:

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

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

// впишите в двух строчках ниже SSID и пароль для своей WiFi-сети,
// чтобы ваша ESP32 могла подключиться к WiFi-роутеру:
#define WIFI_SSID "REPLACE_WITH_YOUR_SSID"
#define WIFI_PASSWORD "REPLACE_WITH_YOUR_PASSWORD"

// вставьте в переменную «MQTT_HOST» IP-адрес своей Raspberry Pi,
// чтобы ESP32 могла подключиться к MQTT-брокеру Mosquitto:
#define MQTT_HOST IPAddress(192, 168, 1, X)

Температура в градусах Цельсия/Фаренгейта

По умолчанию этот скетч публикует температуру в градусах Цельсия. Если вы хотите, чтобы он публиковал температуру в градусах Фаренгейта, перейдите в блок loop() и закомментируйте вот эти две строчки кода:

    // публикуем в топик «esp32/temperature»
    // MQTT-сообщение с температурой в градусах Цельсия:
    // uint16_t packetIdPub2 = mqttClient.publish("esp32/temperature",
    // 2, true, String(sensors.getTempCByIndex(0)).c_str());

Затем раскомментируйте вот эти две строчки:

    // публикуем в топик «esp32/temperature»
    // MQTT-сообщение с температурой в градусах Фаренгейта:
    uint16_t packetIdPub2 = mqttClient.publish("esp32/temperature", 
    2, true, String(sensors.getTempFByIndex(0)).c_str());

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

Попробуйте включить/выключить светодиод, щелкая переключатель «LED»:

Кроме того, если нажать на кнопку, подключенную к ESP32, это тоже должно изменить состояние светодиода, т.к. его автоматически обновляет переключатель на панели управления Node-RED.

Наконец, каждые 10 секунд на графике будут показываться новые данные о температуре от датчика DS18B20.

Итого

В этом проекте-примере мы продемонстрировали, как настроить коммуникацию между ESP32 и Node-RED при помощи MQTT. В частности, мы показали, как публиковать в топик (чтобы управлять контактами ESP32) и подписываться на топики (чтобы получать команды и данные о температуре).

Теперь вы можете без труда создать систему для управления ESP32 при помощи панели управления Node-RED – например, для какого-нибудь проекта домашней автоматизации.

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

Схема

Код

/*********
  Руи Сантос
  Более подробно о проекте на: https://randomnerdtutorials.com  
*********/

#include <WiFi.h>
extern "C" {
  #include "freertos/FreeRTOS.h"
  #include "freertos/timers.h"
}
#include <AsyncMqttClient.h>
#include <OneWire.h>
#include <DallasTemperature.h>

// впишите в двух строчках ниже SSID и пароль для своей WiFi-сети,
// чтобы ваша ESP32 могла подключиться к WiFi-роутеру:
#define WIFI_SSID "REPLACE_WITH_YOUR_SSID"
#define WIFI_PASSWORD "REPLACE_WITH_YOUR_PASSWORD"

// вставьте в переменную «MQTT_HOST» IP-адрес своей Raspberry Pi,
// чтобы ESP32 могла подключиться к MQTT-брокеру Mosquitto:
#define MQTT_HOST IPAddress(192, 168, 1, X)
#define MQTT_PORT 1883

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

unsigned long previousMillis = 0;  // время, когда в последний раз
                                   // была опубликована температура 
const long interval = 10000;       // интервал, с которым будут
                                   // публиковаться данные от датчика 

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

// GPIO-контакт, к которому подключен датчик DS18B20:
const int oneWireBus = 27;          
// создаем объект «oneWire» для коммуникации с OneWire-устройствами: 
OneWire oneWire(oneWireBus);
// передаем объект «oneWire» объекту температурного датчика:
DallasTemperature sensors(&oneWire);

const int buttonPin = 32;            // задаем GPIO-контакт,
                                     // к которому подключена кнопка 
int buttonState;                     // текущее состояние
                                     // входного контакта (кнопки) 
int lastButtonState = LOW;           // предыдущее состояние
                                     // входного контакта (кнопки) 
unsigned long lastDebounceTime = 0;  // время, когда в последний раз 
                                     // был переключен
                                     // выходной контакт
unsigned long debounceDelay = 50;    // время антидребезга
                                     // (увеличьте это значение,
                                     //  если выходной сигнал
                                     //  по-прежнему «прыгает») 

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

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

void WiFiEvent(WiFiEvent_t event) {
  Serial.printf("[WiFi-event] event: %d\n", event);
  switch(event) {
    case SYSTEM_EVENT_STA_GOT_IP:
      Serial.println("WiFi connected");  //  "Подключились к WiFi"
      Serial.println("IP address: ");  //  "IP-адрес: "
      Serial.println(WiFi.localIP());
      connectToMqtt();
      break;
    case SYSTEM_EVENT_STA_DISCONNECTED:
      Serial.println("WiFi lost connection");
                 //  "WiFi-связь потеряна"
      // делаем так, чтобы
      // ESP32 не переподключалась к MQTT
      // во время переподключения к WiFi:
      xTimerStop(mqttReconnectTimer, 0);
      xTimerStart(wifiReconnectTimer, 0);
      break;
  }
}

// в этой функции можно добавить новые топики для подписки:
void onMqttConnect(bool sessionPresent) {
  Serial.println("Connected to MQTT.");  //  "Подключились к MQTT."
  Serial.print("Session present: ");  //  "Текущая сессия: "
  Serial.println(sessionPresent);
  // подписываем ESP32 на топик «esp32/led»:
  uint16_t packetIdSub = mqttClient.subscribe("esp32/led", 0);
  Serial.print("Subscribing at QoS 0, packetId: ");
           //  "Подписываемся при QoS 0, ID пакета: "
  Serial.println(packetIdSub);
}

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

void onMqttSubscribe(uint16_t packetId, uint8_t qos) {
  Serial.println("Subscribe acknowledged.");
             //  "Подписка подтверждена."
  Serial.print("  packetId: ");  //  "  ID пакета: "
  Serial.println(packetId);
  Serial.print("  qos: ");  //  "  уровень качества обслуживания: "
  Serial.println(qos);
}

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

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

// в этой функции задается, что произойдет,
// когда ESP32 получит то или иное сообщение в топик «esp32/led»
// (эту функцию можно отредактировать):
void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
  String messageTemp;
  for (int i = 0; i < len; i++) {
    //Serial.print((char)payload[i]);
    messageTemp += (char)payload[i];
  }
  // проверяем, пришло ли MQTT-сообщение в топик «esp32/led»:
  if (strcmp(topic, "esp32/led") == 0) {
    // если светодиод выключен, включаем его (и наоборот):
    if (messageTemp == "on") {
      digitalWrite(ledPin, HIGH);
    } 
    else if (messageTemp == "off") {
      digitalWrite(ledPin, LOW);
    }
  }
 
  Serial.println("Publish received.");  
             //  "Опубликованные данные получены."
  Serial.print("  message: ");  //  "  сообщение: "
  Serial.println(messageTemp);
  Serial.print("  topic: ");  //  "  топик: "
  Serial.println(topic);
  Serial.print("  qos: ");  //  "  уровень качества обслуживания: "
  Serial.println(properties.qos);
  Serial.print("  dup: ");  //  "  дублирование сообщения: "
  Serial.println(properties.dup);
  Serial.print("  retain: ");  //  "  сохраненные сообщения: "
  Serial.println(properties.retain);
  Serial.print("  len: ");  //  "  размер: "
  Serial.println(len);
  Serial.print("  index: ");  //  "  индекс: "
  Serial.println(index);
  Serial.print("  total: ");  //  "  суммарно: "
  Serial.println(total);
}

void setup() {
  // запускаем датчик DS18B20:
  sensors.begin();
  
  // делаем контакт светодиода выходным (OUTPUT)
  // и задаем ему значение «LOW»:
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);
  
  // делаем контакт кнопки входным (INPUT):
  pinMode(buttonPin, INPUT);
  
  Serial.begin(115200);

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

  WiFi.onEvent(WiFiEvent);

  mqttClient.onConnect(onMqttConnect);
  mqttClient.onDisconnect(onMqttDisconnect);
  mqttClient.onSubscribe(onMqttSubscribe);
  mqttClient.onUnsubscribe(onMqttUnsubscribe);
  mqttClient.onMessage(onMqttMessage);
  mqttClient.onPublish(onMqttPublish);
  mqttClient.setServer(MQTT_HOST, MQTT_PORT);

  connectToWifi();
}

void loop() {
  unsigned long currentMillis = millis();
  // каждые Х секунд («interval» = 5 секунд)
  // ESP32 будет публиковать новое MQTT-сообщение
  // в топик «esp32/temperature»:
  if (currentMillis - previousMillis >= interval) {
    // сохраняем время, когда в последний раз
    // были опубликованы новые данные от датчика:
    previousMillis = currentMillis;
    // новые температурные данные:
    sensors.requestTemperatures(); 

    // публикуем в топик «esp32/temperature»
    // MQTT-сообщение с температурой в градусах Цельсия:
    uint16_t packetIdPub2 = mqttClient.publish("esp32/temperature", 2, 
    true, String(sensors.getTempCByIndex(0)).c_str());
                            
    // публикуем в топик «esp32/temperature»
    // MQTT-сообщение с температурой в градусах Фаренгейта:
    // uint16_t packetIdPub2 = mqttClient.publish("esp32/temperature", 
    // 2, true, String(sensors.getTempFByIndex(0)).c_str());
                            
    Serial.print("Publishing on topic esp32/temperature at QoS 2, packetId: ");
             //  "Публикуем в топик «esp32/temperature» 
             //   при QoS 2, ID пакета: "
    Serial.println(packetIdPub2);
  }

  // считываем состояние кнопки
  // и сохраняем его в локальную переменную:
  int reading = digitalRead(buttonPin);

  // если состояние кнопки изменилось (из-за шума или нажатия),
  // сбрасываем таймер:
  if (reading != lastButtonState) {
    // сбрасываем таймер «антидребезга»:
    lastDebounceTime = millis();
  }
  
  // если состояние кнопки изменилось после периода «антидребезга»:
  if ((millis() - lastDebounceTime) > debounceDelay) {
    // и если новое значение отличается от того,
    // что хранится сейчас в переменной «buttonState»:
    if (reading != buttonState) {
      buttonState = reading;
      // публикуем MQTT-сообщение в топик «esp32/led/toggle»
      // чтобы переключить состояние светодиода 
      // (т.е. чтобы включить или выключить его):
      if ((buttonState == HIGH)) {
        if (!digitalRead(ledPin)) {
          mqttClient.publish("esp32/led/toggle", 0, true, "on");
          Serial.println("Publishing on topic esp32/led/toggle topic at QoS 0");
                     //  "Публикуем в топик «esp32/led/toggle»
                     //   при QoS 0"
        }
        else if (digitalRead(ledPin)) {
          mqttClient.publish("esp32/led/toggle", 0, true, "off");
          Serial.println("Publishing on topic esp32/led/toggle topic at QoS 0");
                     //  "Публикуем в топик «esp32/led/toggle»
                     //   при QoS 0"
        }
      }
    }
  }
  // сохраняем новое значение кнопки в переменную «lastButtonState»;
  // в результате при следующем проходе через loop()
  // новое значение кнопки будет считаться ее предыдущим значением:
  lastButtonState = reading;
}

См.также

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