Борьба за открытое окно: технологии победили

Тема «закройте окно, мне холодно!» и «откройте окно, мне душно!» знакома каждому, но особенно она доставляет неудобств, когда начинается не в офисе, а дома.

«Наука обычно побеждает!» — подумал я. И решил доказать домашним (а я как раз тяжело дышу при закрытых окнах), что закрытые окна приводят к повышению концентрации СО2 в квартире, что не сказывается положительно ни на ком. Тем более на детях.

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

Борьба за открытое окно: технологии победили

Постановка задачи была такая: прибор должен быть маленький, питаться от любой USB-розетки, собирать данные о CO2 и передавать их куда-то, чтобы потом получать графики и анализировать. Для изменения настроек устройства не должны быть нужны провода.

Было принято решение строить сам прибор для снятия данных на ESP8266, а данные отправлять в конечном итоге в Grafana на Raspberry PI 3, которая и так есть и всегда включена.

Датчиков было заказано по два комплекта.

  • CO2: CCS881 и MH-Z19.
  • Температура и влажность: DHT-11(22) и BME280 (в последнем еще и давление).

Но сборка производилась на тех компонентах, которые приехали раньше.

Финальный комплект компонентов конкретной сборки

  1. Макетная плата 3 х 7 см — 48 рублей.
  2. ESP8266 Wi-Fi модуль ESP-12S — 365 рублей.
  3. DC-DC преобразователь AMS1117 (с 4.8–12 V в 3.3 V) — 11 рублей.
  4. DHT-11 — 55 рублей.
  5. CCS881 — 458 рублей.
  6. MICRO MINI5P USB to Dip Female B-type — 7 рублей.
  7. Тактовая кнопка (две штуки) — 4 рубля.
  8. Резисторы по одной штуке (1 кОм, 2.2 кОм, 10 кОм) — 3 рубля.
  9. Гребенка с четырьмя штырьками — 1 рубль.
  10. Провода — 3 рубля.

Дополнительно для проведения работ

  • USB-TTL модуль CP2102 (для подключения к компьютеру).
  • Кабель mini-USB.
  • Сервер на Linux (у меня это Raspberry PI на Raspbian).

Софт

  • Arduino IDE.
  • Fritzing.
  • Raspbian.
  • Mosquitto.
  • Telegraf.
  • InfluxDB.
  • Grafana.

0. Аппаратная часть

После определения модулей, которые будут использованы, сначала была отрисована принципиальная схема во Fritzing, а за ней превращена в удобочитаемую макетная. На реальной плате не предполагается наличие модуля USB-TTL, а только пины для его подключения.

Борьба за открытое окно: технологии победили
Борьба за открытое окно: технологии победили

Далее по схеме на макетной плате была собрана плата.

Борьба за открытое окно: технологии победили

Готовую плату пока откладываем. И приступаем к настройке принимающей стороны.

1. Устанавливаем и запускаем mosquitto MQTT брокер (сервер и клиент)

Погнали.

sudo apt -y install mosquitto mosquitto-clients

Для настройки авторизации в файл /etc/mosquitto/conf.d/auth.conf записываем следующие параметры.

allow_anonymous false password_file /etc/mosquitto/passwd

Создаем пользователя arduino для отправки данных с платы в брокер и назначаем ему пароль. Именно этот пароль нужно использовать в дальнейшем в скетче (вписать до заливки).

sudo mosquitto_passwd -c /etc/mosquitto/passwd arduino //пароль для примера 99999

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

sudo mosquitto_passwd /etc/mosquitto/passwd telegraf //пароль для примера inserthardtoreadpasswordhere

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

sudo service mosquitto restart

Теперь уже можно проверять работу брокера. Открываем на «прослушку» все топики брокера под определенным названием (в нашем случае sensors/…)

mosquitto_sub -d -t sensors/# -u "telegraf" -P "inserthardtoreadpasswordhere"

