Перейти к содержимому

Телеметрия на ESP8266 и MQTT. Пошаговое руководство по созданию DIY-проекта с удаленным управлением

Добрый день, уважаемый читатель!

Данная статья ориентирована в первую очередь на начинающих программистов, которые только начинают свой творческий путь программирования микроконтроллеров, в данном случае – ESP8266.

Повторив этот проект вы можете создать с помощью микроконтроллера ESP8266, WiFi, MQTT и некоторой доли творчества:

  • Дистанционный контроль и управление вашими устройствам, причем не только в локальной сетке, но и из любой точки планеты, где есть интернет
  • Удаленное управление реле и нагрузкой, например включение и отключение освещения или котла
  • Обратный контроль состояния нагрузки с устройства – получило ли оно вашу команду или нет
  • Удаленный мониторинг логических состояний на входах микроконтроллера, что позволяет создать простенькую охранную систему например
  • Удаленный мониторинг температуры и влажности в помещении, на улице, котла и т.д.
  • Оперативные уведомления о различных событиях в Telegram *
  • Отправка данных на сторонние сервисы (Thing Speak, Народный мониторинг, Open Monitoring) для отслеживания изменений во времени и построения графиков *

При этом я не ставлю Вам “жестких” рамок, у Вас всегда остается свобода для собственного творчества. При творческом подходе не сложно прикрутить управление нагрузкой по автоматическим сценариям, например включение котла по температуре. И все это с дистанционным управлением.

Примечания: функции, отмеченные * и курсивом обсудим в следующих статьях цикла про ESP8266 и Arduino-framework

 

В данной статье я подробно и по шагам расскажу как:

  1. Создать Arduino – проект для ESP8266 в более продвинутой и удобной среде разработки Visual Studio Code + PlatformIO.
  2. Подключить ESP8266 к вашей сети WiFi.
  3. Подключить необходимые библиотеки к проекту.
  4. Получить точное текущее время с NTP-сервера в интернете.
  5. Подключиться к MQTT-брокеру, в том числе и по TLS/SSL-протоколу
  6. Создать устройство для дистанционного управления реле через MQTT
  7. Опубликовать состояние цифровых входов (например нажатие на кнопку) на MQTT-брокере
  8. Прочитать и опубликовать температуру с датчика температуры и влажности, например DHT22

 

Вам понадобятся:

В конце статьи, как обычно, есть ссылка на готовый проект на GitHub (вам останется только изменить данные для подключения на свои), так что если Вам лень читать – листайте в конец статьи. Осторожно – многа букав…


Подготовка среды разработки

Если вы заглянете в демо-проекты kotyara12/arduino, то, наверное, заметите, что в архиве нет никаких файлов .ino, а есть main.cpp и platformio.ini. Это означает, что проекты собраны не в классической Arduino IDE, а в Visual Studio Code + PlatformIO.

<sarcazm>Конечно, вы можете легко перенести код из main.cpp в классическую Arduino IDE. Но я не буду рассказывать, как это сделать, вот такой уж я вредный. Вместо этого я расскажу, как сделать все в ненавистной для многих IDE Visual Studio Code и плагине к ней PlatformIO. А перенести в Arduino IDE вы и без меня смогёте.</sarcazm>

Ну а если серьёзно – то инструкций как создать проект в Arduino IDE в интернете – пруд пруди. Не вижу смысла делать еще одну.

Итак, для начала нам понадобится собственно статья разработки. Как установить эту связку я уже рассказывал на канале и на сайте, повторяться не стоит. Стоит только отметить, что на этапе 8. Устанавливаем платформы… нужно поставить Espressif 8266:

Для этого:

  1. Нажмите “домик” PIO Home на нижней панели VS Code или значок PlatformIO на левой боковой панели, а затем выберите в списке Open.
  2. Перейдите на вкладку “Platforms
  3. В строке поиска введите 8266 и нажмите Enter
  4. Поиск выдаст единственный результат – Espressif 8266, нажмите кнопку Install (у меня это уже Uninstall).
  5. Подождите пару минут…

Создаем проект

Вновь возвращаемся на страницу PIO Home (“домик” PIO Home на нижней панели VS Code или значок PlatformIO на левой боковой панели, а затем выберите в списке Open). Но на этот раз нажмите New Project:

Выбираем название проекта (Name), плату (Board) и Framework Arduino, и нажимаем Finish:

Затем придется немного подождать, пока PlatformIO создаст необходимые файлы и папки…


 

Каталоги проекта

PlatformIO создаст в выбранном каталоге несколько подкаталогов (подпапок)

  • .pio\ – папка для “служебного пользования” PlatformIO, здесь будут создаваться файлы, здесь можно будет найти скомпилированный бинарник для загрузки в устройство “вручную”.
  • .vscode\ – ещё одна папка для “служебного пользования” PlatformIO, здесь хранятся настройки VSCode. Здесь есть что можно поправить, в некоторых случаях.
  • include\ – этот каталог предназначен для заголовочных файлов проекта. Надеюсь, вы знаете, что это такое ?.
  • lib\ – сюда можно поместить локальные библиотеки проекта. “Локальные” в данном случае я понимаю как использующиеся только в данном проекте, но не в других. Сюда можно поместить части проекта, отвечающие за ту или иную функциональность проекта. Я, например, помещаю сюда файлы “прикладных” задач проекта, то есть конкретно ту автоматизацию, которую и будет выполнять данное устройство. Если у Вас есть библиотеки, которые используются сразу в нескольких ваших проектах, их лучше поместить в каталог “вне” каталога проекта для совместного использования. Я знаю, есть любители кидать в каждый проектов свои “локальные” копии общих библиотек, но я категорически не поддерживаю подобную практику, так как это приводит к огромным проблемам при дальнейшем обновлении кода.
  • src\ – здесь, собственно, и находится исходники вашего проекта. У меня, обычно, здесь только файл main.c,которые только запускает все нужные задачи и сервисы. Но ничто не мешает накидать сюда прикладных файлов, игнорируя каталог lib (см. выше), но тогда заголовочные файлы прикладных задач вам придется положить в папку include.
  • test\ – здесь будут ваши автоматизированные тесты вашего же кода, когда вы их создадите.

