- BrainTools - https://www.braintools.ru -
Всем привет и наступающими! Захотелось тут сделать пару гаджетов для друзей в их умные дома. И что-то вдруг подумалось – а что они все скучные такие? Давайте же сегодня сделаем датчик в таком форм-факторе, в котором точно не купишь в магазине, будет отличный подарок на Новый Год или Рождество. Ну и ещё что-бы подарить было не стыдно.
Сразу оговорюсь, прибор конечно же не аналоговый, а полностью цифровой. “Аналоговый” тут только дизайн, внутри же ничего сверхъестественного на данный момент времени. Используется народный датчик SenseAir S8 и инструментальный шаговый двигатель для автомобильных приборок.

Дисклеймер: электроника у меня только хобби, так что могут быть неточности и ошибки [1]. Пишите комментарии, поправим. Так же являюсь луноходом и живу в Германии, что приводит к некоторым особенностям данного поста: цены в евро + в процессе разработки мы будем использовать только свободный софт под Linux.

Полное создание данного малыша заняло примерно полтора месяца. Обзор скомпилирован из фоток разного времени, когда я ещё находился в активном поиске – на фотографиях могут быть раз��ые версии приборов, прототипов, могут быть отличия в деталях, косяки и видна пыль. Помощь AI в данной статье не использовалась – соответственно могут быть опечатки или странная лексика. Просьба отнестись с пониманием :)

Всё опубликованное здесь и в репозитории лицензировано под GNU GPLv3 [2].
Наш прибор задумывался как девайс, который можно подарить и пользователь мог сам с ним справиться – без использования программатора, при этом должна быть интеграция в популярные системы Умный Дом (но как опция). Второе ключевое трбование: прибор должен быть технологичным, что-бы его мог собрать и подарить кто-то другой, кому он понравился, при этом не имея геморроя типа поиска советских радиоламп или выпиливания бронзы лобзиком.
Для начала формализуем требования:
Измерение уровня CO2 с хорошей точностью и отзывчивостью;
Измерение стандартных климатических параметров: температура, влажность и давление;
Передача всего этого намеренного добра в умный дом;
Отображение текущего уровня на аналоговом дисплее прибора (стрелкой);
Прибор должен иметь подсветку;
Веб-интерфейс с отображением статуса, настройками, обновлением прошивки и проч – это вожно что-бы пользователь мог с ним разобраться сам;
Желательно что бы это не выглядело как страшное DIY;
Устройство должно быть легкое в повторении [3] и не содержать уникальных компонентов; Компоненты должны быть доступны по всему миру;
Изначально я хотел сделать именно “авиационный прибор”, но в процессе у меня быстро попросили сделать так же ретро-версию. Соответственно, прибор у нас будет с опциями комплектации, каждая может быть выбрана независимо:
Стиль шкалы: “авиационный” и “ретро”, так же ставится соответствующая стрелка;
Подсветка: без подсветки, “ламповая” и “RGB”;
Датчики: CO₂, климата – могут быть установлены в любой комбинации, так же вообще без датчиков – в этом случае прибор будет управляться только внешней командой, например от системны Умный Дом;
Требования к конкретно прошивке: она должна быть с документацией, коммьюнити и обновлениями; Должно быть минимум велосипедов и нормальная (для обычного человека) юзабилити; Итеграция в умный дом без плясок с бубном и прошивки;
Мне не нравится, что многие хорошие девайсы зачастую сложно найти. Часто никто не париться с названием и делает что-то типа “Advanced Co2 home sensor module”, которое невозможно найти или понять – где что.
Придумал название для серии датчиков – Galoped. Название не несёт никакого смысла, просто стоял курил на улице и подумал – надо что-то такое, что было в стиле Икеи, состоящие из слов “Галоперидол” и “Педиатр”. Так и придумал.
Потом о таком названии пожалел, т.к. постоянно спрашивали – что это значит и вообще какой скрытый смысл, наверное можно было-бы придумать что-то более очевидное. Но что есть – то есть.
Небольшая предыстория – сделать “аналоговый” показометр у меня была уже очень давно. Я где-то в году 2012 купил себе двух-стрелочный тахометр от турбовинтового самолёта, который я хотел “оживить”. Естественно никто ничего за 10 лет не оживил :) Но идея никуда не девалась, прибор переехал вместе со мной в Германию в 2017 году и до сих пор стоит у меня на столе:

Данный девайс оживить сложно, ему нужно два трёхфазных сигнала, которые вращают контрольные диски внутри, которые через магнитную муфту отклоняют стрелки. Девайс большой и наверняка шумный. Но в принципе это возможно, он может указывать что-то в процентах. В данном случае лучше было-бы воспользоваться современными технологиями и сделать всё с нуля – у меня вроде как всё есть для этого: старенький 3D принтер Ender-3 (ещё самая первая модель) и руки чешутся.
Конечно на рынке есть всякие-разные показометры, например на Алишке полно приборов типа такого:


, но они все сделаны для стритсракеров и мало подходят для разумного примнения. До того момента как я понял на чём подобные приборы работают – думал заказать на пробу, разобрать и посмотреть. Но это не понадобилось, так как оказалось что сделать с нуля в общем будет проще.
Так же рассматривал вопрос использования аналогового вольт- или амперметра. Они показывают что надо, сигнал легко намутить через DAC. Тут я застрял на том что уголы поворота из коробки слишком маленькие – из коробки это обычно около 90° для большинства моделей. Использование реальных авиационных вольтметров или как-то “специальных” у нас тоже отпадает (см требования по технологичности). Кроме того, я думал что для вольтметра будет куда сложнее сделать красивую стрелку и всё остальное, так как они обычно очень нежные создания, ведь управялть мы сможем только низковольтным напрямую. Всякие вольтметры от ВАЗ или советские приборы тоже отпадают, т.к. являются экзотикой за пределами СНГ.
В итоге решил делать по технологии автомобильных приборок. Они же как-то крутят там стрелками? У них там и всё красиво и угол большой. Значит будем смотреть в этом направлении.
Следующий вопрос – а что, собственно, показывать? Можно отображать разные метрики умного дома, с работы или домашней инфраструктуры, но такое слишком специфично. В итоге выбираем уровень CO2 – подходит отлично: таких исполнений датчиков просто нет (я не нашёл ни одного), данная метрика медленно меняется, автономна (можно измерять на месте), кроме того полезна – можно посмотреть когда пора-бы проветрить. У нас в офисе кстати постоянно спорят – надо ли открывать окна или нет, и такой девайс может разрешить подобные спорты путём прямого инструментального измерения. Я не буду в сотый раз расписывать – что такое измерение CO₂ и если тема для вас непонятна – почитать можно например тут [4]. В качестве измерителя выбираем SenseAir S8, как самый авторитетный из народных. Почитать про него и сравнение с другими можно здесь [5]. Ну и конечно же данный прибор может показывать что угодно, строго говоря CO₂ – только лишь пример, что-бы получить законченное устройство.
Когда пришла в голову идея создания нового прибора – казалось что всё просто, берём шаговый моторчик (серву?) и приводим в движение одним из миллиона способов. Но быстро стало понятно что серва не подходит – шумная, неточная и имеет слишком малый угол поворота.
Довольно быстро я нагуглил что существует прям народный шаговый двигатель X27-168, точнее – целое их семейство. Довольно быстро нашёл пост замечательного парня guy.carpenter [6] с экспериментами с драйверами данных двигателей. Данные моторчики широко применяются в различных девайсах, например популярны у симмеров [7]. Так же, оказалось что их существует целое большое семейство [8] – например есть варианты с 2-мя стрелками или датчиками нуля (варианты для часов). Эти и подобные им двигатели ставятся в практически все автомобили и широко доступны повсюду. Используются например в приборках OPEL или ВАЗ (например в ВАЗ-2110).
Существуют целый семейство и множественные клоны специальных драйверов, которые реализуют алгоритм микростеппинга: когда одна команда от контроллера поворачивает привод не на одно деление (обычно это 1/3°), а на 1/12°. Так же в даташите написано что такой способ является предпочтительным – это повышает точность и уменьшает шум привода. С большим трудом отыскал английский даташит на VID6608 [9] и залил его в репу, потом заказал обойму из 10 штук.
Для начала заказал сразу шесть моторчиков (комплект обошелся в 12€), они приехали первыми. Драйверов у меня ешё не было, и я проверил их через рандомные H-Bridge драйвера, какие нашлись в хламе. В принципе работает, но выглядит очень так себе – точность плохая, шум сильный. Стрелка двигается неестественно. Да, в принципе уже можно сказать – работает и так, но на приборку авто пока что мало похоже.

