DIY Zigbee датчик СO2 для вашего Умного дома

Приветствую всех читателей Habr. Меня зовут Андрей, примерно около 5 лет я осознано занимаюсь DIY разработкой электронных устройств для своего «Умного дома», да и не только для своего. Сегодня хочу рассказать об одном из моих DIY проектов, небольшом устройстве для контроля уровня углекислого газа. Проект называется EFEKTA iAQ, это датчик с круглым IPS TFT дисплеем, сенсором CO2, работающий на протоколе Zigbee.
4wurolikfcpchlopnpfv7hinw3e.jpeg
На данный момент существуют еще две версии этого устройства. iAQ2 — версия с дополнительным сенсором атмосферного давления, iAQ3 — версия с дополнительным сенсором VOC (летучие органические соединения). В этой статье я расскажу как я разрабатывал этот проект, как появлялись новые версии, как развивалась и дополнялась функциональность, с какими трудностями пришлось столкнуться в процессе взросления этого проекта.

Как я пришел к идее создать этот проект, сейчас уже точно не вспомню. Но началось всё 5 ноября 2021 года, это зафиксировано в одном телеграм чате по разработке zigbee устройств.

k1kzuql9dwcbbjf83u6jqhosnii.png

В то время я только-только начал пробовать свои силы в программировании не на Ардуино. В какой-то момент я решил попробовать запустить небольшой круглый IPS TFT дисплей на СС2530. Несколько дней потратив на код драйвера, я его запустил. Передача данных по SPI была очень медленная, дисплей очень долго обновлялся, но это была победа, я был счастлив:), кто-то из читающих этот текст меня точно поймет.

Запустил и положил на полку, так как на тот момент нужно было закончить несколько других DIY проектов на электронных чернилах. Но уже в начале 2022 года, я получил несколько сенсоров CO2 с али экспресс. Они меня заинтересовали своими размерами, наличием интерфейса I2C, более интересными возможностями в сравнении с сенсорами sensair s8 и mh-z19.
mdnopindbtwgsg5atgq9aoxqpqs.jpeg

Сделал драйвер (за основу взял generic embedded driver от Sensirion), запустил. Получается примерно в это время и появилась идея сделать DIY проект zigbee датчика СO2 с дисплеем.

Думая на тему как должен выглядеть датчик, я принял решение что он будет настольным. Начал проект с разработки платы, но держа в голове как примерно должен выглядеть готовый датчик. Разрабатывать корпус датчика решил делать уже после того как будет готовое, оттестированное железо и рабочий софт.
Основные требования к датчику у меня были следующими:

  • Передача данных с сенсоров в умный дом по протоколу zigbee
  • Конфигурация датчика внешними командами, через zigbee сеть.
  • Возможность привязки внешнего датчика температуры и влажности
  • Вывод данных на дисплей.
  • Питание 5В через USB разъем

В первой ревизии железа для проекта, помимо сенсора scd40 (CO2) были добавлены сенсоры bh1750(освещенность) и shtc3(температуры и влажность). Дисплей ER-TFT1.28–1 оснащен внешне управляемой LED подсветкой. В проекте я реализовал управление подсветкой через ШИМ.

qtogwt-sjgwnnbxhckcic0vg_ii.png

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

Написав ПО для проверки железа первой версии прототипа, я начал тесты. Стало сразу понятно что внешний сенсор температуры подвержен нагреву и от подсветки дисплея и от нагрева встроенного в дисплее однокристального SOC-драйвера GC9A01 и от радиомодуля E18-MS1-PCB (CC2530).

В следующей ревизии я перенес сенсор температуры и влажности воздуха в другое место на плате, но и это не принесло результата. Было решено использовать в ПО встроенный в scd40 сенсор, так как если делать программную компенсацию, то логичнее это делать используя сенсор СO2 с встроенным сенсором температуры и влажности. Тем не менее так и оставил возможность установки сенсора shtc3 на плате, место позволяло.

На следующем этапе разработки я стал реализовывать вывод графики на дисплей. Основными пунктами этой задачи были вывод значений СО2, температуры и влажности с внутреннего сенсора, вывод температуры и влажности с внешнего привязываемого датчика, вывод графика на основе данных СO2. Ну и конечно нужно было решать вопрос со скоростью обновления дисплея. Медленное обновление дисплея в основном связано со способом передачи данных, отправляем байт, ожидаем успешной передачи, отправляем следующий. Переписал драйвер, сделал передачу всего массива байтов за раз. Это сработало, ускорение обновления на порядки, стабильность передачи.

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

