Всем привет. Это еще одна статья из разряда ESP8266 + Blynk = ?. Прошу не воспринимать как рекламу, а только как дань уважения разработчикам платформы Blynk и личный опыт, который может быть полезен кому то еще, кроме меня.

Начало


Идея проекта родилась несколько лет назад, когда в порыве DYI-энтузиазма на Ali был куплен датчик качества воздуха MQ-135. По спецификации этот датчик реагирует на наличие в воздухе таких веществ как: NH3, NOx, спирт, бензин, дым и CO2 и выдает свою абстрактную оценку качества воздуха на аналоговом выходе [да я знаю, что существуют подстроечные резисторы и способы калибровки, но как то это слишком сложно].

Испытания показали, что на всякие вредные и «вонючие» соединения датчик реагирует отлично, показывая достаточно резкое изменение выходного уровня. Хуже дело обстояло с определением невидимого врага, а именно углекислого газа СО2. Про вред и очевидную повсеместность этого диоксида сказано немало, повторяться не будем.


Поэтому для меня, датчик MQ-135 оказался бесполезным, поскольку не мог «заметить» существенную разницу в качестве воздуха в переполненном людьми помещении и на свежем воздухе. Но вызов был уже принят, поэтому несколько итераций спустя родилась последняя (текущая) версия платы OpenWindAir с ИК-датчиком MH-Z19 [да не идеальный, да китайский]. Подробнее про получившуюся железку и ее аппаратные возможности написано в статье Система сбора данных на ESP. Часть I.

Для задачи измерения уровня углекислого газа в жилом помещении датчик оказался идеальным и оптимальным по цене (1200 рублей на Ali с доставкой) решением.

Blynk — помогает соединить железо, облако и телефон


Про платформу Blynk уже много хорошего сказано, например тут. Возможности платформы просто удивляют своей продуманностью и удобством использования. Поэтому когда пришло время выбирать среду разработки для ESP8266 и писать программу, выбор сразу пал на Arduino IDE и библиотеку Blynk.

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


Архитектура ПО


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

Перед началом разработки ТЗ было сформулировано тезисно и включало следующие пункты:

1. Необходимо с определенным интервалом считывать показания датчика CO2 (MH-Z19) и отображать результаты с помощью трех (зеленый, желтый, красный) светодиодов. Пределы были выбраны почти с учетом ГОСТ 30494-2011 (Здания жилые и общественные. Параметры микроклимата в помещениях.): до 900 PPM – зеленый, от 901 до 1400 PPM — желтый, выше 1401 PPM — красный. Также у нас есть бипер, порог бибикания которого задан на уровне 1100 PPM, но его можно настроить или вообще отключить через Blynk. Во время отладки выяснилось, что иногда MH-Z19 может глюкануть и выдать свое максимальное значение (в зависимости от установленного предела: 1000, 2000, 3000 или 5000 PPM), вместо фактически измеренного. Это немного осложнило обработку результатов и могло привести к ложным сообщениям пользователю, а нервы пользователя надо беречь. И поскольку нет абсолютно верного (кроме многократных измерений) способа отличить неверно измеренные 2000 PPM (дикое значение для жилого помещения) от ситуации, когда пользователь сидит и специально дышит в датчик. То было принято две меры по маскировке данной проблемы: установлен предел измерения в 2000 PPM (предполагается использование прибора в жилых помещениях и все что больше 1400 для нас уже красная зона) и добавлено усреднение результатов последних 10 измерений. Как итог — единичные ложные срабатывания (на 2000 PPM) не дают больших всплесков на усредненном графике. Но при желании через Blynk можно настроить предел измерения датчика и посмотреть фактическое (не усредненное значение CO2).

2. Для работы с датчиком температуры\влажности (AM2302) была использована библиотека DHT Sensor Library от Adafruit. Было сделано два небольших изменения: добавил повторное считывание AM2302 (иногда считывается не с первого раза) и введены поправочные коэффициенты для значений температуры и влажности. Если используется встроенный датчик, то опытным путем установлено, что воздух внутри прибора «суше» на 15% и теплее на 2 градуса C (1 градус F) чем снаружи, при использовании выносного датчика (выбирается джампером) — поправку в измеренные результаты вносить не надо и можно отключить.

3. Пользователь должен иметь возможность настроить устройство (подключиться к WiFi, указать auth token и тд) без дополнительного софта или перепрошивки. Наиболее оптимальным решением стало использование библиотеки WiFiManager, которая переводит ESP в режим точки доступа и позволяет через Captive портал сохранить во флешку настройки WiFi сети и другие параметры.