Кроме этого, в каталоге проекта появится и несколько файлов:

  • .gitignore – файл, в котором вы можете указать, какие из файлов не нужно загружать в GitHub.
  • platformio.ini – файл настроек проекта PlatformIO. Именно по этому файлу VSCode “понимает”, что это проект PlatformIO, а не какой-либо ещё. Этот файл будет открыт по умолчанию, давайте его сразу и рассмотрим.

Настройка проекта

platformio.ini – основной файл параметров проекта на PlatformIO в стандартном формате, придуманном ещё во времена Windows 3.1. В нем хранятся все данные о проекте – платы, порты, скорости, фильтры, скрипты и много чего ещё. Таким образом, открывая очередной проект, вам не потребуется заново выбирать плату и её параметры, как это делается в Arduino IDE.

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

Совет: Если нажать на ссылку https://docs.platformio.org/page/projectconf.html в комментариях к файлу, то вы попадете в справочную систему PlatformIO, где вы сможете узнать обо всех доступных опциях. Здесь я упомяну только некоторые из них, которыми я чаще всего пользуюсь.

Единственная пока секция [env:nodemcuv2] задает параметры компиляции проекта:

  • platform = espressif8266 указывает, что для компиляции проекта будем использовать платформу espressif8266
  • board = nodemcuv2 определяет, что проект будет собран для платы Node MCU v2 (ESP-12F)
  • framework = arduino ну и собственно здесь мы должны указать, какой фреймворк будем использовать

Файл platformio.ini позволяет указать сразу несколько таких секций. Например, вы можете собирать одновременно один и тот же проект сразу для нескольких разных плат одновременно. Примеры смотрите в справочной системе https://docs.platformio.org/page/projectconf.html

Кроме секции [env:nodemcuv2] вы можете создать отдельную секцию [env] (то есть без указания целевой платформы) – в этом случае параметры этой секции будут считаться “общими” для всех указанных целей.

Скорость COM-порта

Что здесь стоит сразу же добавить? Во первых, нужно указать скорость обмена данными через USB / COM-порт в двух режимах: режиме монитора и режиме загрузки прошивки в плату. Номер COM-порта можно не указывать – если вы используете только одно подключение (одну плату), то автовыбор порта делает свою работу замечательно.

По умолчанию для большинства проектов Arduino используется скорость 9600 бод, поэтому в опции monitor_speed ставим цифру 9600. А вот для загрузки прошивки upload_speed можно выбрать скорость и повыше (чтобы снизить время прошивки); ESP8266 прошиваются нормально на скорости 921600 бод.

Фильтры COM-монитора.

Следующее, что можно поправить – настроить фильтры для монитора COM-порта. Доступны такие фильтры (подробнее смотри здесь):

  • default – удалить типичные коды управления терминалом из ввода
  • colorize – применение разных цветов для разного типа сообщений (хм, на самом деле это не работает)
  • debug – выводить и отправленное и полученное
  • direct – пересылать все данные без обработок, нужен для вывода цветных отладочных логов (ошибки – красным, предупреждения – желтым и т.д.)
  • hexlify – печать данных в шестнадцатеричном представлении для каждого символа
  • log2file – записывать данные в файл «platformio-device-monitor-%date%.log», расположенный в текущем рабочем каталоге
  • nocontrol – удалить все контрольные коды, в т.ч. CR+LF
  • printable – показать десятичный код для всех символов, отличных от ASCII, и заменить большинство управляющих кодов
  • time – добавить временную метку с миллисекундами для каждой новой строки (но это будет временная метка компьютера, а не самого устройства)
  • send_on_enter – отправить текст на устройство на ENTER
  • esp8266_exception_decoder – декодер исключений Espressif 8266, удобно, но уж очень медленно, я не использую
  • esp32_exception_decoder – декодер исключений то же самое, но для Espressif 32

Желаемые фильтры можно перечислить либо через запятую, либо в каждой строке, как вам удобнее (мне – в каждой строке, так как в таким случае их очень удобно “комментарить”). Сделать это можно в секции [env:nodemcuv2] для конкретного устройства, либо в секции [env] сразу для всех устройств, сколько бы секций вы не создали.

Я пользуюсь в основном direct и log2file – иногда очень записывать протоколы вывода в файл для последующего анализа.

На этом пока оставим этот файл, но вкладку редактора закрывать ещё рано, он нам ещё понадобится. Не забудьте сохранить изменения с помощью Ctrl+S.


Редактор кода

Ок, теперь можно начинать программировать. Для начала создадим подключение к точке доступа WiFi. Откройте главный файл проекта – main.cpp, он находится в папке src. Как видите, он ничем не отличается от типичного шаблона скетча в Arduino IDE:

Первое, что потребуется сделать – настроить COM-порт для вывода отладочных сообщений:

Цифры должны соответствовать тому, что вы указали ранее в platformio.ini. Пустой Serial.println(); нужен просто для того, чтобы перевести указатель на новую строку, так как при старте MCU в порт часто попадает мусор.


 

Подключение к WiFi точке доступа

Перед созданием кода, который будет выполнять подключение к точке доступа, необходимо выполнить некоторые подготовительные операции:

  • Добавьте #include <ESP8266WiFi.h> в начало файла, чтобы подключить стандартную библиотеку ESP8266WiFi к проекту.
  • Добавьте пару строк const char*, в которых укажите имя вашей сети (SSID) и пароль подключения к ней (да, да, в открытом виде, но вы же собираетесь публиковать это где попало)

Я настоятельно рекомендую вам расставлять комментарии где это только возможно. Это сильно облегчит вам жизнь, когда вам срочно понадобиться доработать проект спустя пару лет его эксплуатации

Способы подключения к WiFi

Я могу предложить несколько вариантов логики подключения к WiFi. Разумеется, предложенные мной в статье способы не исчерпывают всех возможных вариантов, вы можете найти другие или придумать свои. Рассматривайте это просто как измышления на тему…

 

1. Можно запихнуть весь код подключения к WiFi в функцию setup(). Так часто делается в примерах для esp8266:

На первый взгляд, всё это очень просто и хорошо.