Пока занимался другими делами – приехали драйвера (и сразу взял breakout pcb для SOP-28). Первый прототип собрал на макетных платах и приступил к тестированию. В принципе, что-то уже работало, но тут было первое разочарование: собрав прошивку на коленке в platform.io с библиотекой SwitecX25 [10] от уважаемого guy.carpenter наткнулся на кучу багов и проблем. Пришлось их на ходу править, что довольно сильно затормозило процесс.

Начнём с того что библиотека под ESP32 вообще не работает – там имеется неосторожное использование char, который под ESP32 имеет тип unsigned и попадаем на целочисленное переполнение. Причём конкретно этот баг поправили, но версию не выкатили – если тащить библиотеку по зависимостям в platform.io, то получим баги. Потыкался логическим анализатором и нашёл ещё проблемы – например библиотека не парится по поводу необходимых тайм-аутов, которые требует даташит на VID6608.
В какой-то момент я пришёл к выводу что проще написать свою с нуля, чем разбираться в оригинальной – автор явно потыкался немного, а потом потерял к теме интерес [11]. Пулл-риквесты висят уже давно. Но всё равно – огромное спасибо автору! Он положил начало для реализации.
С самого начала задумал использовать прошивку Tasmota [12] как основу для девайса, т.к. содержит в себе уже всё что нужно и широко известна. Но тут меня ждало следующее разочарование, что в Tasmota имеется поддержка только H-Bridge драйверов для “обычных” шаговых двигателей.
Итого, формируем наш с вами план – как же нам собрать всё вместе?
Добавляем поддержку инструментальных шаговых двигателей в Tasmota
Находим подходящий корпус-донор (часы-будильник)
Проектируем и заказываем печатную плату
Проектируем и печатаем корпусные детали
Придумываем подсветку как-нибудь
Собираем это вместе и придумываем че-нибудь, что-бы это всё вместе заработало :-)
Итак, раз решили – надо делать :-) Клонируем проект и погружаемся в документацию. На деле оказалось всё непросто: документация в принципе есть, но довольно отрывочная, пришлось выбрать парочку простых драйверов и использовать как живой пример. Посмотрел как сделано поддержка других библиотек – они просто закоммичены напрямую прямо в репозиторий, не как суб-модули или зависимости. В этот момент я решил что проще написать свою библиотеку и добавить её туда же + драйвер.
Про разработку особо не буду распинаться, сделано всё по образу и подобию уже существующей SwitecX25 – только без багов)) Отличия от старой:
Поддержка управления только через драйвер VID6608 и им подобным – прямое управление бессмысленно, кмк
Поддержка настроек кривых ускорения
Поддержка выставления нуля из сохранённого положения
Меньше вычислений на шаг, проще алгоритм управления
Более точное соответствие даташиту, старая не заботилась о некоторых таймингах, которые требует даташит
В итоге появилась новая библиотека: arduino-vid6608 [13].
Библиотека использует более простой алгоритм расчёта ускорения, где имеется статический массив расстояние-задержка, и до первой 1/2 пути происходит сравнение расстояния и рост скорости, на второй половине происходит замедление. Фу��кции задания положения асинхронные, функция vid6608::loop() чувствительна к точности вызова функции обновления.
На видео пример работы алгоритма – прибор просто показывает случайные значения. В итоге оставил так – как по мне получилось достаточно реалистично при минимуме вычислений.
В данном режиме инженерный образец отработал чуть больше двух суток без остановки – пропусков шагов и дрейфа показаний я не обнаружил.
Далее выбираем следующий свободный Driver-ID в Тасмоте и занимаем его. Мне достался номер 92, в результате чего появился на свет драйвер xdrv_92_vid6608 [14]. Обратите внимание [15] что реализация именно в виде драйвера общего назначения, изначально я думал сделать как display [16], но у данного класса драйвера нет достаточно точного тайминга для обеспечения плавного движения стрелки.
Сделав первую версию драйвера – сразу же сталкиваемся с двумя новым проблемами:
Движение стрелки очень медленное – из-за того что функция цикла не вызывается в Tasmota с достаточным разрешением
Функция выставления в ноль при запуске вызывает срабатывание Watchdog в некоторых случаях – из-за блокировки главного потока на слишком большое время
Решение первой проблемы очевидно: для ESP32 в Tasmota используется FreeRTOS из пакета разработки ESP-IDF, а значит что мы можем использовать примитивы и функции операционной системы реального времени. Собственно решение простое как полено: мы просто заведём обработку обновления в отдельный поток, который по-умолчанию уйдёт на второе ядро (мы будем ставить на плату двух-ядерную версию ESP32):
// Start background RTOS thread -> required for precision timing
xTaskCreate(
VID6608XvTask, /* Function to implement the task */
"VID6608XvTask", /* Name of the task */
1024, /* Stack size in words */
NULL, /* Task input parameter */
0, /* Priority of the task, lowest */
NULL /* Task handle. */
);
...
void VID6608XvTask(void *) {
while(true) {
...
driver->loop();
...
/*
If we dont need to move any -> go sleep.
This will delay next move begin up to 500ms, but freeds up CPU a lot.
*/
if (!needToMove) {
vTaskDelay(500 / portTICK_PERIOD_MS);
}
}
}
Хорошо, но данный трюк сработает только для ESP32, а для старой ESP8266 данное решение не канает. В итоге оставил как есть – я не планировал использовать ESP8266, и в принципе драйвер там всё равно работает – только довольно медленно: порядка 20 секунд на каждые 10 градусов движения. Этого всё ещё достаточно для отображения медленно изменяющихся параметров (такие например как температура или количество лайков к этому посту). Обратите внимание что цикл в VID6608XvTask() надолго засыпает, если мотор окончил движение: это замедляет начало следующего движения на 0…500 мс, но зато существенно разгружает ядро своим сном [17].
Решение второй проблемы менее очевидно: надо просто добавить вызов функции yield() в процедуру выставления нужного шага:
void vid6608::step(vid6608::MoveDirection direction, uint16_t delayUs) {
...
// We should keep resources reserved by others
yield();
}
В этом случае даже многократный вызов в цикле не будет блочить нашу программу и мы избавимся от падения Watchdog.
Добавляем примитивы синхронизации FreeRTOS – мьютексы. Изначально я на них забил, т.к. решил что проблема не должна быть большая, в крайнем случае просто лишнее попадёт в вывод MQTT, а так сэкономим на ресурсах. Но нет: проблемы синхронизации многопоточности быстро вылезли на собранном девайсе, поэтому делаем себе макрос, с заворотом этого всего в ESP-32-only код:
#ifdef VID6608_RTOS
SemaphoreHandle_t vid6608Mutex;
#define VID6608_MUTEX_TAKE xSemaphoreTake(vid6608Mutex, portMAX_DELAY);
#define VID6608_MUTEX_GIVE xSemaphoreGive(vid6608Mutex);
#else
#define VID6608_MUTEX_TAKE
#define VID6608_MUTEX_GIVE
#endif
, который можно безопасно пихать везде где надо, и это всё собирается и под esp8266. Вообще, работа драйвера под esp8266 не является строго обязательной, у Тасмоты гайдлайн требует только что-бы собиралось так же для esp8266, в принципе фичу можно оставить как эксклюзив для ESP32, но я решил оставить – мы же не Apple, что бы делать искусственные ограничения людям.
Ещё хочу коснуться другой проблемы: тайминги. Для красивой отрисовки движения стрелки мы должны выдерживать тайминги вплоть до 0.3мс (300 мкс), что уже средствами таймеров FreeRTOS невозможно. Да, у них есть своя реализация delayMicroseconds(), но посмотрев в исходный код нас ждёт фрустрация: там просто цикл из nop-ов на нужное время. Что просто превращает в тепло время ожидания вместо таймеров. Проблема на данный момент не решена – если кто-то знает хорошее решение – пожалуйста напишите в комментах. Из хорошего: проблема не то что бы большая, она проявляется только во время активного движения стрелки и только на некоторых участках.
В любом случае: патчи успешно приняты в Тасмоту и баги поправлены:
Добавление драйвера [18] – здесь можете посмотреть пример полного пакета изменений на добавление драйверов, пинов к ним и команд
Добавление поддержки сброса из сохраненного состояния и исправление багов по-меньше [20]
На момент написания обзора драйвер уже включён в релиз Tasmota v15.2.0 Stephan [21]. Обратите внимание что третий пакет изменений (сброс из сохраненного состояния) в релиз попасть не успел, так что база для прошивки пока что всё равно development.
Драйвер добавляет следующие команды:
GaugeZero N – процедура калибровки, где N опциональная начальная позиция (см следующий раздел)
GaugeSet N – выставить положение в абсолютном значении шагов мотора, от 0 до 3840 (320*12)
GaugePercent N – выставить положение в процентах, от 0 до 100
Драйвер поддерживает до 4 двигателей. Все команды по-умолчанию управляют первым двигателем, могут быть с суффиксом мотора (от 1 до 4), при этом 0 – это все двигатели. Пример: GaugeSet0 520 – выставит все двигатели в значение 520, GaugeZero3 – сброс только мотора №3.
При старте прошивки все двигатели сбрасываются по-очереди, что несколько замедляет инициализацию, но сделать парралельную довольно сложно, надо будет делать усложнение нашего маленького драйвера, что в общем тоже нежелательно. Оставил так.
Это была одна из главных загадок для меня: я когда только заказал и сидел ждал моторчики, пытался нагуглить или выяснить – как же делают другие? На форумах симмеров предлагали всякие громоздкие решения типа оптических датчиков, а все остальные – просто ничего не писали про это. В старой библиотеке в исходном коде нашёл объяснение – там просто происходило движение назад на всю шкалу, что, видимо, приводило к торможению привода в крайнем положении, после чего текущее положение принималось за нулевое. Это действительно работает, но приводит к долблению нашего моторчика об концевые упоры – непорядок.
Собственно решение: а давайте просто будем запоминать текущее значение «куда-нибудь» и потом использовать его для калибровки. В этом случае можно просто продвинуть стрелку вперёд на остаток от последнего значения, а потом полный круг назад. Если стрелка была в рассинхроне с тем что у нас было записано, то в худшем случае произойдёт долбление либо в начале, либо в конце (смотря куда уехало реальное значение), но в итоге стрелка будет гарантировано в нулевой позиции.
Вопрос только – куда писать? Флеш не подходит, т.к. у него сильно ограничено кол-во записи. Но всё уже придумано до нас: это – сегнетоэлектрическая оперативная память [22] (Ferroelectric RAM, FeRAM или FRAM) — оперативная память, по своему устройству схожая с DRAM, использующая слой сегнетоэлектрика вместо диэлектрического слоя для обеспечения энергонезависимости. Чип стоит недорого и обеспечивает до 10¹² циклов записи, этого должно быть достаточно для чуть больше 31 тысяч лет непрерывной работы при условии записи раз в секунду. Реализация очень простая – давайте просто поставим недорогой чип Fujitsu MB85RC04V FRAM [23] на плату и будем писать туда каждый раз, когда стрелка принимает команду.
Пример восстановления из сохранённого состояния. Всё работает и не долбится где не следует :-)
Вообще, помимо FRAM существуют другие решения, например DRAM с бекапом во флеш [24] при отключении питания и им подобные, но в данном случае это избыточно. Много запоминать нам и не надо, стоит недорого. В автомобильных приборках думаю какой-то особой процедуры нет вообще – так как там система находится под постоянным бекапом батареи, у меня такой опции нет. У меня в машине приборка кстати делает тест стрелок – точно так же как у меня получилось, но это чисто декоративное, можно выключить шнурком.
Итак, мы отработали основные идеи и можно приступать к дизайну электроники нашего девайса.
Внимание: я буду использовать схему с исправлениями оригинального дизайна. Вы можете заметить отличия от фотографий реального девайса, но схема уже содержит исправления косяков. Это сделано для простоты повторения. Оригинальные схемы могут быть найдены в репозитории [25], если вас интересует именно изначальное исполнение.
Заходим на EasyEDA и рисуем схему:

Основа для всего девайса – модуль ESP32-WROOM-32, двух-ядерный, 4Мб флеша. Вы, конечно, можете поставить любой другой в том же форм-факторе.
Самое важное на данном этапе – выбрать правильные GPIO. Если вы задействуете не тот пин, который имеет двойное назначение – то это может привести к куче неожиданных эффектов, например кристалл не загрузится, если некоторые ноги будут иметь подтяжку в этот момент. Я использовал вот этот гайд: https://randomnerdtutorials.com/esp32-pinout-reference-gpios/ [26] – всё под рукой и доходчиво.

Данная схема нужна для удалённого сброса кристалла из esptool без необходимости нажимать кнопки сброса/boot. Схема срисована с модуля ESP32-Devkit-v1. Реализует элемент ИЛИ-НЕ для обеспечения очередности сигнала сброса с DTR или RST. Это обеспечивает прошивку через подходящий USB-UART модуль в полностью автоматическом режиме.

Собственно – самое главное :) Драйвер привода, двух-канальный. В стоке девайс собран с одной стрелкой, но может быть оснащён двойной при необходимости. Чип не управляется схемой сброса с MCU, как это рекомендует даташит, так как у меня чипы с алишки VID6608 реагировали неадекватно – возможно не-оригиналы :) В любом случае, прижать резет к питанию и поставить рекомендуемые конденсаторы – базовый минимум, работает как н��до тогда.

На данный разъём выведены GPIO 4, 13 и 25 а так же шина I²C, к которой можно подключить дополнительные датчики или исполнительные устройства, например часы. Присутствует питание и земля. Так же на плате есть ещё два GPIO (18 и 23), выведеные просто на площадки на плате, можно подпаяться.

Выполнена на I²C кристалле FRAM памяти MB85RC04V, производства Fujitsu. Имеет на борту 4Кб памяти – довольно скромно по нашим меркам, зато недорого и работает :)
Дальше ничего особо интересного нет, от модуля разведены:
Кнопка сброса для ручного привода EN. Сбрасывает кристалл при необходимости. Для отсутствия сброса без необходимости предусмотрены цепи подтяжки и конденсатор против дребезга.
Кнопка GPIO34: настроена как Button 0 в Tasmota. При однократном нажатии включает/выключает подсветку, при зажатии на 45 секунд сбрасывает настройки и активирует первоначальную настройку через точку доступа.
Кнопка boot: зажимает GPIO0, переводит в режим прошивки, если нажата одновременно с EN. Данной кнопки нет на фотографиях, добавлена позже.
Датчик SenseAir S8, подключён к GPIO 16 и 17 для коммуникации, порт калибровки подключён к GPIO 19
Модуль ANT20+BMP280, подключён к I²C, измеряет температуру, влажность и давление. Является опцией, при отсутствии просто не будет телеметрии.
Управление подсветкой “ретро” выполнена на схеме биполярного транзистора с открытым коллектором – может управлять чем-то ещё разумеется.
Программный триггер калибровки может помочь, если хотите откалибровать датчик принудительно (иначе он выполняет авто-калибровку каждые 7 дней). Но в прошивке пока что не придумал, как бы сделать нормальный UI под это. Тем не менее, датчик можно высунуть в окно, прижать ногу на 5 секунд – датчик тогда возьмёт текущее значение как 400 ppm.
Заказываем и ждём :) У меня случилось что-то с доставкой и я получил платы только недели через три с извинениями. Платы заказаны с установкой деталей с одной стороны, без модуля ESP32 (они у меня были отдельно, плюс я не был уверен что не наделал косяков).

