Подписаться YouTube

Монитор температуры для умного дома. Часть 2 | Xiaomi Smart Home Gateway 2 | Wemos | ECP8266

Текст видео

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

Первое что мне нужно было сделать, чтобы получить информацию с датчика температуры через hub Xiaomi - это включить режим разработчика на этом hub. Про то, как это сделать я снял отдельное видео. Ссылка на него будет в описании под этим видео.

Сейчас я открыл раздел “протокол связи по локальной сети” в приложении Mi Home. Похоже, что протокол обмена данными, который используется в режиме разработчика называется green rice. В целом, он достаточно простой. Он реализован через, обычный UDP сокет который открывается на порте 9898 нашего hub, после включения режима разработчика. Запрос и ответ представляют собой строковые данные в формате JSON.

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

В качестве ответа возвращается JSON с информацией, полученной с этого устройства.

Пример запроса

{"cmd":"read","sid":"12345678901234"}

Пример ответа

{"cmd":"read_ack","model":"weather.v1","sid":"12345678901234","short_id":1234,"data":"{\"voltage\":3005,\"temperature\":\"2342\",\"humidity\":\"7769\",\"pressure\":\"97590\"}"}

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

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

Самый простой способ получить интересующий нас id датчика температуры это найти его в приложении Mi Home. После включения режима разработчика он будет отображаться в разделе информация о шлюзе. В этом большом JSON-е нужно найти блок с именем нашего устройства. Id датчика и имя устройства выделены на картинке красным.

Пароль нам не понадобится, он используется в запросах записи, нам же не нужно менять состояние, и потому достаточно запросов чтения.

В дополнении к информации с датчиков мне хотелось бы еще видеть, прогноз погоды и информацию об облачности. Но наш датчик не передает информацию об облачности. Потому я решил использовать один из открытых и бесплатных сервисов в интернете для получения этой информации, который называется open weather map.

Рассмотрим схему подключения дисплея к микроконтроллеру. Этот дисплей достаточно универсален и может работать от как от напряжения 3,3 вольта, так и от 5 вольт. Я решил его подключить к пятивольтовому пину, так он не должен нагружать преобразователь напряжения Wemos. Провод gnd монитора подключаем к пину G WeMos. SCK к D2, SDA к D1. Дополнительных источников питания, преобразований напряжений логики здесь не требуется. На этом сборка электрической части этого проекта завершена.

Приступим к написанию программы. Я не буду рассказывать о каждой строчке этой программы. Опишу только общие моменты.

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

К сожалению Arduino IDE, не такое мощное как IDE от компании JetBrains. И сам язык не такой удобный и мощный. В прочем это не сложно понять, учитывая то, что мы пишем программу для микроконтроллера с весьма скромными характеристиками железа.

И так, чтобы как-то структурировать код в Arduino IDE я обычно разбиваю программу на несколько файлов. Основной файл должен называться так же как называется проект, это ограничение самого Arduino IDE. В этом файле содержится основной цикл программы, и ее настройки, объявлены все глобальные переменные, константы и происходит подключение основных зависимостей. Основной функционал программы я разбиваю на несколько файлов. Например, в файле с названием wi-fi находится функционал, относящийся к работе с wi-fi. В файле с названием display функционал, относящийся к работе с монитором.

Все функции, типы данных, глобальные переменные файла называются с прификсом, совпадающим с именем файла. От этого правила можно отступить, если переменная не глобальная и будет использоваться только в этом файле. Таким образом, можно всегда понять в каком файле объявлен, тот или иной метод. Например, если вы увидите в коде программы метод с именем displayLog, то будет сразу понятно, что он объявлен где-то в файле display, и то, что он делает, имеет отношение к монитору. Если тот или иной функционал требует предварительной настройки, то в файле объявляется метод Setup, с прификсом файла. Например: wifiSutup, displaySetup. Если требуется выполнять какие-то действия, в основном цикле программы, то объявляются методы loop. Например, wifiLoop, displayLoop.

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

Отдельно следует сказать про глобальные переменные. Вообще лучше избегать прямое использование одних и тех же переменных в разных файлах. Т.е. если в одном из файлов объявлена глобальная переменная, то ее лучше использовать только в этом файле. Если вам нужно предоставить доступ к ее состоянию для других файлов, то лучше будет объявить геттеры и сеттеры, или другие специальные методы для изменения состояния той или иной части программы. Иногда бывает необходимым все-таки объявить такую переменную, которая должна быть доступной во все программе. В таком случае ее лучше объявить в главном файле. И то же самое относится ко всем константам. Если они носят глобальный характер, то их лучше объявлять в главном файле, если локальный, то в отдельном файле.

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