На заключительном этапе разработки ПО нужно было реализовать работу устройства в сети zigbee, передачу данных, команд, получение данных, команд, биндинг. Тут я столкнулся с проблемой некорректной конвертации данных СО2 в существующей в zigbee2mqtt реализации. Видимо до моего проекта никто не делал свой zigbee СO2 датчик с дисплеем. Проблема выглядела следующим образом, на дисплее одно значение в Home Asistant почти оно, просто меньше на единицу. Проблема обнаружилась в обработчике кластера СО2, в этой строке:

return {co2: Math.floor(msg.data.measuredValue * 1000000)};


Используется встроенная функция с округлением вниз.

Поэтому пришлось добавить свой обработчик непосредственно в конвертер.


co2: {
        cluster: 'msCO2',
        type: ['attributeReport', 'readResponse'],
        convert: (model, msg, publish, options, meta) => {
			if (msg.data.hasOwnProperty('measuredValue')) {
				return {co2: Math.round(msg.data.measuredValue * 1000000)};
			}
        },
    },

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

С получением времени через zigbee сеть тоже были нюансы. В первой версии конвертера, которой сейчас находится в основной базе zigbee2mqtt время отправлялось при джойне устройства, на этапе конфигурации. Это часто могло не срабатывать, поэтому время так же можно было отправить в ручном режиме, нажав на кнопку на закладке свойства (exposes), на странице устройства, либо отправит команду в mqtt топик.


const time = Math.round(((new Date()).getTime() - constants.OneJanuary2000) / 1000 + ((new Date())
                .getTimezoneOffset() * -1) * 60);
            const values = {time: time};
            endpoint.write('genTime', values);

e.enum('local_time', ea.STATE_SET, ['set']).withDescription('Set date and time'),

В следующий версии время отправлялось уже асинхронно в ответ на запрос от устройства, с синхронизацией через каждые 6 часов.


async function onEventSetLocalTime(type, data, device) {
 
    if (data.type === 'attributeReport' && data.cluster === 'genTime') {
	    try {	
		    const endpoint = device.getEndpoint(1);
		    const time = Math.round((((new Date()).getTime() - constants.OneJanuary2000) / 1000) + (((new Date()).getTimezoneOffset() * -1) * 60));
            await endpoint.write('genTime', {time: time});
        }catch (error) {
            
        }
    }
}

onEvent: onEventSetLocalTime,

Еще одна проблема всплыла при реализации функционала биндинга, с кластером температуры всё было хорошо, а вот по кластеру влажности воздуха привязку сделать не получалось. Начал разбираться почему и от чего так и выяснилось, что zigbee2mqtt ничего из типа кластеров Measurement and Sensing Clusters не поддерживает. На самом деле много чего не поддерживает, но тогда меня интересовали именно Measurement and Sensing Clusters. Почитал, как это реализовано для кластера температуры, сделал аналогично локально на своем сервере для кластера влажности и освещенности. Всё получилось.

Как только все проблемы были решены, добавил конвертер в zigbee2mqtt, добавил описание датчика, сделал pull request на githab проекта zigbee2mqtt по поводу поддержки нужных мне кластеров для биндинга.

Пришло время разрабатывать корпус под железо проекта. Модель разрабатывалась в Солид Воркс. Тут никаких особых проблем не было, одно удовольствие от работы. Корпуса печатались на бытовом fdm принтере и после шлифовались и полировались.

op32bkgyfbhjjy5oopixvajmutm.jpeg

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

Проблема решилась в Китае, к тому времени на сайте JLCPCB уже была доступна услуга 3D печати. Изучив предложения, остановил свой выбор на печати по технологии MJF и SLS, это печать лазером по полимеру в виде порошка. Получив первую партию корпусов, убедился, что печать MJF это именно то что мне нужно.
Корпус напечатанный по технологии MJF плотный, тяжелый, имеет матовый блеск, точность печати высокая, прочность очень высокая. Корпус напечатанный по технологии SLS более легкий, немного пористый, точность печати очень высокая, по прочности уступает MJF.

Было сделано 2 варианта корпуса, первый вариант со скруглениями граней, второй вариант с фасками на гранях. В дальнейшем остановился на варианте с фасками.

rw45j38zox-stc55m6mf9h7t4xc.jpeg