Народные x27-168 и им подобные рекомендуют закреплять мотор посредством пайки. Хорошая идея, в общем, но требует отдельную печатную плату. Для установки двигателя в корпус сделал простой вариантик с выводами как под разъем, так и под пайку.
Собираем и проверяем – всё почти работает :) У первой версии был один косяк – у кнопки 0 не стал ставить подтяжку, т.к. в документации Tasmota утверждалось [27] что при такой конфигурации используется внутренний резистор подтяжки в MCU. Но нет, нифига. Если оставить болтаться в воздухе то кнопка срабатывает, что может приводить к сбросу настроек (при “нажатии” на 45 секунд). Но благо поправить очень просто – кидаем выводной резистор поверх и готово.


Ссылки на проекты в EasyEDA Web:
Основная плата [28]
Это, наверное, самое важное. В этом разделе мы определим – будет ли наш прибор выглядеть как колхоз или же как что-то приличное. Разумеется, корпус можно полностью сделать на 3D-Принтере, но я эту идею отбросил сразу, по бокам будет видно что корпус печатный, а хотелось что-то более няшное, а значит – покупное и фабрично сделанное. Кроме того, у нас в требованиях пунктик что девайс должен быть простой в повторении. Следовательно, Aliexpress подходит тут слабо – у продавцов часто фактические изделия отличаются в зависимости от времени и может не соответствовать картинке. Ну и внутренности тоже могут быть разных ревизий, а нам надо что-бы всё влезало хорошо.

Начать решил с Ikea. Доводы такие: икея распространена по всему свету и у них изделия строго одинаковые везде. Следовательно, сделать себе такой прибор можно будет практически везде, купить икеевскую штуку можно даже там, где Икеи нет. Посмотрел что у них есть и выбрал Ikea Dekad [30]. Это единственное что подходило более-менее.


Я хочу сказать что этот лот с самого начала мне не очень понравился, думал что будут сложности. Но как же я ошибался! Девайс просто как будто придума�� для самоделок. Красивый и ровный корпус, очень технологичный – минимум лишнего, внутри всё на болтах. Циферблат, рамка и стекло – отдельные детали, что позволяет развернуться полёту мысли. Сверху имеется дырка для привода звонка – который электрический и приводится в действие электромотором. Его можно оставить и например удивлять пользователя громким звонком, если CO₂ высоковат. Но я решил что это будет перебор :)

От оригинального будильника нам понадобится: собственно корпус, стекло, рамка (я так думал тогда, на самом деле – не совсем), гайки и ножки из нержавейки, а так же пипка от стрелки – мы её запрессуем в нашу.
Что мы узнали на данном шаге:
Габариты прибора: цилиндр примерно 95 на 50 мм
Диаметр шкалы
Название приобретает суффикс от своей сущности: Galoped-dekad
Лицо нашего прибора – тут всё должно быть максимально по красоте. У меня ушло на это больше времени чем планировалось. Начал с того что нашёл видео [31], где мужик делает круговую шкалу в Inkscape. Сделал по аналогии – у меня только более свежая версия, там были небольшие отличия. У меня ушло много времени что бы разобраться – для того что бы модификатор “Шаблон по кривой” работал – ваш образец кусочка шкалы должен быть одной целой кривой, тогда всё делается правильно. Документ делаем сразу под будку само-обслуживания фото-печати, под 10х15.

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

И, сразу делаем версию ретро:

Для дизайна шкалы мы должны определить минимальные и максимальные значения. Смотрим на даташит на наш моторчик x27.168 и видим что полный оборот – это 315° (на самом деле 320°, но на момент дизайна шкалы я этого не знал). Теперь прикидываем: у нас есть 315*12 шагов, надо захватить примерно подходящий диапазон и минимизировать вычисления (особенно деление). Так же нужно сделать небольшую “мертвую зону” (для индикации не-готовности прибора). Рассчитываем: оставляем 15° на отображение мертвой зоны, итого у нас остаётся 300° значащего диапазона. Разбиваем его на 18 кусочков, получается что мы можем отобразить от 400 до 2200 ppm, а самое главное, что при этом получаем ровное соответствие: (300*12) / (2200-400) = 2, то есть у нас получается ровно два шага привода на 1 ppm. Это очень удобно, потому что тогда переводить одно значение в другое можно просто сдвигом на 1 бит влево или вправо, что гораздо быстрее других математических операций в MCU. Ну и у нас остаётся так же лишних 5° за пределами шкалы (и даташита) – но я просто забил на это, т.к. узнал об этом я гораздо позже и не стал ради этого переделывать. Но то значение обязательно надо учитывать при калибровке.

Идём в DM и печатаем нашу чудо-полиграфию в будке. У нас это удовольствие стоит примерно 30 копеек при печати малыми тиражами. У нас при печате в будке есть только глянцевая фото-бумага, матовую надо заказывать. Я думал что это проблема, планировал сбрызнуть матовым лаком – но это не нужно, всё выглядит супер и так.

Дальше сталкиваемся с проблемой – а как нам сделать красивую дырку в центре, что бы это не выглядело как колхоз? Я пытался просверлить или прорезать, получилось так себе. В итоге решение пришло такое – я заказал на Amazon пробойники для кожи. Просто пробить молотком получается плохо, но если зажать пробойник в патрон шуруповерта и “просверлить”, то получается идеально ровное и красивое отверстие.
Вот тут я больше всего провозился в итоге. Изначально план был простой – в Икеевском будильнике штатная рамка как будто прям сделана для подсветки, она полу-прозрачная, изнутри накатка – выглядит супер. Но на деле оказалось всё не так просто.

Сразу стало очевидно что обычная светодиодная лента годится слабо – хотелось что-бы подсветка выглядела “лампово”, то есть без видимых диодов. Для этого было решено приобрести гибкие филаментные нити на 3 вольта. Выглядят отлично, светятся тоже. Я взял вариант где выводы на одной стороне трубки – не делайте так, монтировать потом очень неудобно. Следующие версии буду делать на лентах с подключением с разных концов.

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

Для RGB подсветки приобрел 2.7мм ленту с адресуемыми диодами WS2812b. С оригинальной рамкой проблема та же – они дают тоже резкое гало вокруг циферблата, а так же выглядят ещё хуже – от диодов подсветка выглядит очень точечной, на реальные приборы мало похоже.

Я пробывал распечатать своб рамку из белого пластика, что-бы получился рассеиватель, но помогает слабо. На фото пример такого варианта. В итоге гало можно немного уменьшить, если диодную ленту клеить диодами наружу – но они всё равно светят сквозь ленту.

Пример рамки из “прозрачного” пластика, как видите тоже – так себе. В этом разделе довольно кратко, но я напечатал и проверил кучу вариантов, с диодами внутри кольца, снаружи, ближе к стеклу и циферблату – результат плохой.

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