Чтобы закинуть что-то в эфир у нас есть два варианта:

  1. Перейти в конец данной статьи, скорректировать в соответствии с уже известными логином, паролем и IP-адресом mosquitto скетч и залить его на ESP скетч и убедиться, что от него летят данные.
  2. Открыть второе окно терминала и оттуда что-то отправить в топик sensors. Например, “Hello world!”:
mosquitto_pub -h localhost -t "sensors" -m "hello world" -u "arduino" -P "99999"

Если все сделано правильно, то в первом окне терминала мы увидим свое сообщение. Можно продолжать.

Борьба за открытое окно: технологии победили

2. Устанавливаем и запускаем influxDB

Здесь без каких-то особых нюансов, все по инструкции, коих много. Все заработало с первого раза.

curl -sL https://repos.influxdata.com/influxdb.key | sudo apt-key add - echo "deb https://repos.influxdata.com/debian stretch stable" | sudo tee /etc/apt/sources.list.d/influxdb.list sudo apt update sudo apt -y install influxdb sudo service influxdb start

Сразу создаем двух пользователей: одного для сервиса telegraf, а второго уже для Grafana.

influx -execute "CREATE USER "telegraf" WITH PASSWORD 'telegraf' WITH ALL PRIVILEGES;" influx -execute "CREATE USER "grafana" WITH PASSWORD 'grafana' WITH ALL PRIVILEGES;"

3. Устанавливаем и запускаем telegraf

sudo apt -y install telegraf

Установка завершена :) Но есть нюансы. Вся настройка заключается в корректировке файла /etc/telegraf/telegraf.conf.

# Telegraf Configuration # # Telegraf is entirely plugin driven. All metrics are gathered from the # declared inputs, and sent to the declared outputs. # # Plugins must be declared in here to be active. # To deactivate a plugin, comment out the name and any variables. # # Use 'telegraf -config telegraf.conf -test' to see what metrics a config # file would generate. # # Environment variables can be used anywhere in this config file, simply surround # them with ${}. For strings the variable must be within quotes (ie, "${STR_VAR}"), # for numbers and booleans they should be plain (ie, ${INT_VAR}, ${BOOL_VAR}) # Global tags can be specified here in key="value" format. [global_tags] # dc = "us-east-1" # will tag all metrics with dc=us-east-1 # rack = "1a" ## Environment variables can be used as tags, and throughout the config file # user = "$USER" # Configuration for telegraf agent [agent] ## Default data collection interval for all inputs interval = "10s" ## Rounds collection interval to 'interval' ## ie, if interval="10s" then always collect on :00, :10, :20, etc. round_interval = true ## Telegraf will send metrics to outputs in batches of at most ## metric_batch_size metrics. ## This controls the size of writes that Telegraf sends to output plugins. metric_batch_size = 1000 ## Maximum number of unwritten metrics per output. metric_buffer_limit = 10000 ## Collection jitter is used to jitter the collection by a random amount. ## Each plugin will sleep for a random time within jitter before collecting. ## This can be used to avoid many plugins querying things like sysfs at the ## same time, which can have a measurable effect on the system. collection_jitter = "0s" ## Default flushing interval for all outputs. Maximum flush_interval will be ## flush_interval + flush_jitter flush_interval = "10s" ## Jitter the flush interval by a random amount. This is primarily to avoid ## large write spikes for users running a large number of telegraf instances. ## ie, a jitter of 5s and interval 10s means flushes will happen every 10-15s flush_jitter = "0s" ## By default or when set to "0s", precision will be set to the same ## timestamp order as the collection interval, with the maximum being 1s. ## ie, when interval = "10s", precision will be "1s" ## when interval = "250ms", precision will be "1ms" ## Precision will NOT be used for service inputs. It is up to each individual ## service input to set the timestamp at the appropriate precision. ## Valid time units are "ns", "us" (or "µs"), "ms", "s". precision = "" ## Log at debug level. # debug = false ## Log only error level messages. # quiet = false ## Log file name, the empty string means to log to stderr. logfile = "telegraflog" ## The logfile will be rotated after the time interval specified. When set ## to 0 no time based rotation is performed. Logs are rotated only when ## written to, if there is no log activity rotation may be delayed. logfile_rotation_interval = "2d" ## The logfile will be rotated when it becomes larger than the specified ## size. When set to 0 no size based rotation is performed. # logfile_rotation_max_size = "0MB" ## Maximum number of rotated archives to keep, any older logs are deleted. ## If set to -1, no archives are removed. # logfile_rotation_max_archives = 5 ## Override default hostname, if empty use os.Hostname() hostname = "" ## If set to true, do no set the "host" tag in the telegraf agent. omit_hostname = false ############################################################################### # OUTPUT PLUGINS # ############################################################################### # Configuration for sending metrics to InfluxDB [[outputs.influxdb]] ## The full HTTP or UDP URL for your InfluxDB instance. ## ## Multiple URLs can be specified for a single cluster, only ONE of the ## urls will be written to each interval. # urls = ["unix:///var/run/influxdb.sock"] # urls = ["udp://127.0.0.1:8089"] urls = ["http://localhost:8086"] ## The target database for metrics; will be created as needed. ## For UDP url endpoint database needs to be configured on server side. database = "sensors" ## The value of this tag will be used to determine the database. If this ## tag is not set the 'database' option is used as the default. # database_tag = "" ## If true, the database tag will not be added to the metric. # exclude_database_tag = false ## If true, no CREATE DATABASE queries will be sent. Set to true when using ## Telegraf with a user without permissions to create databases or when the ## database already exists. # skip_database_creation = false ## Name of existing retention policy to write to. Empty string writes to ## the default retention policy. Only takes effect when using HTTP. retention_policy = "" ## Write consistency (clusters only), can be: "any", "one", "quorum", "all". ## Only takes effect when using HTTP. write_consistency = "any" ## Timeout for HTTP messages. timeout = "5s" ## HTTP Basic Auth username = "telegraf" password = "telegraf"