Характеристики первой версии и функционал получившегося датчика СO2.

  • Протокол: Zigbee 3.0
  • Тип устройства: EndDevice
  • Мощность передатчика: 4.5 dBm
  • Дисплей: IPS TFT, 1.28 дюйма, разрешение 240×240
  • Точность измерения CO2 ± 40 ppm 5%
  • Температура, диапазон и точность: -10°C ~ 60°C, ± 0,8°C в диапазоне 15°C — 35°C
  • Влажность, диапазон и точность: 0 — 100% относительной влажности (без конденсации), ± 6% в диапазоне 15°C — 35°C и 20%RH — 65%RH
  • Освещенность, диапазон: 1 — 65535 lx
  • Питание: USB тип C, 5В

В следующей ревизии в проект iAQ был добавлен функционал роутера сети zigbee, и еще позднее поддержка радиомодулей с усилителем, 20dBm. Добавлена поддержка работы с «умными» блоками питания. Перенесен разъем usb на заднюю часть корпуса.
jmb2dlem3ls7q0xh7nsm_g_fsha.jpeg
Добавлен функционал газостата. Можно привязать этот датчик напрямую к любому zigbee реле или розетке и датчик будет управлять этим реле или розеткой по настроенным порогам углекислого газа. Помните я выше писал про оптимизицию :).

Функционал:

  1. Датчик осуществляет мониторинг уровня СO2 газа, температуры, влажности воздуха, освещенности в реальном времени.
  2. Удаленный мониторинг (Zigbee), к датчику EFEKTA iAQ можно подключить дополнительный внешний датчик температуры, размещенный например на улице.
  3. Прямое управление исполнительными устройствами (реле, розетки) по настраиваемым пороговым значениям углекислого газа.

Пункты 2 и 3 реализуется через биндинг. Это прямая работа между привязанными устройствами. Даже если координатор сети отключить или отключить сервер умного дома, связанные устройства продолжат совместную работу.

Работа в сети zigbee, добавление/удаление устройства.

Только для самой первая версии устройства в проект zigbee2mqtt добавлен конвертер в основную базу. Для последующих версий необходимо использовать внешний конвертер. Все конвертеры доступны на моем гитхаб. Когда-нибудь я соберусь и добавлю их в основную базу.

Для установки внешнего конвертера необходимо скачать его на моем гитхабе. Для пользователей homeassistant самый простой способ добавить конвертер, это загрузка через установленный аддон File Editor

Если вы используете zigbee2mqtt отдельно, то для загрузки конвертера для устройства на сервер есть несколько вариантов. Первый вариант это загрузка через ftp клиент. Это может быть любой клиент, например filezilla

Для подключения к серверу необходимо ввести ip адрес, логин и пароль в соответствующие поля в программе и нажать на кнопку — «быстрое подключение»

uphx70s-fcy03uzkmeym-pocabk.png.

Если вы не используете HassOs, то путь к нужной папке будет /opt/zigbee2mqtt/data, если вы используете HassOs то путь к нужной папке будет /config/zigbee2mqtt

Перейдя в нужную папку просто перетащите туда конвертер из левого окна программы в правое.

Если вы используете Home Assistant, то вы можете установить плагин Самба, тогда у вас появится сетевая папка в которую вы можете просто перетащить нужный файл через стандартный проводник в Windows. Инструкция по установке Самба находится здесь.

Добавление информации о конвертере.

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

Далее найти подраздел — внешние конвертеры и перейти в него.

zftjzoqhijtbumgegpxdjkloc_k.png

Далее нужно спустится вниз страницы и нажать на кнопку»+», появится новое окно для ввода информации, туда нужно добавить название вашего внешнего конвертера с расширением, например EFEKTA_iAQ.js. После этого нажать на кнопку 'Submit» и далее на кнопку — «Перезагрузить» вверху страницы.

ocjmbtnpndow8pw5eym8c0dizqi.png

Добавление картинки по ссылке

Если у вас есть ссылка на изображение устройства, вам необходимо перейти на страницу устройства к которому вы хотите применить изображение, далее нужно перейти на вкладку — «Настройки»

kupo0mnkubaifqiyj0187kkl9wm.png

На странице настройки переместитесь вниз страницы до пункта «Icon» и введите в поле адрес на изображение. Затем нажмите кнопку«Submit»

q-mds5orzc-ugwwhayokrlybmg0.png

Для добавления устройства в zigbee сеть, необходимо включить режим джойна на шлюзе (на примере zigbee2mqtt)

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