Можно заподозрить, что подключение к WiFi осуществляется только один раз. То есть при любом отключении или перезагрузке роутера устройство не сможет вновь подключиться к сети. На самом деле это не так. Дело в том, что класс WiFi (с которым мы работаем) имеет “свойство” AutoReconnect (на самом деле физически его нет, но так проще для понимания) и оно по умолчанию включено. Управлять этим “свойством” можно с помощью методов getAutoReconnect() и setAutoReconnect(bool). То есть наше устройство должно автоматически попытаться восстановить подключение при его потере.

Но как мы узнаем о том, что подключение было потеряно и вновь установлено? Ведь после переподключения нам потребуется вновь подключиться к MQTT брокеру и т.д. и т.п. Для того, что бы отловить изменения состояния WiFi подключения можно предложить как минимум два способа:

  • правильный способ – создать и зарегистрировать функции обратного вызова (callbacks) для событий onStationModeDisconnected (соединение потеряно) и onStationModeGotIP (получен IP-адрес, это финальная стадия подключения). Но функции обратного вызова вызывают затруднение у новичков, поэтому мы пока не будем рассматривать этот способ (если будет необходимо – пишите в комментариях)
  • простой способ – просто проверять состояние в каждой итерации главного цикла, чтобы предпринять необходимые действия. Например так:

И это действительно работает, хотя и не очень надёжно. Почему-то этот алгоритм “пропускает” некоторые отключения и восстановления от WiFi (и дело тут не в большой задержке). Зато это очень просто.

Но если вдуматься – такой подход имеет очень серьезный изъян. Если на момент запуска подключение невозможно установить по любой причине, то устройство никогда не выйдет на главный цикл и “зависнет” на ожидании подключения. И даже принудительный выход из цикла ожидания ничего толкового не дает – ведь подключение так и не было установлено. Лично для меня это категорически неприемлемо. И я никому не посоветую так делать. Так что же делать?

 

2. Перенесем подключение к сети в цикл loop(). Для этого просто каждый раз проверяем состояние подключения, и если оно отсутствует, “вручную” попытаемся восстановить его.

В этом случае в каждом цикле loop() мы будем точно знать, есть ли подключение к сети, и, в случае необходимости, сможем легко предпринять дополнительные действия без callback-ов, например заново переподключиться к MQTT-брокеру.

Уже гораздо лучше. Но здесь есть большие грабли с очень крепкой дубовой ручкой, которой обязательно прилетит вам в лоб в самый неожиданный момент. Найдете сами? Ладно, даю подсказку:

Догадались? Нет? Дело в том, что если нет подключения к WiFi, ваша программа никогда не выйдет из цикла, обведенного красным. А, следовательно, программа не выполнит что-то ещё, пока нет подключения к сети. Другими словами, температура не будет измерена, реле не будет выключено и т.д. Допустим в процессе работы вы включили насос, а затем… сгорел роутер, досадно, обидно, но… насос останется работать, пока вы не выключите устройство вместе с насосом.

Это очень опасный момент, когда вы доверяете своему устройству управление потенциально опасными устройствами – котлом, поливом и т.д.

Что же делать, как же быть? Есть два пути решения этой проблемы.

 

3. Принудительно ограничить время работы цикла ожидания подключения.

Всё тоже самое, но цикл ожидания немного изменим.

Вуаля, теперь ожидание не будет превышать 30 секунд. А за 30 секунд, надеюсь, ничего страшного не произойдет. Хотя это и “костыль”, но задача решена.

 

4. Переделать всю работу с WiFi на систему по событиям. Самый “правильный” вариант. В этом случае вам потребуется создать кучку функций обратного вызова (callbacks), которые будут асинхронно вызываться в нужные вам моменты времени: при установке соединения и при потере оного. И вы сможете сделать всё, что требуется в нужный момент времени без каких-либо циклов ожидания. В своих проектах я использую эту технику. Но в виду некоторой сложности данного подхода для начинающих (а эта графомания всё-таки для начинающих ардуинщиков), пока не будем рассматривать этот вариант. А если у вас есть некоторый опыт, вы сможете соорудить такое и без моей помощи.

 

Функция проверки подключения к WiFi

Теперь уже таки можно написать достаточно простую функцию подключения к AP. Лично я избегаю включать “чистый” код в setup() и loop(), а выношу все специфичные операции в отдельные функции, так зачастую удобнее. Например так может выглядеть функция, которая проверяет и восстанавливает подключение:

Тогда основные функции setup() и loop() будут выглядеть так:

Компилируем build, загружаем в плату upload. Плата должна корректно отрабатывать все отключения от WiFi сети.

В первый раз подключение успешно установлено за ~4сек, затем была перезагрузка роутера, вначале за 30 секунд установить соединение не удалось, был выход из цикла ожидания, затем удалось


 

MQTT протокол

Для начала давайте чуть-чуть об теории MQTT протокола. MQTT — это протокол обмена сообщениями по шаблону издатель-подписчик (pub/sub). Первоначальную версию в 1999 году опубликовали Энди Стэнфорд-Кларк из IBM и Арлен Ниппер из Cirrus Link. Они рассматривали MQTT как способ поддержания связи между машинами в сетях с ограниченной пропускной способностью или непредсказуемой связью.

Система связи, построенная на MQTT, состоит из клиентов-издателей publisher, сервера-брокера broker и одного или нескольких клиентов-подписчиков subsriber. В обычных условиях клиенты не могут общаться напрямую друг с другом, и весь обмен данными происходит только через какого-либо брокера. Издатель и подписчик ничего не знают друг о друге, но должны знать о том, по какому адресу / каналу topic передавать или ждать данные. Одно и то же устройство может быть одновременно и издателем и подписчиком (но на разные топики). Это сильно облегчает задачу передачи данных из-за NAT-а (то есть из обычных локальных сетей).

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

Кратенько рассмотрим термины, которые нам просто необходимо знать для работы с MQTT протоколом.

Broker

Брокер — это центральный узел MQTT, обеспечивающий взаимодействие клиентов. Обмен данными между клиентами происходит только через брокера. В его задачи входит получение данных от клиентов-издателей, обработка и сохранение, доставка полученных данных подписчикам, а так же контроль за доставкой сообщений в случае необходимости. Брокеров может быть несколько, в том числе связанных между собой так называемыми “мостами”.