Возможно, кому-то эти правила покажутся сложными, но это на самом деле просто. Разбивайте логику своей программы, на несколько файлов, используйте прификсы и старайтесь избегать использования общих глобальных переменных в разных файлах там, где это возможно. Тогда код ваших программ станет более структурированным и его будет проще поддерживать.

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

В файле config содержатся все настройки, которые у вас будут отличаться от моих. В частности идентификатор и пароль wi-fi сети, к которой будет подключаться наш модуль, идентификаторы датчиков температуры, ip адрес шлюза в сети, url и ключ для доступа к open weather map API. Поскольку этот файл у всех индивидуален он находится не под версионным контролем. Если вы будите выкачивать мой проект c github, то вам его нужно будет сделать самостоятельно, переименовав config.ino.example в config.ino.

В файле display находится все методы, отвечающие за формирование картинки на экране.

Файл images.h содержит большие массивы байт с картинками облаков, и шрифтами.

Led отвечает за работу светодиода на плате. После настройки платы его лучше выключить, потому, что он светится слишком ярко и портит картинку на дисплее. Некоторые разработчики его даже выпаивают, чтобы снизить энергопотребление WeMos, но наша плата будет питаться от micro USB провода и для нас это не важно.

Owm это сокращение от open weather map. В основном цикле мы получаем данные о погоде из интернета и сохраняем их в специальном типе структурированных данных. Дальше последние сохраненные данные можно будет получить через геттеры из других частей программы.

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

Рассмотрим более подробно, как это происходит.

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

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

Разбор ответа происходит с помощью библиотеки ArduinoJson. Эта библиотека конечно требовательна к памяти, но в данном случае мы можем это себе позволить. Памяти хватает.

После разбора данных обновленная информация помещается в структуру данных, которая была передана в качестве параметра.

В файле WeMos, я устанавливаю частоту обмета данными с компьютером равной 74880 бод и вывожу на монитор порта некоторую информацию о плате WeMos.

В файле wi-fi происходит настройка wi-fi. Для того чтобы в случаях потери wi-fi сигнала соединение автоматически восстанавливалось нужно использовать метод WiFi.setAutoReconnect(true). Но, по отзывам других программистов, этот метод не всегда ведет себя корректно, поэтому в цикле основной программы я добавил блок кода, который должен восстановить соединение с Wi-Fi в случае его потери. Я тестировал этот код, пробовал выключать wi-fi роутер и снова его включать. Все работает корректно, после сбоев в сети Wi-Fi соединение восстанавливается.

Рассмотрим более подробно, какая информация выводится на дисплее и как это происходит в коде.

В левом верхнем углу экрана я вывел показания температуры с двух датчиков температуры крупным шрифтом для того, чтобы они хорошо читались. Один из датчиков расположен на южной стороне дома, второй на северной. Бывает, что утром температура на них отличается на 10 градусов, днем разброс тоже бывает достаточно большим. Можно было бы посчитать среднюю температуру и вывести только ее. Но думаю, в таком виде информация более полезна. Так можно оценить температуру на солнце и в тени.

Под температурой отображается информация о средней влажности и среднем давлении. Считаю эту информацию менее полезной, потому вывел ее мелким шрифтом.

В правой части экрана отображается крупная читаемая значок погоды. Эта информация берется с сайта open weather map. Вот все возможные виды значков. Под ним отображается его расшифровка на английском языке. Я не стал ее переводить на русский, мне больше нравится так. Согласно документации с сайта погоды, у одного и того же значка может быть несколько расшифровок, но они будут близки по смыслу.

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

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

Мне так же хотелось вывести информацию о заряде батареек в датчиках погоды, но для нее уже практически не осталось места. Поэтому я вывел просто несколько точек. Вот они. Две правые точки не двигаются, а две точки левее будут перемещаться от крайнего правого положения до крайнего левого положения т.е. до левого угла экрана, по мере того как батарейка в датчиках будет разряжаться. Крайнее правое положение соответствует напряжению батарейки в 3 вольта, а крайнее левое напряжению 2,5 вольта. Получается, что когда они придут в движение и сместятся, то это будет означать, что батарейки в датчиках нужно будет скоро менять. Я хорошо различаю эти точки, в то же время ни не занимают много места на экране.

Ну что ж, на этом программирование нашего устройства завершено.

Ссылки на исходный код, документацию, а так же на магазин в котором я покупал эти модули, я оставлю в описании под видео.