Для выхода из сети zigbee необходимо нажать на кнопку «прямо» и удерживать ее в течение 10 секунд, датчик отправит запрос на выход из сети, сотрет у себя все данные сети из памяти и перезагрузится.

Oриентироваться в каком статусе сейчас устройство можно по логотипу zigbee на дисплее, если логотип серый то устройство не добавлено в сеть, если логотип zigbee красный то устройство работает в сети.

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

Кнопка «Вверх» включает или отключает адаптивную подсветку дисплея в зависимости от освещённости в помещении. В выключенном состоянии устанавливается средняя яркость подсветки дисплея.

Кнопка «Вниз» переключает интервал построения графика, заполнение за 1 час или заполнение за 24 часа.

Передаваемые в zigbee2mqtt данные:

co2
Уровень углекислого газа

temperature
Температура с встроенного сенсора

humidity
Влажность воздуха с встроенного сенсора

illuminance_lux
Освещенность в люксах с встроенного сенсора

illuminance
Необработанные данные освещенности с встроенного сенсора

iwdnin_posl5xlncfnup_h3olsc.png

Конфигурационные атрибуты:

auto_brightness
Включение/отключение адаптивной подсветки дисплея

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

long_chart_period
Переключение интервала построения графика, заполнение за 1 час или заполнение за 24 часа.

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

temperature_offset
Подстройка температурной компенсации

humidity_offset
Подстройка компенсации влажности воздуха

forced_recalibration
Форсированная ручная калибровка №1. Это функционал сенсора SCD40 заложенный производителем, который реализован. Калибровка осуществляется на свежем воздухе, необходимо оставить датчик на чистом воздухе на 15 минут, по истечении этого времени отправить команду. Время калибровки примерно 5 секунд, после завершения калибровки точке соответствующей чистому воздуху будет задано значение в 450ppm. Датчик отправит команду «выключено» по завершению калибровки

manual_forced_recalibration
Форсированная ручная калибровка №2. Это функционал сенсора SCD40 заложенный производителем, который реализован. Калибровка осуществляется по показаниям другого датчика СO2, показаниям которого вы доверяете. Необходимо оставить оба датчика рядом, наличие свежего воздуха не обязательное условие. По истечении этого времени, необходимо данные с датчика которому вы доверяете отправить на калибруемый датчик. Датчик вычтет разницу и будет в дальнейшем учитывать ее.

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

factory_reset_co2
Сброс сенсора углекислого газа к заводским настройкам. Это функционал сенсора SCD40 заложенный производителем, который реализован.

enable_gas
Включение функционала газостата. Управление реле к которому привязан датчик. Для работы этого функционала необходимо сделать привязку к исполнительному устройству (реле, розетки)

invert_logic_gas
Инвертирует логику работы управления исполнительными устройствами. Пример. Если опция отключена, при превышение верхнего заданного порога углекислого газа датчик отправит команду «Включить» на привязанное реле. Если опция включена, при превышение верхнего заданного порога углекислого газа датчик отправит команду «Выключить» на привязанное реле.

high_gas
Верхний порог углекислого газа

low_gas
Нижний порог углекислого газа

Привязка внешнего датчика температуры.

Привязка осуществляется на стороне внешнего датчика температуры. Для привязки (биндинг) датчика температуры к датчику iAQ, для прямой передачи данных необходимо в вэб интерфейсе zigbee2mqtt перейти на страницу датчика температуры.

jvdfgwtp5bdpdczrmmdore6ernq.png

Далее нужно перейти на вкладку «Связь» и заполнить поля данных для привязки

bakarcw8-bwmrqg8dykwsulnclu.png

В первом поле слева выбрать »1», в следующем поле, в выпадающем списке выбрать датчик к которому необходимо сделать приыязку. Правее выбрать кластеры (поставить галочки) по температуре и/или влажности воздуха. Потом еще правее нужно нажать на кнопку «Связать».

После нажатия на кнопку в вэб интерфейсе, на датчике нужно нажать кнопку на корпусе. Датчик проснется и получит данные о привязке. В вэб интерфейсе з2 м должно всплыть сообщение об удачной привязке (зеленое).

Привязка датчика iAQ к исполнительному устройству (реле, розетки). Осуществляется на стороне датчика iAQ. Для привязки (биндинг) датчика iAQ к исполнительному устройству, для прямой передачи данных необходимо в веб интерфейсе zigbee2mqtt перейти на страницу датчика iAQ.

В первом поле слева выбрать »1», в следующем поле, в выпадающем списке выбрать исполнительное устройство к которому необходимо сделать привязку. Правее выбрать кластер OnOff. Потом еще правее нужно нажать на кнопку «Связать».