Message

Сообщения (message) содержат в себе информацию, которую один участник сети на базе протокола MQTT (издатель) хочет передать другим (своим подписчикам). В качестве сообщения может быть всё, что угодно – текст, значения температуры, состояние датчиков и т.д. и т.п. И даже двоичные данные.

Publish

Это процесс передачи сообщения брокеру. То есть простым языком, я подошел к брокеру и сказал “кнопка нажата“. Брокер теоретически должен услышать это сообщение, записать (при необходимости) и передать дальше подписчикам. Почему теоретически? Потому что есть особенности протокола (QoS), которые мы разберем чуть ниже. Пока берем за данность, что я сказал что-то брокеру используя механизм publish и он это услышал и обработал как положено.

Subscribe

Сообщение было отправлено брокеру, но как его получить клиенту? Правильно – подписаться. Тогда сервер передаст вам все поступающие сообщения на интересующую вас тему. Чтобы определить на какую тему мы хотим получать эти сообщения, используется механизм Topic

Topic

Топик (его ещё иногда называют темой или каналом) – это своего рода адрес для передачи тех или иных данных. Для того, чтобы клиент-подписчик смог получить опубликованные данные, он должен заранее знать, в каком топике (по сути по какому адресу) их будет отправлять издатель. По аналогии с обычными газетами и почтой:

Издатель печатает несколько газет (топиков) – “Аргументы и факты“, “Ведомости” и “Гудок“. Подписчик может подписаться на один из указанных топиков или несколько. А может подписаться на журнал “Новости адруинщика” (почему нет?). Почтальон (брокер) принесет подписчику только те газеты (сообщения), которые напечатал (отправил) издатель, и на которые подписался подписчик. То есть вы должны заранее знать, на что подписываться.

Топик может быть простым (например temperature) и состоящим из нескольких отдельных частей, разделенных слешем “/” (например home/kitchen/temperature). Для чего нужны эти “/“? А для того, дабы выстроить определенную иерархию данных. По аналогии с приведенным выше примером с почтой это могут быть топики “Газеты/Аргументы и факты” и “Журналы/Мурзилка“. Чем то это очень похоже на структуру каталогов и файлов на дисках вашего контупера.

Во-первых строках письма это даёт более простое понимание множества данных:

Но самое главное – это сильно облегчает подписку на данные путем использования подстановочных знаков wildcards. MQTT протокол предусматривает два типа wildcards при подписках на топики:

  • # (твой дом тюрьма решетка) позволяет элегантно подписаться на все субтопики одном махом
  • + (плюсик) позволяет подписаться на все топики одного уровня

Ну например: у нас есть в кухне и в спальне по два выключателя и один в гараже. Для этого мы формируем на выключателях в топики в виде:

В гостиной:

home/kitchen/switch1
home/kitchen/switch2

В спальне:

home/bedroom/switch1
home/bedroom/switch2

В гараже:

garage/switch1

Допустим мы хотим управлять всеми выключателями в доме, но нам абсолютно не интересен гараж. В обычном режиме нам пришлось бы подписаться на все топики отдельно:

mqttClient.subscribe("home/kitchen/switch1");
mqttClient.subscribe("home/kitchen/switch2");
mqttClient.subscribe("home/bedroom/switch1");
mqttClient.subscribe("home/bedroom/switch2");

Геморой? Не то слово! Гораздо проще и надежнее сделать это так:

mqttClient.subscribe("home/#");

Бонусом мы автоматически получаем подписку на все еще заранее неизвестные топики в доме (впрочем иногда это лишнее).

Хорошо, а если нас интересует только выключатель 1, но не важно в какой комнате. Тогда напишем так:

mqttClient.subscribe("home/+/switch1");

Но чтобы пользоваться этими возможностями, нужно заранее продумывать структуру топиков для своих устройств, а не сочинять их с бухты-барахты.

<вредный совет>Таки если вы желаете сделать себе нервы и выесть моск, создавайте топики максимально простыми и случайным образом. Это очень поможет вам в дальнейшем. Тормоза придумали трусы, а иерархию – ботаники.</вредный совет>

Служебные сообщения

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

  • Birth Message – которое сообщает миру что “я родился и живой”
  • Last Will and Testament (LWT) которое сообщает что после этого сообщения считать меня мертвым
  • Keep Alive – которые сообщают брокеру что “я все еще живой” и стандартно посылаются каждые 60 секунд. Если брокер не получил это сообщение от клиента, то он принудительно пингует его чтобы выяснить жив ли тот, и если выясняется что он неживой, то брокер публикует за клиента LWT сообщение, чтобы все узнали что тот скончался.

Соответственно получение брокером Birth Message от устройства, переводит устройство в режим ONLINE, а после того как брокер получает от устройства LWT сообщение, либо когда сам принимает решение что тот скончался (проверив устройство на доступность), то переводит статус устройства в режим OFFLINE.

На практике нам понадобится только LWT, все остальные служебные сообщения отправляются библиотекой самостоятельно и без нашего ведома. Как создать LWT и как допилить его под удобную индикацию статуса устройства будет рассказано чуть ниже.

Retain или Retained

Протокол MQTT никоим образом не предназначен для накопления данных на сервере с последующим отображением в виде графиков. Максимум что может храниться на сервере – последнее опубликованное издателем сообщение, если оно было отправлено с флагом retain или retained (в разных версиях может называться по разному, но суть одна и та же). Это позволяет клиенту-подписчику при подключении к брокеру сразу же получить последние интересующие его данные, а не ждать когда их отправит издатель в очередной раз. Однако использование этого флага требует известной аккуратности, неверное его использование может привести к невразумительным проблемам.

QoS

QoS расшифровывается как Quality Of Service, то есть качество предоставляемой услуги. Для MQTT этот показатель отвечает за вероятность прохождения пакета между двумя точками сети. Флаг QoS может принимать следующие значения, основанные на надежности передачи сообщения:

  • 0 = не более одного раза: отправитель пересылает сообщение получателю и забывает об этом. Сообщения могут быть потеряны в канале связи или продублированы (если отправитель посчитал что пакет потерян и отправил его повторно, но первый пакет таки дошёл до получателя).
  • 1 = по крайней мере один раз: получатель подтверждает доставку сообщения. Если подтверждение не было получено, отправитель должен отправить его еще раз и так до получения подтверждения о получении. Сообщения могут дублироваться, но доставка гарантирована
  • 2 = ровно один раз: сервер обеспечивает гарантированную доставку. Сообщения поступают точно один раз без потери или дублирования. Самый медленный и машинотрудозатратный вариант, так как отправитель и получатель дополнительно обмениваются подтверждениями.

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


 

