Практический опыт использования Blynk для датчика СО2. Часть 1

    Всем привет. Это еще одна статья из разряда 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 месяца.

    Проект работает, прошу ничего не ломать.
    Поделиться публикацией
    Никаких подозрительных скриптов, только релевантные баннеры. Не релевантные? Пиши на: adv@tmtm.ru с темой «Полундра»

    Зачем оно вам?
    Реклама
    Комментарии 22
    • +1
      И что же будет в части 2?
      Тег «говнокод» выглядит как-то не очень. Вы уверены, что хотите, чтобы вашу статью по этому тегу находили?
      • +1
        Во второй части будут рассмотрены другие вопросы, которые возникли при разработке под ESP но для немного другого устройства, с немного другими ограничениями.
        Я проверил, то тегу говнокод нет новых публикаций, а по другому я пока свое хобби назвать не могу. Такая самоирония.
      • +3
        Спасибо за статью! Как раз продумываю веньиляцию в квартире и хочу сделать автоматическое управление заслонками в зависимости от со и температуры (много со сосем с улицы, холодно гоним через батарею, мало со и холодно гоним через батарею с улицы не сосем, жарко и много со сосем с улицы и гоним через кондей, жарко и мало со просто работает кондей)
        • 0
          Теоретически у нас есть реле (https://geektimes.ru/post/298211/) но я пока не придумал ничего чем бы можно было управлять.
        • +2
          Очень интересная статья. Спасибо.
          Возник вопрос такой: получается можно передавать данные в пределах своей Wi-Fi сети? То есть, можно обойтись без облака?
          • 0
            При использовании Blynk совсем без облака не обойтись, можно установить свой локальный сервер (скачиваешь java и запускаешь, ничего сложного), например на parsberry, и «отвязаться» от интернета.
            • 0
              Спасибо.
              Мне просто интересен вопрос безопасности передаваемых данных)
              • 0
                Если верить документации, то железка подключается к облаку с использованием SSL/TLS.
                А свой собственный сервер — вообще полностью изолированная штука (можно даже исходники посмотреть).
                • +1
                  Тогда круто) Можно умный дом на нем спроектировать полноценно.
                  • +1
                    Смотря что понимать под определением «умный дом»)
                    • +1
                      Все в рамках бюджета, дешево и сердито.
                    • 0
                      Я бы конечно назвал менее громким названием — типа «малая автоматизация своими руками»
                      • +1
                        Ну, да, я имею ввиду домашнюю автоматизацию, своими руками, в рамках необходимой системы и бюджета)
            • +1
              Уже давно такой же проект делал один чувак, вот исходники с прошивкой github.com/kumekay/kuhomon
              Сам собрал быстренько по его проекту, работает как часы.
              • 0
                Да, я давно понял, что я примерно десятитысячный «кто придумал» такую домашнюю метеостанцию. Но иногда процесс важнее результата.
                • +6
                  как говорится, что не делай из ардуины, всё равно получатся часы, а из esp8266 — метеостанция :)
                • +1
                  есть ещё варианция wifimanager-a с доп. добавленным блинковским токеном.
                  • 0
                    Я не написал об этом, но я добавил в 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
                      ок, спасибо. сам я немного эту библиотеку изучал, но как-то мне не всё понравилось. позвольте пару вопросов:
                      1) если нет связи с AP (например, роутер завис или сосед запустил свою точку доступа с таким же именем, с более сильным сигналом, но с другим паролем), у вас wifimanager переводит ESP-шку в режим AP и ожидания ввода настроек или будет пытаться реконнектиться к старой AP?
                      2) mqtt_server[0] != '\0' || blynk_token[0] != '\0' — это означает, что wifimanager не отработал, esp-шка перезагрузилась или что-то ещё?
                      • +1
                        1) Если пропадет сигнал WiFi (роутер завис) — то через минуту ESP ресетнется, при старте WiFi менеджер попробует подключиться к точке доступа с таким же именем (соседа), но пароль не подойдет и поэтому ESP перейдет в режим точки доступа (начнет моргать красный светодиод). Через 5 минут (настраиваемый таймаут) — ребутнемся (если блинк токен сохранен в памяти) или просто пойдем в loop.

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

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

                    Самое читаемое