До следующего раздела все оставляем по умолчанию. В связке логин и пароль выше — доступ к InfluxDB, а ниже — к mosquitto.

# # Read metrics from MQTT topic(s) [[inputs.mqtt_consumer]] # ## MQTT broker URLs to be used. The format should be scheme://host:port, # ## schema can be tcp, ssl, or ws. servers = ["localhost:1883"] # # ## Topics that will be subscribed to. topics = ["sensors/node2/#"] # # ## The message topic will be stored in a tag specified by this value. If set # ## to the empty string no topic tag will be created. # # topic_tag = "topic" # # ## QoS policy for messages # ## 0 = at most once # ## 1 = at least once # ## 2 = exactly once # ## # ## When using a QoS of 1 or 2, you should enable persistent_session to allow # ## resuming unacknowledged messages. qos = 0 # # ## Connection timeout for initial connection in seconds # # connection_timeout = "30s" # # ## Maximum messages to read from the broker that have not been written by an # ## output. For best throughput set based on the number of metrics within # ## each message and the size of the output's metric_batch_size. # ## # ## For example, if each message from the queue contains 10 metrics and the # ## output metric_batch_size is 1000, setting this to 100 will ensure that a # ## full batch is collected and the write is triggered immediately without # ## waiting until the next flush_interval. max_undelivered_messages = 1000 # # ## Persistent session disables clearing of the client session on connection. # ## In order for this option to work you must also set client_id to identity # ## the client. To receive messages that arrived while the client is offline, # ## also set the qos option to 1 or 2 and don't forget to also set the QoS when # ## publishing. persistent_session = true # # ## If unset, a random client ID will be generated. client_id = "telegraf" # # ## Username and password to connect MQTT server. username = "telegraf" password = "telegraf" # # ## Optional TLS Config # # tls_ca = "/etc/telegraf/ca.pem" # # tls_cert = "/etc/telegraf/cert.pem" # # tls_key = "/etc/telegraf/key.pem" # ## Use TLS but skip chain & host verification # # insecure_skip_verify = false # # ## Data format to consume. # ## Each data format has its own unique set of configuration options, read # ## more about them here: # ## https://github.com/influxdata/telegraf/blob/master/docs/DATA_FORMATS_INPUT.md data_format = "value" data_type = "float"

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