Подключаем сторонние библиотеки

Для работы с MQTT протоколом нам понадобится сторонняя библиотека, например PubSubClient. Существует две популярные версии данной библиотеки (хотя может быть и больше):

Чем они отличаются? В основной версии значение QoS можно указать только при подписке. А при публикации – нет! Я считаю, что это не есть хорошо (к слову, разработчики MQTT клиента для ESP-IDF считают так же, поскольку в ESP32 реализован “по второму типу”).

Лично я предпочитаю пользоваться версией от Imroy. Но в данном примере я буду использовать основную ветку. Просто потому что во 99.9999999% примеров в сети используется именно она.

Если вы работали с Arduino IDE, то, наверное, знаете, что там библиотеки можно достаточно легко и просто скачать и установить с помощью встроенного менеджера библиотек. В PlatformIO всё на первый взгляд сложнее, но на самом деле всё не так страшно, да и возможностей гораздо больше. Я настоятельно рекомендую ознакомиться с документацией по менеджеру библиотек, но для начала достаточно усвоить самые простые способы. Итак, если очень кратенько, существует несколько способов подключить сторонние библиотеки:

  • Локальные библиотеки проекта. Для этого потребуется скачать архив библиотеки с GitHub и распаковать его содержимое в каталог \lib вашего проекта. В этом случае PIO автоматически “подхватит” эти библиотеки и ничего нигде настраивать не нужно. Но я настоятельно не рекомендую так делать. Почему? Потому что вам придется хранить отдельную копию библиотек для каждого из своих проектов. Это занимает дополнительное место на дисках, но самое главное – если необходимо обновить библиотеки, вам придется сделать это в каждом из своих проектах. Ручками. Как всегда – за свою лень в начале мы заплатим многократным трудом впоследствии, чудес не бывает.
  • Общие локальные библиотеки. Этот способ очень удобно использовать, когда одни и те же библиотеки используется сразу в нескольких проектах. Допустим, все общие библиотеки мы будем складывать в папку C:\PlatformIO\libs. Создаем такой каталог, вручную скачиваем архив с GitHub, и вручную распаковываем содержимое в только что созданный каталог. Всё как и в предыдущем случае, только целевая папка изменилась. Теперь нам нужно “подключить” каталог C:\PlatformIO\libs к нашему проекту через параметр lib_extra_dirs. Для этого потребуется указать только каталог lib_extra_dirs = C:\PlatformIO\libs, все вложенные библиотеки не более одного уровня вложенности “подхватятся” автоматически. Открываем файл platformio.ini (помните, я вас предупреждал не закрывать его) и добавляем такие строчки:

Этот способ идеален для “своих” общих библиотек, но для “чужих” лучше использовать следующий

  • Публичные библиотеки. Я настолько ленив, что мне даже лень скачивать и распаковывать библиотеки вручную. Да ещё потом и обновлять их придется в случае чего. Данунафиг. PlatformIO предоставляет очень удобный способ подключения публичных библиотек напрямую с GitHub или из каталога библиотек PlatfortmIO. Просто укажите прямые ссылки на них в параметре lib_deps – и вам не придется следить за скачиванием и обновлением этих библиотек – PlatformIO всё возьмет на себя:

Вот так гораздо лучше

Просто? Очень! Нужно только знать, где найти эту самую нужную библиотеку. На гитхабе конечно! Ну да гуголь вам в помощь.

Теперь можно добавить библиотеку в исходный код нашего проекта: #include <PubSubClient.h> и начинать писать код подключения к брокеру.


Подключение к MQTT-серверу без шифрования

Для начала давайте попробуем подключиться к брокеру в открытом виде, без TLS-шифрования. Я использую такой вариант только для подключения к своему локальному брокеру, который находится в моей локальной сети, относительно защищенной от интернета NAT-ом. Но для примера вполне сойдет.

Для подключения необходимо предварительно запастить следующими данными:

  • адрес сервера в сети
  • порт сервера (обычно 1883, но вполне может быть и другой)
  • имя пользователя
  • пароль пользователя

Все это вы должны заранее узнать на том сервере, к которому вы собираетесь подключиться. Я достаточно давно уже пользуюсь wqtt.ru (он платный, но стоит относительно недорого и без существенных ограничений на текущий момент !!!не реклама!!!). В данном примере нам понадобятся:

Порт берем пока самый простой, первый по списку. Не перепутайте, иначе ничего не выйдет. Заведем их в программу в виде констант:

Не забудьте подставить свои значения

Далее, нам потребуется завести две глобальные переменные:

Первая – это экземпляр WiFi клиента, который требуется для работы MQTT-клиента. Вторая – MQTT-клиент собственной персоной.

Далее пишем такую функцию:

По аналогии с предыдущим случаем, здесь мы проверяем, есть ли подключение к брокеру; и если нет – указываем параметры сервера и подключаемся. Всё предельно просто.

Во многих примерах ClientID генерируется “на лету” из части MAC-адреса устройства, но я не вижу необходимости тратить на это свободную память кучи. Ибо нефиг. Только не забывайте менять его от проекта к проекту (он должен быть уникальным).

Затем дополняем основной цикл loop() следующими строками:

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

В результате, если всё сделано правильно, мы должны получить примерно следующее:

Прелэстно, прелэстно… К MQTT брокеру мы подключились, но пока мы не научили наше устройство что-либо отправлять и получать с него.


Добавляем LWT и статус устройства