Весной этого года я продолжил расширять функционал этого проекта, на свет появились версии iAQ2 и iAQ3.

Версия iAQ2.

9aa2txjpoinxqcoqqbftyahfgjs.gif

Данная версия получила дополнительный сенсор атмосферного давления. Переработан дизайн вывода графики на дисплей. Это версия с белым фоном. На дисплее выводится помимо уровня углекислого газа, еще и атмосферное давление, строится свой график атмосферного давления. На основе скорости изменения атмосферного давления рассчитываться простой прогноз погоды на ближайшие часы, облачно, дождь, ясно. Выводится на дисплей в виде иконок. Учитывается время суток, в дневное время солнце, в ночное время луна.

Из этой версии удален конфигурационный атрибут set_altitude, с сенсора bmp280 в реальном времени передаются данные об атмосферном давлении напрямую в сенсор scd40. Это все отличия этой версии.

Версия iAQ3.

m5d33u64ge5toiyzqnffwugq-qg.jpeg

Эта версия получила дополнительный сенсор VOC (летучая органика), сенсор sgp40. Эта самая интересная для меня версия, сенсор sgp40 имеет очень низкое потребление, но из-за этого производителю пришлось очень упростить встроенный контроллер, сенсор отдает только не обработанное значение. Для расчета индекса VOC производитель предоставляет отдельную библиотеку с алгоритмом расчета индекса VOC. Поэтому в расчет индекса принимает участие и основной SOC данного устройства. Нужно периодически с интервалом в 1 секунду (рекомендовано для лучшей чувствительности, но не обязательно) получить данные с сенсора sgp40 и отдавать их в библиотеку с алгоритмом который рассчитывает текущий уровень VOC. А это дополнительные байты кода, здравствуй снова оптимизация.

В этой версии датчика на дисплей помимо уровня СO2 выводятся данные индекса VOC, диапазон 0–500. Строится свой график. В этой версии отсутствует вывод температуры и влажности воздуха на дисплей. Если бы было принято решение делать вывод данных по температуре и влажности воздуха с внутреннего сенсора, то пришлось бы делать либо меньше шрифты для вывода основных данных, СО2 и VOC, либо отказываться от возможности привязать внешний датчик, а этого мне совсем не хотелось делать. В принципе получилась полноценная версия про качество воздуха с возможностью привязать внешний датчик температуры, отображать данные с внешнего датчика на дисплее. Но данные с встроенного в SCD40 сенсора температуры и влажности тем не менее используются для расчета абсолютной влажности. Эти данные в реальном времени передаются в сенсор sgp40, эти данные необходимы для более точного расчета необработанных данных.

VOC, как и TVOC не описанный тип данных в ZCL, поэтому реализацию работы с этими данными осуществляет разработчик. Не стал это делать через кастомный кластер, передаю эти данные через аналоговый кластер. Отправляется необработанное значение и рассчитанный индекс VOC.

Что дальше?
Думаю что работа над этим моим DIY проектом не закончена, скорее всего функционал будет и дальше расширяться. Обратная связь работает отлично. Совсем недавно в проект была добавлена возможность с помощью внешней командой изменять ротацию вывода графики на дисплей. Это был многократный запрос от владельцев этого датчика, которым понадобилось закреплять датчик в перевернутом виде.
c1kqasgn_8i0onaliqsbmbnl4g8.jpeg
9fr7ubczld_xwecxdjqatkpc1ym.jpeg

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

Про DIY

Давно уже собирался написать на Хабре об этом проекте, да и не только об этом, с момента публикации моей крайней статьи прошел почти год. Уже сделано много новых DIY проектов. Но каждый раз что-то отвлекало. Увидел в рассылке информацию о конкурсе DIY, это хороший повод, у меня же все проекты про электронику и DIY. Очень здорово что есть какое-то движение в теме DIY. Наши DIY мейкеры вниманием внутри страны не избалованы. Очень надеюсь что это только начало. Надеюсь появиться у нас свои instructables.com, hackaday.com и прочие.

Всех кого заинтересовали мои diy устройства, кто хочет быть в курсе моих новых проектов, кто строит свой умный дом, увлекается разработкой DIY устройств, я приглашаю в свой телеграм канал DIY_DEV

Посмотреть мои наиболее интересные diy устройства можно тут

Почитать отзывы владельцев можно тут

© Habrahabr.ru