sudo service telegraf reload

После этого можно проверить работоспособность связки InfluxDB и telegraf:

influx SHOW DATABASES

В результате в списке баз мы должны увидеть базу sensors.

Борьба за открытое окно: технологии победили

4. Установка и запуск Grafana

curl https://bintray.com/user/downloadSubjectPublicKey?username=bintray | sudo apt-key add - echo "deb https://dl.bintray.com/fg2it/deb jessie main" | sudo tee -a /etc/apt/sources.list.d/grafana.list sudo apt update sudo apt -y install grafana sudo systemctl enable grafana-server sudo service grafana-server start

Сразу после запуска Grafana становится доступна по адресу http://localhost:3000. Логин и пароль по умолчанию: admin\admin. Лучше его сразу поменять, для этого жмем на шестеренку слева и переходим в раздел Server Admin. Далее все интуитивно понятно.

5. Запуск ESP8266

Открываем в IDE скетч wifi_weather_station_mqtt.ino.

В соответствии со своими настройками правим в начале:

//================================================================== // ### ПАРАМЕТРЫ НАСТРОЙКИ ### #define DHTPIN 0 // пин датчика температуры #define DHTTYPE DHT11 // тип датчика const int PIN_LED = 2; // пин светодиода для тестов (встроенный в esp12s светодиод) const int TRIGGER_PIN = 0; // пин кнопки для перевода в режим прошивки const char* hostaddr = "weather01"; //адрес платы в сети const char* pwd = "99999"; // после смены пароля устройство нужно перезагрузить по питанию const char* mqttname = "arduino"; const char* mqttpwd = "99999"; const long sensors_int = 10000; // интервал отправки данных с датчиков брокеру MQTT const long blink_int = 1000; // интервал изменения статуса светодиода const char* mqttServer = "192.168.0.3"; //адрес MQTT брокера const int mqtt_port = 1883; // порт MQTT брокера

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

Для заливки нажимаем на кнопку ближе к датчику DHT, держим ее, а затем нажимаем на крайнюю и обе отпускаем.

После запуска платы ищем Wi-Fi сеть ESP***** и подключаемся к ней. Если окно настройки не всплывает автоматически, то открываем http://192.168.4.2.

Борьба за открытое окно: технологии победили
Борьба за открытое окно: технологии победили
Борьба за открытое окно: технологии победили

После выбора сети обязательно нажимаем /wifisave.

После перезагрузки устройства проверяем на сервере данные с ESP:

pi@raspberrypi:~ $ mosquitto_sub -d -t sensors/# -u "telegraf" -P "telegraf" Client mosqsub|25451-raspberry sending CONNECT Client mosqsub|25451-raspberry received CONNACK (0) Client mosqsub|25451-raspberry sending SUBSCRIBE (Mid: 1, Topic: sensors/#, QoS: 0) Client mosqsub|25451-raspberry received SUBACK Subscribed (mid: 1): 0 Client mosqsub|25451-raspberry received PUBLISH (d0, q0, r0, m0, 'sensors/node2/hum', ... (20 bytes)) sensor humidity = 33 Client mosqsub|25451-raspberry received PUBLISH (d0, q0, r0, m0, 'sensors/node2/temp', ... (23 bytes)) sensor temperature = 26 Client mosqsub|25451-raspberry received PUBLISH (d0, q0, r0, m0, 'sensors/node2/co2', ... (10 bytes)) CO2 = 1068 Client mosqsub|25451-raspberry received PUBLISH (d0, q0, r0, m0, 'sensors/node2/tvoc', ... (10 bytes)) TVOC = 101

6. Настройка Grafana

В графическом интерфейсе настраиваем Data Source.

Борьба за открытое окно: технологии победили