Добавим топик, который позволит нам отслеживать состояние устройства – в сети оно или нет. Для этого воспользуемся функционалом, предоставляемым нам MQTT протоколом, который называется LWT (Last Will and Testament, «последняя воля и завещание») для уведомления заинтересованных сторон об отключении клиента.

  • Пусть для примера топик статуса будет “demo/status” (хотя на практике тут должно бы быть что-то вроде “home/boiler/status” или “garage/status“).
  • В рабочем состоянии в этом топике должно быть “online“, когда устройство выключено или связи нет, то “offline” (хотя вполне можно использовать и просто “1” и “0“).
  • Для такого типа информации оптимальнее всего использовать Qos = 1 (так как повторное получение никому не помешает), а вот retain лучше поставить 1 или true.

Определим константы:

Совет: Я всегда стараюсь использовать константы, а не прописываю постоянные значения прямо “в коде”. Ибо прямое написание ведет к многократным потерям в производительности труда при модификациях кода и многочисленным ошибкам. Надо ли объяснять почему так? Хотя для компилятора, конечно же, всё равно. Это очень плохая практика, для меня это практически табу. Если вы будете так делать, то вы сами себе злобный Буратино.

Осталось немного модифицировать код подключения к брокеру:

То есть сразу же при подключении мы отправляем на сервер параметры LWT сообщения. Но сразу после подключения в этом топике будет пусто, так как “offline” там появится только после того, как устройство будет отключено. Дабы исправить это недоразумение, мы сразу же публикуем в этот же самый топик состояние “online“. Вот и всё, что требовалось.


 

Добавим SSL / TLS

Подключаться к публичному облачному брокеру в открытом виде – плохой вариант. В этом случае все ваши данные, в том числе логины и пароли, передаются в открытом виде. И более-менее продвинутый кулхацкер сможет перехватить их. Способов изобретено множество – от аппаратных до программных. Например можно создать “подставной” промежуточный сервер брокер или внедрить в вашу сеть сниффер (вопрос только зачем ему это? ну так зачем у нас молодняк дорожные знаки и остановки курочит?). Не будем облегчать кулхацкерам задачу, ну и заодно и себе тоже.

К чему это я? Давайте модифицируем подключение к брокеру с использованием SSL или TLS шифрования. Это дает не только полное шифрование всех передаваемых данных, но и гарантирует, что вы подключаетесь к настоящему серверу, а не подставному, созданному специально для перехвата (хотя вряд ли кто-то когда либо будет создавать сервер-перехватчик специально для вас, если только вы не подпольный миллионер на пенсии).

Я не буду углубляться в теорию TLS-соединений. В сети найдется немало материалов на эту тему, а я не являюсь специалистом в криптографии. Одно я знаю наверняка – для того чтобы установить зашифрованное TLS-соединение, вашему устройству как минимум понадобится сертификат сервера (впрочем не самого сервера, но об этом ниже). Сертификат сервера несет в себе несколько функций:

  • подтверждает подлинность сервера, которому он был выдан
  • содержит в себе ключи шифрования сервера, необходимые для установки зашифрованного канала связи

Отсюда вывод, что без сертификата сервера шифрованное соединение установить не получится. В принципе, сертификат сервера скачает библиотека TLS-соединения самостоятельно, при обращении к серверу. Нам требуется только его проверить – но как это сделать?

Все сертификаты сайтов выданы и подписаны какими-либо центрами сертификации, которые так же имеют свой сертификат. Центр сертификации не обязательно должен быть один, их может быть несколько в цепочке, подписанных один за другим. Например: сертификат для сайта wqtt.ru был выдан ЦС R3, а тому, в свою очередь, выдал сертификат ISRG Root X1.

Первый сертификат в списке называется “корневым”. Если корневому сертификату мы доверяем целиком и полностью, то остальные “вложенные” сертификаты мы сможем проверить “по цепочке”. Поэтому для проверки сертификата любого сайта мы должны иметь “всего лишь” список доверенных корневых сертификатов, которым мы доверяем. Это намного меньше, чем хранить сертификаты всех сайтов, но всё равно это внушительный общем информации, который просто не влезет в память микроконтроллера (впрочем в ESP-IDF имеется возможность подключить к проекту такой список, который называется tls bundle, но речь сейчас не о нём). Поэтому на Arduino обычно ограничиваются подключением с проекту одного или нескольких корневых сертификатов, в зависимости от того, к каким сайтам требуется доступ. Например для подключения к брокеру wqtt.ru нам потребуется подключить сертификат ISRG Root X1.

Но у всех сертификатов (и корневых и не только) имеется встроенная головная боль для ардуино-программистов, и называется она “срок действия сертификата”. После заранее определенного в сертификате срока он считается “испорченным”. Кроме того, сертификат может быть отозван досрочно, если есть подозрения на его компрометацию. И тогда таким “порченным” корневым сертификатом ничего проверить уже будет нельзя. И нам потребуется оперативно заменить его в прошивке. Поэтому для критичных устройств необходимо предусмотреть вариант резервного варианта подключения в открытом виде, дабы не потерять контроль над устройством. Обычно для корневых доверенных сертификатов срок действия достаточно велик, чтобы не очень беспокоиться по этому поводу, но событие в календарик добавить всё-таки стоит.

Как получить файл корневого сертификата

Итак, нам нужен файл корневого сертификата. Самое простое в windows – использовать обычный браузер, например хром. Если у вас установлен антивирус, придется его отключить на время, так как антивирусные программы подменяют сертификаты ЦС своими собственными и это ни к чему хорошему не приведет. Затем откройте нужный вам сайт и найдите замочек в адресной строке, затем кликните на этот замочек:

Затем кликните на строку “Безопасное подключение”, а затем на “Действительный сертификат”:

Откроется окошечко просмотра сертификата, где мы должны перейти на вкладку “Подробнее”, выделить в иерархии верхний сертификат и нажать “Экспорт”:

Затем просто указываем, где бы мы хотели сохранить файл и нажимаем ОК. В итоге у вас в выбранной папке должен нарисоваться новый файлик с вот таким примерно содержанием:

Это и есть то, что нам нужно!

Добавим к нашему проекту константу static const char ISRG_Root_x1[] PROGMEM = R”EOF()EOF”; а затем скопируйте содержимое сертификата и вставьте между круглых скобок. У вас должно получиться нечто вроде этого:

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