В дальнейшем при старте библиотека пытается подключится к сохраненной WiFi точке и в случае неудачи снова переходит в режим точки доступа и Captive портала. А если пользователь вдруг не захочет использовать Blynk или у него не окажется WiFi-роутера, то в этом случае OpenWindAir никогда на загрузится и будет только стартовать в AP-режиме и перезагружаться по таймауту.

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

if (!wifiManager.autoConnect("OpenWind - tap to config")){
if (mqtt_server[0] != '\0' || blynk_token[0] != '\0'){
Serial.println("Failed to go online for Blynk and MQTT, restarting..");
ESP.restart();
}
else{
Serial.println("Failed to go online, offline mode activated");
online = false;
}


4. Blynk требует подключения к Интернету (если сервер не локальный) и поэтому необходимо контролировать наличие подключения к WiFi. Библиотека WiFiManager на данный момент не умеет восстанавливать соединение с WiFi и если в квартире «моргнет» свет и роутер перезагрузится, то восстановить подключение ESP8266 к WiFi поможет только перезагрузка. Поэтому пришлось добавить простой таймер, который через 60 непрерывных секунд отсутствия коннекта перезагрузит устройство.

if (WiFi.status() != WL_CONNECTED && online){
if (!wifilost_flag){
wifilost_timer_start = uptime;
wifilost_flag = true;
}
if (((uptime - wifilost_timer_start) > wifilost_timer_max) && wifilost_flag){
Serial.print("\n\rWiFi connection lost, restarting..");
wifilost_flag = false;
ESP.restart();
}
}

5. В качестве альтернативы использования Blynk пользователь может выбрать отправку показаний по протоколу MQTT на сервер Народного мониторинга или любого другого подобного сервиса. Для этих целей была выбрана библиотека PubSubClient, которая написана на наиболее понятном мне языке Си и единственная (из представленных в каталоге Arduino IDE), которая имела понятные примеры.

6. Перепрошивка устройства дело хоть и не частое и не очень сложное (особенно при наличии встроенного CP2102), но все равно захотелось максимально упростить этот процесс. Библиотека ArduinoOTA позволяет легко загрузить новый бинарник и прошить его. Активировать ОТА можно как кнопкой на устройстве, так и удаленно через телефон. Однако без сюрпризов не обошлось, оказывается мной были куплены модули ESP8266-12E с разным размером файловой системы (SPIFFS).

Примерное распределение Flash


Внешне не отличимые модули ESP8266-12E могут иметь файловую систему размером 1 или 3 Мб и требовать разные прошивки (опции сборки в Arduino IDE). Поэтому, чтобы избежать возможных проблем, при загрузке надо проверять фактический размер памяти и при ОТА апгрейде запрашивать на сервере соответствующий бинарник (пока не сделано). Или можно пойти чуть более простым путем и собирать все прошивки под SPIFFS c меньшим номиналом 1 Мб, т. к. они вполне работают на ESP8266-12E c большим объемом памяти.

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

String realSize = String(ESP.getFlashChipRealSize());
String ideSize = String(ESP.getFlashChipSize());
bool flashCorrectlyConfigured = realSize.equals(ideSize);

if(flashCorrectlyConfigured){
Serial.println("flash correctly configured, SPIFFS starts, IDE size: " + ideSize + ", match real size: " + realSize);
}
else{
Serial.println("flash incorrectly configured, SPIFFS cannot start, IDE size: " + ideSize + ", real size: " + realSize);
}

7. Чтобы самому не путаться в разных версиях ПО и отличать их друг от друга, был немного переписан файл arduino-1.8.5\hardware\platform.txt от Arduino IDE так, чтобы во время компиляции запускался bat файл, который делает копию текущего скетча и полученного бинарника, а также автоматически инкрементирует номер версии.

recipe.hooks.sketch.prebuild.0.pattern=D:\arduino-1.8.5\hardware\increment.bat {build.path} {build.source.path} {build.project_name}

Таким образом, после каждой сборки\прошивки имеем зашитый в бинарнике номер версии и копию скетча с таким же номером. А если папку со скетчем положить в Dropbox — то получится самодельная система контроля версий.

Инструкция по настройке автоинкремента версии для Arduino IDE и bat-файл выложены на гитхабе.

8. Ну а раз есть встроенный USB-UART переходник (с драйвером для CP2102 нет никаких проблем в Windows и Linux), то нельзя было не добавить вывод результатов измерений через Терминал (на скорости 9600). Раз в двадцать секунд выводятся результаты измерений и сообщения об ошибках.

Reading MHZ19 sensor: ok
Reading DHT22 sensor: ok
===================================================

Humidity: 36.20%
Temperature: 27.20C \ 83.56F
C02: 1153 ppm
C02 average: 462 ppm
ADC: 99
UpTime: 0 days, 0 hours, 3 minutes, 45 seconds.
Time: 16:25:56 20/3/2018
===================================================


А по нажатию кнопки Enter можно получить сообщение с системной информацией.
======SYSTEM-STATUS================================
Device name: OpenWindAir
Software version: 0.1.235
FreeHeap: 33824
ChipId: 13704617
FlashChipId: 1405167
FlashChipSize: 4194304
FlashChipSpeed: 40000000
CycleCount: 2204474679
Time: 16:27:6 20/3/2018
UpTime: 295
======BLYNK-STATUS=================================
Blynk token: 65a99f9e363a421c8b22d5b0162cce27
Blynk connected: 1
Notify level: 1100
Beep: 1
CO2 limit: 2000
Temperature correction: 1
======NETWORK-STATUS===============================
WiFi network: adakta2
WiFi status: 3
RSSI: -70
MAC: 18FE34D11DA9
IP: 192.168.0.152
Online: 1
======MQTT-STATUS==================================
MQTT server:narodmon.ru
MQTT port:1883
MQTT login:login
MQTT key:key
MQTT topics:
/OpenWindAir/h
/OpenWindAir/t
/OpenWindAir/f
/OpenWindAir/ppm
/OpenWindAir/status
======END-of-STATUS================================


Самая неприятная проблема


Самое неприятное с чем пришлось столкнуться при разработке, это когда при одновременной отправке результатов измерений на сервер MQTT и в Blynk, часть данных может начать теряться и не доходить до сервера. Как оказалось, на то, чтобы подключиться к серверу MQTT и отправить данные — может понадобиться несколько секунд и за это время библиотека Blynk успевает потерять соединение со своим сервером и в результате если вручную не инициировать переподключение к серверу — может пройти достаточно много времени и часть результатов измерений потеряется. Пришлось добавить проверку состояния WiFi клиента _blynkWifiClient и случае отсутствия коннекта делать принудительный стоп _blynkWifiClient.stop(), а потом подключаться к серверу Blynk заново.

if (WiFi.status() == WL_CONNECTED){
wifilost_flag = false;
if (blynk_token[0] != '\0'){
if (Blynk.connected() && _blynkWifiClient.connected()){
Blynk.run();
}
else{
Serial.print("\n\rReconnecting to blynk.. ");
Serial.print(Blynk.connected());
if (!_blynkWifiClient.connected()){
 _blynkWifiClient.stop();
 Return _blynkWifiClient.connect(BLYNK_DEFAULT_DOMAIN, BLYNK_DEFAULT_PORT);
}
Blynk.connect(4000);
Serial.print(Blynk.connected());
}
}

Заключение


Это моя первая статья, хотя с момента регистрации на Хабре прошло уже 7 лет. Прошу не судить очень строго и не обращать внимание на говнокод, который пока является единственным языком программирования, которым я владею.

Ознакомиться с проектом целиком можно в репозитории на гитхабе.

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

Далее будет QR код, просканировав который приложением Blynk (AppSore , Android) можно узнать, какой микроклимат был у меня дома последние 3 месяца.

Проект работает, прошу ничего не ломать.

Комментарии (22)


  1. negasus
    21.03.2018 11:07

    И что же будет в части 2?
    Тег «говнокод» выглядит как-то не очень. Вы уверены, что хотите, чтобы вашу статью по этому тегу находили?


    1. a3x Автор
      21.03.2018 11:49

      Во второй части будут рассмотрены другие вопросы, которые возникли при разработке под ESP но для немного другого устройства, с немного другими ограничениями.
      Я проверил, то тегу говнокод нет новых публикаций, а по другому я пока свое хобби назвать не могу. Такая самоирония.


  1. Rupper
    21.03.2018 11:50
    +2

    Спасибо за статью! Как раз продумываю веньиляцию в квартире и хочу сделать автоматическое управление заслонками в зависимости от со и температуры (много со сосем с улицы, холодно гоним через батарею, мало со и холодно гоним через батарею с улицы не сосем, жарко и много со сосем с улицы и гоним через кондей, жарко и мало со просто работает кондей)


    1. a3x Автор
      21.03.2018 12:01

      Теоретически у нас есть реле (https://geektimes.ru/post/298211/) но я пока не придумал ничего чем бы можно было управлять.


  1. dmsav
    21.03.2018 11:50
    +2

    Очень интересная статья. Спасибо.
    Возник вопрос такой: получается можно передавать данные в пределах своей Wi-Fi сети? То есть, можно обойтись без облака?


    1. a3x Автор
      21.03.2018 11:52

      При использовании Blynk совсем без облака не обойтись, можно установить свой локальный сервер (скачиваешь java и запускаешь, ничего сложного), например на parsberry, и «отвязаться» от интернета.


      1. dmsav
        21.03.2018 12:25

        Спасибо.
        Мне просто интересен вопрос безопасности передаваемых данных)


        1. a3x Автор
          21.03.2018 12:34

          Если верить документации, то железка подключается к облаку с использованием SSL/TLS.
          А свой собственный сервер — вообще полностью изолированная штука (можно даже исходники посмотреть).


          1. dmsav
            21.03.2018 12:37
            +1

            Тогда круто) Можно умный дом на нем спроектировать полноценно.


            1. negasus
              21.03.2018 13:04
              +1

              Смотря что понимать под определением «умный дом»)


              1. a3x Автор
                21.03.2018 13:09
                +1

                Все в рамках бюджета, дешево и сердито.


            1. a3x Автор
              21.03.2018 13:10

              Я бы конечно назвал менее громким названием — типа «малая автоматизация своими руками»


              1. dmsav
                21.03.2018 15:22
                +1

                Ну, да, я имею ввиду домашнюю автоматизацию, своими руками, в рамках необходимой системы и бюджета)


  1. apan65
    21.03.2018 12:13
    +1

    Уже давно такой же проект делал один чувак, вот исходники с прошивкой github.com/kumekay/kuhomon
    Сам собрал быстренько по его проекту, работает как часы.


    1. a3x Автор
      21.03.2018 12:15

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


    1. safari2012
      21.03.2018 18:35
      +1

      как говорится, что не делай из ардуины, всё равно получатся часы, а из esp8266 — метеостанция :)


  1. safari2012
    21.03.2018 18:28
    +1

    есть ещё варианция wifimanager-a с доп. добавленным блинковским токеном.


    1. a3x Автор
      21.03.2018 18:43

      Я не написал об этом, но я добавил в WiFiManager токен блинка и настройки MQTT
      WiFiManagerParameter custom_blynk_token("blynk", "blynk token", blynk_token, 33);
      WiFiManagerParameter custom_mqtt_server("server", "mqtt server", mqtt_server, 40);
      WiFiManagerParameter custom_mqtt_port("port", "mqtt port", mqtt_port, 5);
      WiFiManagerParameter custom_mqtt_login("login", "mqtt login", mqtt_login, 23);
      WiFiManagerParameter custom_mqtt_key("key", "mqtt key", mqtt_key, 23);


      1. safari2012
        22.03.2018 15:12
        +1

        ок, спасибо. сам я немного эту библиотеку изучал, но как-то мне не всё понравилось. позвольте пару вопросов:
        1) если нет связи с AP (например, роутер завис или сосед запустил свою точку доступа с таким же именем, с более сильным сигналом, но с другим паролем), у вас wifimanager переводит ESP-шку в режим AP и ожидания ввода настроек или будет пытаться реконнектиться к старой AP?
        2) mqtt_server[0] != '\0' || blynk_token[0] != '\0' — это означает, что wifimanager не отработал, esp-шка перезагрузилась или что-то ещё?


        1. a3x Автор
          22.03.2018 16:55

          1) Если пропадет сигнал WiFi (роутер завис) — то через минуту ESP ресетнется, при старте WiFi менеджер попробует подключиться к точке доступа с таким же именем (соседа), но пароль не подойдет и поэтому ESP перейдет в режим точки доступа (начнет моргать красный светодиод). Через 5 минут (настраиваемый таймаут) — ребутнемся (если блинк токен сохранен в памяти) или просто пойдем в loop.

          2) Это если WiFi менеджер не смог подключить к WiFi — мы проверяем, сохранены ли у нас во флешке Blynk token или сервер MQTT — и если сохранены, то подключение к интернету нам обязательно — и будем перезагружаться и пытаться снова.
          Если в флешке ничего не сохранено — то значит в онлайн нам не очень надо и можно включить и начать мерять СО2 и без интетнета.


  1. mr_Piglet
    22.03.2018 06:39
    +2

    От одиночных всплеск показаний может помочь медианный фильтр.


    1. a3x Автор
      22.03.2018 11:50

      Да, спасибо, действительно должно помочь.