Далее добавляем интересующие нас дашборды по такому примеру (или можно на один график, но мне показалось неудобным). График рваный, так как проводились разные опыты над ESP.

Борьба за открытое окно: технологии победили
Борьба за открытое окно: технологии победили

В результате после настройки я получил такое решение, доступное мне откуда угодно:

Борьба за открытое окно: технологии победили

Скетч

Скетч очень тщательно закомментирован, чтобы по-максимуму не вызывать вопросов.

Датчики типа DHT имеют свойство с платами ESP периодически выкидывать максимальные для себя значения (2147483647), поэтому данные по температуре и влажности более 100 просто не передаем.

Некоторым помогает перепрошивка ESP в другую версию прошивки — мне не помогло. Опытным путем установлено, что опрос раз в десять секунд приводит к минимальному числу таких всплесков.

Инструкция по использованию

Крайняя кнопка на плате — перезагрузка.

Кнопка между датчиком и другой кнопкой имеет две функции:

  • Удерживаем и нажимаем перезагрузку — перевод в режим программирования платы по кабелю.
  • Один раз нажать во время нормальной работы платы — перевод в режим конфигурирования Wi-Fi через портал.

Если зайти на IP-адрес платы во время нормальной работы, то ответ должен быть такой: Not found: /

Веб-команды (на примере адреса платы 192.168.0.100):

  • http://192.168.0.100/restart — перезагрузка платы.
  • http://192.168.0.100/reburn — перевод платы в режим прошивки по воздуху. Для прошивки в IDE нужно выбрать порт:
Борьба за открытое окно: технологии победили

После нажатия upload ввести пароль, который ранее задан в зоне конфигурирования в скетче.

  • http://192.168.0.100/wifi — перевод платы в режим настройки Wi-Fi. Чтобы действие выполнилось наверняка, забиваем настройки платы сетью SSID: 0000, passw: 0000.

Послесловие

По результатам проекта окна в квартире теперь стабильно открыты, так как даже в детской при двух маленьких детях при закрытых окнах за три–четыре часа уровень СО2 уползает с 450–490 до 750 ppm. Тема наконец закрыта :)

P.S.

Дальнейшим развитием проекта планируется с управление приточной вентиляцией с HEPA-фильтрами, дабы автоматизировать процесс. Управление пластиковыми окнами рассматривалось, но так как окна и откидные и распашные и их нужно иногда мыть — признано для себя решением неверным.

Единственная реальная у нас проблема: уровень концентрации пыльцы в воздухе (к сожалению, в доме есть аллергики). В остальном — не существенно, хотя живём в пяти домах от Ленинского проспекта. Просто этаж низкий, окна во двор, в деревья между домами :)

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

Третьей частью планируется освоить 3D-моделирование и напечатать корпус. Но тут пока возможны нюансы (ко мне доехали остальные датчики: MH-Z19 и BME280), и для определения точности, возможно, я сделаю еще одну близкую модель, но с ними. Тогда корпус нужно будет сделать универсальным.

6868
66 комментариев

Комментарий недоступен

39
Ответить

Ну - всё по фэншую! Arduino, макетка, Rpi, linux command line, скрипты, конфиги, grafana, вот это всё)) 

1
Ответить

Комментарий недоступен

12
Ответить

Статья скорее для хабра. С точки зрения технической составляющей - здорово, подробно расписано.

Но с точки зрения вопроса «открывать или не открывать окна» - раскрыт только вопрос об уровне CO2.

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

1. Сильного шума снаружи.
2. Холода.
3. Сквозняк.

Думаю можно еще несколько причин найти, если вспомнить. Поэтому зачастую лучше чуть-чуть подышать лишним СО2, но спать в тишине и тепле, чтобы с утра проснуться выспавшимся и непростуженым, что довольно таки полезно :)

8
Ответить

Проблему концентрации CO2 без проблем с шумом и пылью решает такой прибор как бризер. Например, Tion S3. Я пользую, мне нравится.

4
Ответить

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

2
Ответить