Но и это ещё не всё! Вспомните про срок действия сертификата – нам нужно время! Причем в прямом смысле слова. Если к вашему устройству подключены аппаратные часы реального времени, то можно получить время с них. Иначе нам придется получить время с SNTP-сервера. Как это сделать, я уже писал на этом сайте ранее, повторяться не буду. Здесь приведу лишь готовую функцию получения актуального времени, я добавил этот блок в wifiConnected() сразу после подключения:

Добавим новую глобальную переменную для списка доверенных сертификатов и немного подправим существующую:

После этого уже можно смело добавить корневой сертификат в список доверенных:

Вот теперь всё готово к TLS-соединению!

Необходимо только не забыть заменить порт подключения в параметрах сервера, иначе при подключении будет возвращена ошибка:

Ну вот и всё, этого достаточно, можно пробовать:

Как видите, это совсем не сложно. А страху-то шо было! Крип-то-гра-фия! О как!


 

Дистанционное управление реле

У нас всё готово для решения практических задач. Вначале давайте сделаем так называемую “умную розетку” или “умную лампочку“, а по сути “тупой пульт дистанционного управления с телефона“, гы… Хотя что нам одна лампочка или розетка. Давайте сразу три или четыре. Пощёлкаем орешки релюшками “по полной программе”.

Перво-наперво нужно определить выводы, к которым подключены наши реле, например так:

И не забыть настроить эти GPIO на режим “на выход”:

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

Я не зря добавил в конце каждого топика /control – не спешите обрезать, он нам ещё пригодится. И определим текстовые команды для этих топиков. Я обозначил здесь аж сразу два варианта – можно будет отправить в топик “1” или “on” и результат будет одинаков.

Теперь добавляем подписку на эти топики. Проще всего сделать это сразу после подключения к брокеру:

Подписаться то мы подписались, а как мы узнаем, что с сервера пришла команда на включение или выключение реле? Правильно – нужно таки создать функцию-обработчик, используя прототип std::function<void(char*, uint8_t*, unsigned int)>

Я написал такую функцию:

Вначале нам необходимо выделить полезную информацию из входящего потока байт. Для пущей надежности я добавил функции обрезки “лишних” пробелов из текстовых строк и приведение их к нижнему регистру.

Затем можно сравнить топик и данные с заранее определенными и решить как поступить в данном случае:

Не забудьте зарегистрировать этот обработчик в любом удобном месте, например так:

Прошиваем контроллер, проверяем работу.


Добавим обратную связь на управление реле

Всё это работает, но не забываем, что отправленное сообщение управления может где-то заблудиться на длинной дороге в проводках и кабелёчках. Например устройство может быть просто выключено, или что то ещё…

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

Добавим топики для публикации фактического состояния реле, они будут немного отличаться от топиков управления, например так:

Затем добавим глобальные переменные для хранения текущих состояний реле и для хранения полученных “команд”:

После этого модифицируем наш обработчик следующим образом:

Отлично. Осталось написать код, который будет сравнивать “старое” и “новое” состояние реле и в случае необходимости щёлкать ими:

Если значение relayCommandX и relayStatusX отличаются друг от друга, то переключаем выход, к которому подключено реле и публикуем новое состояние реле в “ответный” топик.

Но это ещё не всё. Хорошо бы при подключении к MQTT серверу опубликовать текущие состояния реле, так как во время временного отключения от сервера состояния реле могло и измениться (например если у вас будет какой-либо автоматический сценарий). Это просто:

Ну и не забудьте добавить эту функцию в основной цикл:

Ещё раз всё компилируем:

А на брокере в это время:

С чем вас и поздравляю! Вы только что сделали умную розетку, да ещё и с обратной связью. Думаю, что теперь вы сможете придумать, как управлять реле не по команде, а в автоматическом режиме.


 

Добавим публикацию состояния цифрового входа

А что, если нам нужно периодически считывать состояние входа и публиковать его состояние при изменении? Давайте попробуем…

Конечно, оптимальнее было бы использовать прерывания, но не будем пока о них. Сделаем “по простому”, выжмем из процессора все соки…

Для этого нам понадобится ещё одна переменная inputStatus1 – для хранения последнего считанного значения. Иначе нам пришлось бы публиковать состояние входа в каждом цикле, а это лишняя и абсолютно бесполезная нагрузка на процессор, сеть и брокер:

Я специально оставил её с номером, дабы было понятно, что таких входов вы можете сделать столько, сколько позволят свободные выводы ESP.

Ну и константы, топики и прочее, куда же без них:

Сама функция чтения не сложнее предыдущих:

Осталось вызывать её из главного цикла:

И таки да, не забудьте настроить используемый вывод:

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

и на брокере:


Измеряем температуру и влажность в помещении

Теперь давайте попробуем подключить какой-нибудь датчик и прочитать с него данные. Для примера я возьму вот такой AM2302, так как на нем уже есть резистор подтяжки шины single bus:

Его я подключил к выводу D1 или GPIO5 (кстати, отличный справочник по выводам ESP8266).

Для работы с данным сенсоров так же потребуется подключить сторонние библиотеки, например от Adafruit:

#include <Adafruit_Sensor.h>
#include <DHT.h>

Но перед этим добавим ссылки на них в файл platformio.ini

Ну а дальше просто копипастим код из примера к драйверу DHT22. Ну и добавляем код для публикации на брокере:

  • Создадим глобальную переменную для DHT22:

  • Определим необходимые топики:

  • Проинициализируем сенсор в функции setup():

  • И можно, наконец, прочитать и опубликовать данные:

  • Но вот вызывать эту функцию каждую итерацию цикла loop() не следует, поэтому добавим простейший псевдотаймер на millis():

Если вы все сделали правильно, то должны получить примерно такой результат:

А на сервере в это же самое время можно увидеть следующее:


Ссылки

Как всегда, пример можно скачать в готовом виде с GitHub: github.com/kotyara12/arduino/tree/master/arduino_eps8266_dzen

 

Если вам понравилась статья и вы желаете отблагодарить автора – просто кликните по любому рекламному объявлению.

💠 Полный архив статей вы найдете здесь


Пожалуйста, оцените статью:
[ 5 из 5, всего 3 оценок ]