На фото пример с RGB лентой, изолентой и промежуточной черной рамкой. Подробности можете посмотреть на 3D моделях. Филаментная лента встаёт в ту же рамку без модификаций.

Адресуемые диоды так же позволяют делать разные эффекты, например секторную подсветку или менять цвет в зависимости от показаний прибора. Секторная смотрится в целом плохо – она даёт яркий блик на противоположную сторону прибора.

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

Получается две детали: сама рамка полупрозрачная и черное кольцо против бликов на стекло.
Теперь самое время сделать пластиковую часть нашего чудо-прибора. Большая часть видимой компоненты (корпус, циферблат) у нас уже выглядят хорошо, осталось только позаботится о задней стенке, стрелке и закрыть дырку наверху корпуса (от механизма звонка). Открываем FreeCAD и приступаем к черчению наших деталей. Кстати, я думал что довольно неплохо знаком с данной CAD-Программой, но решил перед началом посмотреть обучающие видео, нашёл канал Димы Гога [32] тоже из Германии, и просто офигел – я не знал можно сказать ничего. Большое спасибо ему, я сделал всё что нужно гораздо быстрее. Всем очень рекомендую, кто хочет научится делать красивые детали быстро и эффективно.
Итак, шкалу сделали, но что по ней ездить должно? Я над этим долго думал, гуглил по форумам. Там предлагают много чего – вырезать из старой карточки, пробивать, выпиливать руками из металла. Я решил всё-таки попробовать напечатать аккуратно на 3D принтере с максимальным разрешением. Если печатать вверх ногами на хорошей подложке то поверхность получается ровной, по ней даже не видно особо – что она печатная. Я в процессе поиска оптимальной формы и технологии сделал наверное штук сто этих стрелок, они у меня теперь валяются повсюду.

У меня есть любимый лак из Lidl – краска для глушителей. Она даёт прикольную матовую поверхность, я сразу решил черную часть стрелки сбрызнуть именно им. Печатаем, красим – сначала попку, потом саму стрелку белым лаком, который подрезал у своей Фрау.

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

Теперь важный момент – берем оригинальную пипку от икеевского будильника, сверлим 1.8мм дырку в стрелке и запрессовываем в тисках пипку в нашу стрелку. Получается здорово – наша стрелка выглядит уже почти как настоящая в самолёте. Кроме того – она идеально плотно насаживается на вал мотора, ничего мудрить с креплением не надо. Ретро-версия стрелки выглядит почти так же и не окрашивается вообще, печатаю сразу черным пластиком.
В целом, прибор состоит из деталей: рамка (если комплектация с подсветкой, иначе используем оригинальную ikea), передняя панель с седлом для мотора и платы, задняя крышка, крышечьки для датчиков S8 и Климата (если устанавливается).


Вот так выглядит сборочный чертёж. Совмещаем детали, крутим-вертим и смотрим – что-бы все дырки совпадали, ничего не мешало друг другу. Из хитрого оказалось только разве что вычислить – где точно находятся дырки крепления в икеевскоом корпусе: они разнесены на 30° и 34° от вертикальной оси.

Отдельно пришлось повозиться с крышечьками – для датчиков S8 и климата. В итоге остановился на таком – крышка S8 вставляется изнутри и держится просто прижимом (или можно капнуть клея для верности), датчик климата ставится на болты к крышке. Так как оба датчика – опция, то крышки могут не устанавливаться (у меня пока что никто не просил версию без измерения CO₂ на борту, в этом случае надо будет сделать особую крышечьку – или просто удалить дырку под него).

Так же на этом этапе генерим 2D-чертеж, для инструкции для будущих пользователей :) Я для этого создал Page во FreeCad и разместил там нужный View с изометрическим видом продукта, осталось только экспортировать куда следует.

Печатаем все детали, снмиаем и очищаем. Прототипы делал из PLA. Все детали финального устройства печатал пластиком PETG черного цвета, рамка подсветки из “прозрачного”. Корпусные детали и рамки – 0.2 мм слой, заполнение 20%, обдув 20%. Крышечьки датчиков и стрелка – 0.1 мм слой. Обязательно включите ironing для получения красивых плоских поверхностей, особенно которые торчат наружу.
Все модели (CAD и STL) можно посмотреть в репе: https://github.com/petrows/smarthome-galoped-dekad/tree/master/case [33]
Изначально я всё собирал на саморезах М2.3 для пластика, но быстро надоело – выглядит хуже, вопросы к прочности, добавляет “дешевизны” к виду нашего серьёзного спец-изделия. Специально для устройства обзавелся болтам�� М2 и М3 с головкой под шестигранник из нержавеющей стали. Крепление в пластик будет на вплавляемые втулки – просто изумительная штука оказалась, как я без них жил раньше?

Винтики выглядят куда более солидно, придаёт ещё немножко “авиационного” стиля. Кое-что нужно взять от икеевского будильника (гайки и шайбы М3, пару саморезов М2 для врепления USB-C порта, если у вас нет своих).
Итак, давайте теперь соединим всё вместе! Наконец-то увидим, что за франкенштейн у нас тут получается. Выбираем комплектацию для сборки: Авиационный стиль, подсветка ламповая, оба датчика установлены.

Для сборки у нас уже должны быть все детали, что остались от предыдущих шагов или изысканий. Ну или просто печатаем новые на 3D и 2D принтерах, заказываем платы, разьёмы, провода. Раскладываем это всё на столе и любуюмся. Для сборки нужно вообще следующее:
Будильник Ikea DEKAD
Печатные детали: передняя и задняя крышки, верхняя крышка, крышечька датчиков CO₂ и климата, две детали рамки подсветки
Крепёж: Винты M3 (2 шт), Винты M2 (10 шт), Втулки для пластика M2 (10 шт)
Филаментная лента подсветки [34] (26 см или длиннее) и провода к ней
Печатные платы: основная и двигателя
Двигатель X27-168 [35], выводы на задней стороне
Чип VID6608 [37]
Датчик SenseAir S8 [38]
SMD кнопки 3х6 [40], высота любая – у меня 4.5 мм
Разъёмы JST-PH 2.54 [41] и провода: 2-pin (2 шт), 4-pin (1 шт). Мне обжимать впадлу, взял уже обжатые
Разьём питания USB-C [42]
Провода, гребенки, паяльник, хорошее настроение и решительность :)

Первым делом устанавливаем кристалл ESP32. Я заказывал б��з установки оного, т.к. у меня они были и не хотел рисковать возможным браком. Потом всё проклял – установка лютый геморрой, один модуль запорол. Заказывайте установку на заводе вместе с платой – будет тоже самое по цене и гемора намного меньше.

Сразу после установки важно проверить работоспособность – заливаем прошивку и проверяем что прибор приходит в сознание. Можно залить любую Тасмоту, потом закатаем что нужно по воздуху. Я использую данный адаптер [43], он имеет нужные выходы RST и DTR (обратите внимание что DTR вынесен на боковую гребенку – я сначала тупил и перепутал его с CTS и испортил одну плату, думая что сотворил непотребное). В этом случае чип прошивается напрямую через esptool или platform.io. Если такого адаптера нет, то подойдёт любой, главное что бы был совместимый с 3.3В, контакты для входа в режим прошивки коротите тогда руками. Питание лучше подавать внешнее – 3.3В в моей версии отладочного разьёма от адаптера хватало для прошивки, но уже не хватало для первого запуска. В вашей версии платы эта проблема решена (выведено 5В), но в любом случае лучше внешнее питание рекомендуется – вдруг что. В этом случае подключаем только землю от адаптера, иначе можно попалить.

Ура! Кажется что мы ничего не запороли. Пока что :)

Запаиваем остальное – разъёмы и собственно нашего героя дня – VID6608. Его ставить куда легче чем ESP32, накосячить сложнее. Обязательно прозванивайте ноги после установки! У меня всё выглядело супер, но половина ног на деле не звонилась. Если плохо припаяли то будет выглядить так: привод будет дергаться хаотично вместо плавного движения. Устанавливаем всё остальное – разъемы кнопки и прочее.

Устанавливаем гильзы под винты M2. Установка очень простая – ставим на отверстие и вдавливаем паяльником, всё получается ровно и красиво.

Запаиваем наш моторчик на плату крепления и ставим разъём. Дырки под гребенку можно рассверлить и просунуть провода туда для лучшего удержания.

После установки привода можно вырезать и приклеить лицевую панель. Подготавливаем стрелку, прессуем в неё пипку и красим. После этого можно собрать всё вместе.

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

Разбираем будильник, снимаем с него всё ценное, удаляем наклейку и чистим. Получается красивый глянцевый цилиндрик. Стекло аккуратно вынимаем и желательно не ляпаем – жирные следы пальцев потом будет сложно убрать.

Готовим филаментную ленту – припаиваемся к выводам под прямым углом и мастрячим разъём. У меня так же припаяна корректирующая цепь: резистор на 220 Ом и конденсатор подавления пульсаций. В версии платы, что вы увидите в EasyEDA данные детали уже установлены (на момент создания самой первой версии я ещё был в сомнениях по поводу подсветки).

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

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

Устанавливаем основную корпусную деталь, пропускаем провода подсветки. Теперь можно закрутить основные наши крепёжные магистрали: ножки и болты M3 через верхнюю крышечку.

Подсоединяем все разъёмы и устанавливаем основную плату, ориентируем её датчиками вниз. Закручиваем всё красивенькими винтами М2.

В задней крышке привинчиваем разъём USB-C на шурупчики, просунув его изнутри. Можно использовать оригинальные серебристые Ikea от будильника, но у меня есть анодированные черные, они выглядят получше. Устанавливаем решётку датчика S8, сам датчик и помещаем крышку на место, прикручиваем всё на два винта М2.

Датчик климата у нас получается торчит наружу вне корпуса. Это хорошо – тогда тепло от MCU не будет влиять на его показания.

Натыкаем наш климатический датчик на разъём и прикручиваем крышечку сверху на ещё два болта М2. Сборка завершена :-)

Отдаляем на расстояние вытянутой руки, включаем и проверяем что всё работает. Обратите внимание что без начальной конфигурации прибор даже не будет вызывать калибровку “из коробки”, нам необходимо настроить Тасмоту и подготовить агрегат к работе.
Первым делом надо настроить наш “модуль”. Какие ноги где сидят и что делают. Для упрощения жизни я сделал темплейты (версия для RGB и Ламповых подсветок): отличия в одной ноге – она либо на WS2812B либо на транзистор диммера ламповой. Шаблон можно применить руками, но удобнее это сделать сразу в прошивке. Кроме того, нам понадобится парочка #define: по-умолчанию у Тасмоты нет поддержки ни AHT20, ни моего замечательного драйвера. Создаём файл tasmota/user_config_override.h:
// Required features for this project:
// [I2cDriver43] Enable AHT20/AM2301B instead of AHT1x humidity and temperature sensor (I2C address 0x38) (+0k8 code)
#ifndef USE_AHT2x
#define USE_AHT2x
#endif
// [I2cDriver10] Enable BMP085/BMP180/BMP280/BME280 sensors (I2C addresses 0x76 and 0x77) (+4k4 code)
#ifndef USE_BMP
#define USE_BMP
#endif
// Add support for SenseAir K30, K70 and S8 CO2 sensor (+2k3 code)
#ifndef USE_SENSEAIR
#define USE_SENSEAIR
#endif
// Add support for VID6608 Automotive analog gauge driver (+0k7 code)
#define USE_VID6608
// Reset VID6608 on init (default: true), change if you control this manually
#define VID6608_RESET_ON_INIT false
// Enable WS2812 leds number (any HW model)
#undef WS2812_LEDS
// If RGB backlight is used, it will have 40 LED's
#define WS2812_LEDS 40
// Template defaults, apply on flash reset.0
// You may skip this templay application, in this case please apply manually (see README.md)
// Read from file, generated by ./bin/build.sh or CI
#include "user/user_config_hw.h"
// Activate template on reset/flash new
#undef MODULE
#define MODULE USER_MODULE
Так же, для выбора уже конкретной комплектации выбираем один из двух вариантов в виде файла tasmota/user/user_config_hw.h:
/*
Part for Galoped-dekad hw version:
* Backlight: Retro
*/
#undef FRIENDLY_NAME
#define FRIENDLY_NAME "Galoped-dekad-retro"
#define USER_TEMPLATE "{"NAME":"Galoped-dekad-retro","GPIO":[1,1,1,1,1,1,1,1,1,1,416,0,1600,1632,1,1,0,640,608,1,0,1,12160,12192,0,0,0,0,1,1,32,1,1,0,0,1],"FLAG":0,"BASE":1}"
, это было для ламповой, для RGB:
/*
Part for Galoped-dekad hw version:
* Backlight: RGB
*/
#undef FRIENDLY_NAME
#define FRIENDLY_NAME "Galoped-dekad-rgb"
#define USER_TEMPLATE "{"NAME":"Galoped-dekad-rgb","GPIO":[1,1,1,1,1,1,1,1,1,1,0,1376,1600,1632,1,1,0,640,608,1,0,1,12160,12192,0,0,0,0,1,1,32,1,1,0,0,1],"FLAG":0,"BASE":1}"
Как видите – отличие в одном пине и редефайн FRIENDLY_NAME названии модуля – что бы не забыть :) Теперь можно собрать прошивку.
Для ленивых: идём в раздел Releases [44] моего проекта и качаем там готовую.
Остались уже совсем мелочи: надо всё проверить и присоединить стрелку к показаниям CO₂. К счастью, в прошивке ничего даже модифицировать не надо – у Tasmota имеется Berry script [45] в версии для ESP32, так что просто напишем скриптик и закинем в агрегат. Для автозапуска надо именовать данный чудо-файл как autoexec.be и положить его через встроенный файл-менеджер:
# Connect and preare i2c FRAM MB85RC04V
var fram_addr = 0x50
var wire = tasmota.wire_scan(fram_addr)
# Address in FRAM to store last gauge position
var addr_pos = 0x0000
# Check initialization
if !wire
print("FRAM not found")
end
# Function to write FRAM memory, 2 bytes
def fram_write_u16(addr, data)
if !wire
return 0
end
# Split address and data into two bytes
var addr_hi = (addr >> 8) & 0x7F
var addr_lo = addr & 0xFF
var data_hi = (data >> 8)
var data_lo = data & 0xFF
# ---------------- WRITE ----------------
wire._begin_transmission(fram_addr)
wire._write(addr_hi)
wire._write(addr_lo)
wire._write(data_hi)
wire._write(data_lo)
wire._end_transmission(true)
end
# Function to read FRAM memory, 2 bytes
def fram_read_u16(addr)
if !wire
return 0
end
# Split address and data into two bytes
var addr_hi = (addr >> 8) & 0x7F
var addr_lo = addr & 0xFF
# ---------------- READ ----------------
wire._begin_transmission(fram_addr)
wire._write(addr_hi)
wire._write(addr_lo)
wire._end_transmission(true)
wire._request_from(fram_addr, 2)
var value_hi = wire._read()
var value_lo = wire._read()
var value = (value_hi << 8) | value_lo
return value
end
# Read last gauge position from FRAM
var last_gauge_pos = fram_read_u16(addr_pos)
if last_gauge_pos
print("FRAM gauge pos read:", last_gauge_pos)
end
# Call Reset option from saved position, and save zero
tasmota.cmd("GaugeZero " + str(last_gauge_pos))
fram_write_u16(addr_pos, 0)
# Function to update Gauge position on CO2 change
def co2_update(value, trigger)
var drivePos = 180 + ((int(value) - 400) * 2)
if last_gauge_pos != drivePos
tasmota.cmd("GaugeSet " + str(drivePos))
last_gauge_pos = drivePos
# Save current position into FRAM
fram_write_u16(addr_pos, int(drivePos))
end
end
# Add rule to monitor CO2 changes
tasmota.add_rule("S8#CarbonDioxide", co2_update)
Данный файл в репе [46]. Функционально состоит из двух частей – чтение / запись в FRAM и функция чтения показания CO₂:
tasmota.add_rule("S8#CarbonDioxide", co2_update)
Данный вызов вешает вызов кэллбека co2_update при обновлении. Как видите – проще некуда. Теперь наш прибор ожил и будет что-то показывать.
Для чтения и записи значений в FRAM смотрим Datasheet [23]. Данные у нас uint16_t (должен влезать полный лиапазон, т.е. от 0 до 3840), следовательно два байта. Адрес у нас будет просто 0 – мы пока не храним там ничего больше. Как и два байта адреса. Для записи разбиваем адрес и данные по-байтно и отправляем команду на запись: сначала адрес, потом данные:
# Split address and data into two bytes
var addr_hi = (addr >> 8) & 0x7F
var addr_lo = addr & 0xFF
var data_hi = (data >> 8)
var data_lo = data & 0xFF
# ---------------- WRITE ----------------
wire._begin_transmission(fram_addr)
wire._write(addr_hi)
wire._write(addr_lo)
wire._write(data_hi)
wire._write(data_lo)
wire._end_transmission(true)
Чтение выполняется так же. Проверяем – всё работает! Добавим немного кода для восстановления состояния и обновления:
# Read last gauge position from FRAM
var last_gauge_pos = fram_read_u16(addr_pos)
if last_gauge_pos
print("FRAM gauge pos read:", last_gauge_pos)
end
# Call Reset option from saved position, and save zero
tasmota.cmd("GaugeZero " + str(last_gauge_pos))
fram_write_u16(addr_pos, 0)
После сброса привода не забываем [47] сразу же записать новое значение – иначе у нас будет неверная стартовая позиция при отключении питания до выхода датчика CO₂ на рабочий режим.
Подключаем наш прибор к WiFi по инструкции через стандартную процедуру для Тасмоты – точку доступа, открываем ip-адрес устройства и наслаждаемся веб-интерфейсом:

Так же прибор должен показывать показания датчиков и положение стрелки в веб-интерфейсе. В зависимости от комплектации будет либо диммер “ламповой” подсветки, либо выбор цвета для RGB. Дальше уже дело техники – следуйте инструкциям Tasmota что-бы настроить интеграцию в Умный Дом и прочее, наша прошивка отличается только базовой конфигурацией и все фичи доступны:
Поддержка MQTT;
Поддержка HTTP-API;
Поддержка нативных и облачных интеграций [48], таких как AWS IoT или ioBroker;
И многое другое, что вы в конфиге включить не забыли ;)
У меня довольно большой умный дом в OpenHab [49] (около 150 устрйоств), там интеграция делается стандартно, как для любой Тасмоты. В примере у нас объявляется устройство sz_climate:
Thing mqtt:topic:openhab:sz_climate "SZ Climate" (mqtt:broker:openhab) {
Channels:
Type number : rssi [stateTopic="tele/sz_climate/STATE", transformationPattern="JSONPATH:$.Wifi.RSSI"]
Type string : bssid [stateTopic="tele/sz_climate/STATE", transformationPattern="JSONPATH:$.Wifi.BSSId"]
Type number : la [stateTopic="tele/sz_climate/STATE", transformationPattern="JSONPATH:$.LoadAvg"]
Type datetime : activity [stateTopic="tele/sz_climate/STATE", transformationPattern="JS:codegen-activity.js"]
Type switch : light [stateTopic="stat/sz_climate/RESULT", transformationPattern="REGEX:(.*"POWER1".*)∩JSONPATH:$.POWER1", commandTopic="cmnd/sz_climate/POWER1", on="ON", off="OFF"]
Type number : co2 [stateTopic="tele/sz_climate/SENSOR", transformationPattern="JSONPATH:$.S8.CarbonDioxide", unit="ppm"]
Type number : temperature [stateTopic="tele/sz_climate/SENSOR", transformationPattern="JSONPATH:$.AHT2X.Temperature", unit="C°"]
Type number : humidity [stateTopic="tele/sz_climate/SENSOR", transformationPattern="JSONPATH:$.AHT2X.Humidity", unit="%"]
Type number : dewpoint [stateTopic="tele/sz_climate/SENSOR", transformationPattern="JSONPATH:$.AHT2X.DewPoint", unit="C°"]
Type number : pressure [stateTopic="tele/sz_climate/SENSOR", transformationPattern="JSONPATH:$.BMP280.Pressure", unit="hPa"]
}
...
Switch sz_climate_backlight "SZ Climate Backlight" <light> (g_all_sw) {channel="mqtt:topic:openhab:sz_climate:light"}
Number:Dimensionless sz_climate_co2 "SZ Climate CO₂ [%d ppm]" <co2> (g_all_co2) {channel="mqtt:topic:openhab:sz_climate:co2"}
Number:Temperature sz_climate_temperature "SZ Climate temp [%.0f %unit%]" <temperature> (g_all_temperature,sz_hz_temperature_sensor) {channel="mqtt:topic:openhab:sz_climate:temperature"}
Number:Dimensionless sz_climate_humidity "SZ Climate humidity [%.0f %%]" <humidity> (g_all_humidity) {channel="mqtt:topic:openhab:sz_climate:humidity"}
Number:Temperature sz_climate_dewpoint "SZ Climate dewpoint [%.0f %unit%]" <temperature> (g_all_dewpoint) {channel="mqtt:topic:openhab:sz_climate:dewpoint"}
Number:Pressure sz_climate_pressure "SZ Climate pressure [%.0f %unit%]" <pressure> (g_all_pressure) {channel="mqtt:topic:openhab:sz_climate:pressure"}
Number:Dimensionless sz_climate_rssi "SZ Climate RSSI [%.0f]" <network> (g_all_rssi) {channel="mqtt:topic:openhab:sz_climate:rssi"}
String sz_climate_bssid "SZ Climate BSSID [%s]" <network> (g_all_bssid) {channel="mqtt:topic:openhab:sz_climate:bssid"}
DateTime sz_climate_activity "SZ Climate activity [JS(codegen-display-activity.js):%s]" <time> (g_all_activity) {channel="mqtt:topic:openhab:sz_climate:activity"}
В данном примере мы прицепили наши итемы к показаниям датчиков на борту (CO₂, температура, влажность и давление), а так же добавили вкл/выкл подсветки. Вы также можете управлять диммированием подсветки и/или цветом (для RGB версии), для этого надо накинуть итем с типом Dimmer.
Ну и само собой – можно отцепить стрелку от датчика и управлять напрямую, уже по команде Умного Дома, для этого просто отапрвить в MQTT команду cmnd/sz_climate/GaugeSet с payload в абсолютных шагах. Можно закинуть диммер на панель УД и крутить стрелкой вручную.
Точную стоимость сильно зависит от кол-ва изготавливаемых экземпляров и что у вас есть уже в наличии. В моём случае чисто по железу получилось порядка 50€ за прибор, и это при условии что 3Д-принтер у меня свой и корпусные детали получаются условно-бесплатные. Основная стоимость – датчик, он один получается дороже всей остальной электроники вместе взятой. Если кому-то нужен точный расчёт то пишите в комменты – добавлю.
Вот такое у нас с вами получилось чудо техники – что думаете по поводу?

И руки размяли и подарить не стыдно. Ну и ещё в Тасмоту хороший драйвер добавили.

Старый и новый приборы вместе – гештальт закрыт.
Вообще, устройство конечно легко кастомизируется и адаптируется под суровую реальность:
Внешний вид: я написал имена людей на циферблате (кому дарил) – получается прикольный подарок в виде уникального гаджета, который в магазине не купишь;
Прибор конечно может показывать что угодно, как с датчиков, так и с внешнего управления; Тут уже на что хватит воображения – температура, влажность, черти в ступе или остаток рабочего дня на сегодня;
На плате имеется разъём расширения, который может в себя принять в общем что угодно – I²C датчики, дисплеи и прочее;
Плата может управлять двумя двигателями – как одним сдвоенным с двумя стрелками, так и двумя отдельно;
Можно докинуть ещё одну VID6608 или заменить на VID6606 – в этом случае можно будет рулить сразу 4-мя приводами;
Можно управлять оригинальным мотором будильника и громко звонить когда надо :-)
Что думаю надо бы в будущем улучшить (возможно когда вычитаете этот обзор то уже что-то сделано, см обновления в репе):
Думаю что надо-бы добавить работу с FRAM в саму Тасмоту, это неудобно что мы читаем-пишем в скрипте, думаю можно сделать автоматическую запись и чтение при калибровке. Но вот не уверен что такое примут – наверное такой патч слишком уж специфичный;
Надо разделить один 8-пиновый разъём для двух двигателей на два по 4: такое решение оказалось очень неудобное;
Разъёмы все надо перевернуть на 180°, оказалось что невозможно поставить угловые при текущей компоновке – так было бы удобнее;
Идея с Breakout-board для двигателя оказалось не очень хорошей – это сильно усложняет конструкцию и вообще бестолковое решение. Думаю хорошая универсальная печатная крышечька с креплением проводов напрямую будет получше;
Надо-бы добавить индикацию статуса какую на плату, а то юзеру невозможно понять – она живая там вообще или нет;
Использованный USB-C коннектор работает только с USB-A-C кабелем, USB-C-C не подходит. Не критично, но безусловно минус;
Исходники проекта: https://github.com/petrows/smarthome-galoped-dekad [50] . В репе сейчас небольшой бардак из тестовых деталей и нет нормального Readme – не было времени прибраться, наведу порядок чуть позже. Но всё тем не менее есть.
Если у вас ещё есть хорошие идеи – делитесь, прикрутим. Всем хорошего дня ;)
Автор: petro_64
Источник [51]
Сайт-источник BrainTools: https://www.braintools.ru
Путь до страницы источника: https://www.braintools.ru/article/23467
URLs in this post:
[1] ошибки: http://www.braintools.ru/article/4192
[2] GNU GPLv3: https://github.com/petrows/smarthome-galoped-dekad/blob/master/LICENSE
[3] повторении: http://www.braintools.ru/article/4012
[4] почитать можно например тут: https://www.iqair.com/ru/newsroom/indoor-carbon-dioxide-co2
[5] здесь: https://mysku.club/blog/china-stores/75322.html
[6] пост замечательного парня guy.carpenter: https://guy.carpenter.id.au/gaugette/2017/04/29/switecx25-quad-driver-tests/
[7] популярны у симмеров: https://simvimx.com/gau_stepper_x27.html
[8] целое большое семейство: https://github.com/petrows/smarthome-galoped-dekad/blob/master/datasheet/juken-stepper-motors-for-dashboards-datasheet_vers.3.0.pdf
[9] английский даташит на VID6608: https://github.com/petrows/smarthome-galoped-dekad/blob/master/datasheet/VID6608.pdf
[10] SwitecX25: https://github.com/clearwater/SwitecX25
[11] интерес: http://www.braintools.ru/article/4220
[12] Tasmota: https://tasmota.github.io/docs/
[13] arduino-vid6608: https://github.com/petrows/arduino-vid6608
[14] xdrv_92_vid6608: https://github.com/arendst/Tasmota/blob/development/tasmota/tasmota_xdrv_driver/xdrv_92_vid6608.ino
[15] внимание: http://www.braintools.ru/article/7595
[16] display: https://tasmota.github.io/docs/Displays/
[17] сном: http://www.braintools.ru/article/9809
[18] Добавление драйвера: https://github.com/arendst/Tasmota/pull/24153
[19] Исправление багов многопоточности (я забил на создание синхронизационных объектов, пока не собрал реальный девайс): https://github.com/arendst/Tasmota/pull/24189
[20] Добавление поддержки сброса из сохраненного состояния и исправление багов по-меньше: https://github.com/arendst/Tasmota/pull/24218
[21] Tasmota v15.2.0 Stephan: https://github.com/arendst/Tasmota/releases/tag/v15.2.0
[22] память: http://www.braintools.ru/article/4140
[23] Fujitsu MB85RC04V FRAM: https://github.com/petrows/smarthome-galoped-dekad/blob/master/datasheet/MB85RC04V.pdf
[24] DRAM с бекапом во флеш: https://github.com/petrows/smarthome-galoped-dekad/blob/master/datasheet/47L04-EERAM.pdf
[25] репозитории: https://github.com/petrows/smarthome-galoped-dekad/tree/master/shematic
[26] https://randomnerdtutorials.com/esp32-pinout-reference-gpios/: https://randomnerdtutorials.com/esp32-pinout-reference-gpios/
[27] документации Tasmota утверждалось: https://tasmota.github.io/docs/Buttons-and-Switches/
[28] Основная плата: https://oshwlab.com/petrows/galoped-dekad
[29] Breakout плата для привода: https://oshwlab.com/petrows/galoped-dekad-drive
[30] Ikea Dekad: https://www.ikea.com/de/de/p/dekad-wecker-schwarz-30540479/
[31] видео: https://www.youtube.com/watch?v=kPpti6-jHaQ
[32] канал Димы Гога: https://youtube.com/playlist?list=PLrwLdIBuPZQYLgEido7oWpHCu6io0WywY&si=4EqxRqk2pRcRh05R
[33] https://github.com/petrows/smarthome-galoped-dekad/tree/master/case: https://github.com/petrows/smarthome-galoped-dekad/tree/master/case
[34] Филаментная лента подсветки: https://www.aliexpress.com/item/1005009437951627.html
[35] Двигатель X27-168: https://www.aliexpress.com/item/1005008385301163.html
[36] Модуль ESP32-WROOM32E (4Mb): https://www.aliexpress.com/item/1005002515949841.html
[37] Чип VID6608: https://www.aliexpress.com/item/1005007050530920.html
[38] Датчик SenseAir S8: https://www.aliexpress.com/item/1005009122286447.html
[39] Датчик климата – AHT20 / BMP280 модуль: https://www.aliexpress.com/item/1005005486181411.html
[40] SMD кнопки 3х6: https://www.aliexpress.com/item/1005009645342892.html
[41] Разъёмы JST-PH 2.54: https://www.aliexpress.com/item/1005008339053787.html
[42] Разьём питания USB-C: https://www.aliexpress.com/item/1005010142667928.html
[43] данный адаптер: https://www.aliexpress.com/item/1005008554998627.html
[44] Releases: https://github.com/petrows/smarthome-galoped-dekad/releases
[45] Berry script: https://tasmota.github.io/docs/Berry/
[46] Данный файл в репе: https://github.com/petrows/smarthome-galoped-dekad/blob/master/firmware/autoexec.be
[47] забываем: http://www.braintools.ru/article/333
[48] нативных и облачных интеграций: https://tasmota.github.io/docs/Integrations/
[49] умный дом в OpenHab: https://github.com/petrows/smarthome-openhab
[50] https://github.com/petrows/smarthome-galoped-dekad: https://github.com/petrows/smarthome-galoped-dekad
[51] Источник: https://habr.com/ru/articles/978912/?utm_source=habrahabr&utm_medium=rss&utm_campaign=978912
Нажмите здесь для печати.