16 комментариев для “Телеметрия на ESP8266 и MQTT. Пошаговое руководство по созданию DIY-проекта с удаленным управлением”

  1. Connecting to MQTT broker: failed, error code: -2!
    id клиента менял, но всё равно эта ошибка.
    в чём может быть ошибка?

    1. Добрый день!
      В чем угодно: неверный сервер, неверный порт, неверный сертификат, неверный логин, неверный пароль…

  2. Добрый день
    попробовал проєкт “arduino_eps8266_relays_no_ssl” ругается:
    ==========================================================================
    src\main.cpp: In function ‘bool wifiConnected()’:
    src\main.cpp:106:10: error: invalid use of member function ‘bool ESP8266WiFiSTAClass::setAutoConnect(bool)’ (did you forget the ‘()’ ?)
    106 | WiFi.setAutoConnect = false;
    | ~~~~~^~~~~~~~~~~~~~
    src\main.cpp:107:10: error: invalid use of member function ‘bool ESP8266WiFiSTAClass::setAutoReconnect(bool)’ (did you forget the ‘()’ ?)
    107 | WiFi.setAutoReconnect = false;
    ==========================================================================
    Заметил что в проєкте “arduino_eps8266_relays_ssl” єтих строк нету… Можете пояснить почему так?
    Спасибо!

    1. Это ошибка. Нужно писать:
      WiFi.setAutoConnect(false);
      WiFi.setAutoReconnect(false);
      Без этих строк работать тоже будет, но вы должны понимать, почему это нужно или нет.

      Я не проверял все версии. Я проверял только SSL, других я не использую. И вам не советую.

  3. Дмитрий

    Помогите! Несколько раз переустанавливал VS и PIO. В любом проекте ругается на Arduino.h :
    Обнаружены ошибки #include. Измените includePath. Волнистые линии отключены для этой единицы трансляции (C:\Users\dmser\Desktop\arduino-master\arduino-master\arduino_eps8266_relays_no_ssl\src\main.cpp).C/C++(1696)
    не удается открыть источник файл “pins_arduino.h” (dependency of “Arduino.h”)C/C++(1696)

    1. Скорее всего, соответствующая платформа не установлена. Перейдите на PIO Home и установите нужную – Espressif 32 или Espressif 8266.

  4. Дмитрий

    Установлена Espressif 8266. Даже в примере Arduino Blink Выдает ошибку: Обнаружены ошибки #include. Измените includePath. Волнистые линии отключены для этой единицы трансляции (C:\Users\dmser\Documents\PlatformIO\Projects\230708-153109-arduino-blink\src\main.cpp).
    не удается открыть источник файл “pins_arduino.h” (dependency of “Arduino.h”) и так в любом проекте.

    Пробовал устанавливать ранние версии PIO, результат тот же. За 3 дня облазил весь инет, ничего не помогает.
    Попытался поставить PIO на Atom. Atom в репозитории не находит PIO…

    1. А проект-то компилируется?
      Если это просто IntelliSence ругается, а проект компилируетя, то это ерунда. Поправьте пути к платформам в c_cpp_properties.json и будет вам щасье.
      Об этом даже вроде бы статья была соответствующая

    1. Если не компилируется, значит у вас действительно проблема. Но я не телепат и на расстоянии не могу определить, что не так.
      Кстати, а вы учли, что в статье на Дзене настройки для ESP32 и Вам нужно указывать совсем другие пути?
      Вот например в моем случае (под ваш профиль его тоже корректировать нужно)
      “includePath”: [
      “C:/PlatformIO/arduino/arduino_eps8266_dzen/include”,
      “C:/PlatformIO/arduino/arduino_eps8266_dzen/src”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/tools/sdk/include”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/cores/esp8266”,
      “C:/Users/kotyara12/.platformio/packages/toolchain-xtensa/include”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/tools/sdk/lwip2/include”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/variants/nodemcu”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/libraries/ArduinoOTA”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/libraries/DNSServer/src”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/libraries/EEPROM”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266AVRISP/src”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266HTTPClient/src”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266HTTPUpdateServer/src”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266LLMNR”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266NetBIOS”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266SSDP”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266SdFat/src”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266WebServer/src”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266WiFi/src”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266WiFiMesh/src”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266httpUpdate/src”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/libraries/ESP8266mDNS/src”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/libraries/Ethernet/src”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/libraries/GDBStub/src”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/libraries/Hash/src”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/libraries/I2S/src”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/libraries/LittleFS/src”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/libraries/Netdump/src”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/libraries/SD/src”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/libraries/SDFS/src”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/libraries/SPI”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/libraries/SPISlave/src”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/libraries/Servo/src”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/libraries/SoftwareSerial/src”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/libraries/TFT_Touch_Shield_V2”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/libraries/Ticker/src”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/libraries/Wire”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/libraries/esp8266/src”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/libraries/lwIP_PPP/src”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/libraries/lwIP_enc28j60/src”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/libraries/lwIP_w5100/src”,
      “C:/Users/kotyara12/.platformio/packages/framework-arduinoespressif8266/libraries/lwIP_w5500/src”,
      “”
      ],

  5. Дмитрий

    Pio перестраивает файл. Удалил VScode, очистил вручную комп от папок и файлов VS и PIO. Устанавливаю всё заново.
    PIO установился в C:\Users\dmser\.vscode\extensions\platformio.platformio-ide-3.2.0-win32-x64
    папки .platformio нигде нет.
    path: C:\Users\dmser\.vscode\extensions\platformio.platformio-ide-3.2.0-win32-x64\scripts
    Команда pio – Имя “pio” не распознано как имя командлета, функции, файла сценария или выполняемой программы.

    1. Поищите папку .platformio прямо в корне диска C:\ – возможно она там…

      Если pio не распознается как команда – значит путь к ней не зарегистрирован в переменных окружения
      https://kotyara12.ru/iot/pio_install/, раздел “Установка” пункт 6-7

    2. Дмитрий

      К сожалению, изменить коммент не могу, PIO встал на место после обновления, path прописал, остался трабл с этим:
      Команда pio – Имя “pio” не распознано как имя командлета, функции, файла сценария или выполняемой программы.

  6. Отлично! Повторил проект, всё работает, добавил датчики 18b20, на смартфоне отображается температура, состояние (online/offline). Хороший сайт, очень много информации и всё в одном месте, благодарность автору!

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *