Делаем кондиционер умным с помощью Elixir и Nerves

в 13:00, , рубрики: IoT, lirc, Raspberry Pi Zero, ruvds_перевод, кондиционеры, пульт дистанционного управления, умные устройства

Делаем кондиционер умным с помощью Elixir и Nerves - 1


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

Хотя идея дистанционного управления кондиционером далеко не нова (многие уже проложили этот путь до меня, став для меня источником вдохновения), я считаю, что моя реализация достаточно уникальна.

Самым простым было купить необходимые компоненты — Raspberry Pi Zero WH и Infrared Transceiver Hat; нетерпеливо ждать их оказалось уже не так легко.

После получения заказа я осознал, что transceiver hat прибыл без самого инфракрасного датчика и инфракрасных диодов, поэтому мне пришлось заказать необходимые компоненты с Amazon, самостоятельно припаять их и убедиться, что я могу получать инфракрасный сигнал в Raspberry Pi OS при помощи команды LIRC с интуитивно понятным названием mode2.

Делаем кондиционер умным с помощью Elixir и Nerves - 2

Заметили, чего не хватает? Ещё хочу отметить, что меня постоянно поражает крошечный размер Raspberry Pi Zero. Приложил для сравнения крышку от бутылки.

На этом этапе мне нужно было принять важное решение: насколько амбициозным я хочу сделать проект? У меня было четыре варианта.

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

Вторая сложность связана с развёртыванием веб-сервера, предоставляющего интерфейс пользователя для управления кондиционером. Выбрать ли мне простое развёртывание веб-сервера Phoenix на Raspberry Pi OS или пойти поболее сложному, но, безусловно, и более крутому пути связывания Phoenix с Nerves?

В обоих случаях я выбрал вторые варианты, более безумные, но и, бесспорно, более впечатляющие. Давайте сначала поговорим о Nerves.

Делаем кондиционер умным с помощью Elixir и Nerves - 3

Наверно, не самое лучшее соединение в моей жизни, но должен сказать, что прошло много лун с тех пор, как я в последний раз держал в руках паяльник.[1]

[1] Примечание: изначально я припаял только один IR-диод, потому что так выглядели платы во всех статьях, которые я видел в Интернете. Результат меня разочаровал, потому что плата никак не реагировала на мои команды. Расстроившись, я наудачу прикрепил ещё один диод, даже не припаяв его, ни на что особо не надеясь. Затем я проверил диоды объективом своего iPhone и, к своему искреннему удивлению, увидел, что они оба испускают инфракрасное излучение. Думаю, это связано с сектором SJ1, соединённым каплей припоя на большинстве плат, но не на моей; впрочем, эту теорию я не проверял.

▍ Elixir на встроенных системах

Nerves — это одна из самых существенных библиотек Elixir. Phoenix позволяет писать веб-серверы, превосходящие по скорости гепарда, Ecto позволяет общаться с широким спектром баз данных, а Nx позволяет подчинить себе мощь машинного обучения и искусственного интеллекта. Nerves же позволяет беспроблемно развёртывать Elixir на встроенных устройствах, например, на Raspberry Pi. И у меня наконец-то появилась возможность воспользоваться ею!

Nerves объединяет ваше приложение с урезанным ядром Linux и несколькими необходимыми утилитами в прошивку, которую можно «прожечь» на карту microSD, что позволит встроенному устройству напрямую загружать BEAM (Erlang Virtual Machine). Такой минималистичный подход, в отличие от развёртывания приложения Elixir в полнофункциональной Raspberry Pi OS, обеспечивает множество преимуществ:

  • Весь бандл операционной системы и приложения занимает десятки, а не сотни мегабайтов[2]; [2] К тому же он потребляет микроскопический объём памяти.
  • система загружается и делает приложение доступным за секунды, а не за десятки секунд;
  • обновления приложения и его зависимостей обрабатываются как часть вашего кода, а не через медленный и сложный в воспроизведении менеджер пакетов;
  • благодаря малому количеству ПО вектор атаки существенно меньше;
  • вместо работы с системами init Linux вы определяете приложения Erlang;
  • при этом всё равно сохраняется способность ядра Linux взаимодействовать с оборудованием через драйверы Linux.

Создание и запуск базового приложения Nerves оказались очень лёгким процессом, зато гораздо сложнее было заставить работать в Nerves инфракрасный приёмник и передатчик; недостаточно было просто выполнить apt install lirc и отредактировать config.txt, как в Raspberry Pi OS.

К счастью, Nerves может похвастаться потрясающей документацией, помогающей в течение всего процесса настройки системы. Даже несмотря на то. что в половине случаев я почти не понимал, что делаю, мне удалось упаковать lirc-tools в пользовательское пространство, внедрить поддержку инфракрасного пульта управления в ядро Linux и настроить свой проект Nerves на работу с этой операционной системой. Но потом… ничего не произошло, устройства /dev/lircX не стали доступны сразу волшебным образом.

Оказалось, что нужно было выполнить ещё несколько шагов и преодолеть ещё несколько препятствий. В частности:

Закончив с этим, я приступил к написанию простого декодера сигналов, который должен превращать вывод команды LIRC mode2 в строку CSV; затем можно будет импортировать её в Google Sheets для анализа!

Делаем кондиционер умным с помощью Elixir и Nerves - 4

SSH к IEx никогда не перестанет меня удивлять.

▍ Взламываем код

Прочитав упомянутые выше статьи, я начал догадываться, что инфракрасный сигнал пульта закодирован модифицированной версией [3] протокола NEC. Этот протокол представляет двоичный ноль в виде последовательностей кратковременных импульсов (562,5 мкс), за которыми идёт короткая пауза, а двоичную единицу — в виде последовательностей кратковременных импульсов, за которыми следует длинная пауза (1,6875 мс) [4].

[3] Модифицированной, потому что, по крайней мере, согласно найденной мной спецификации, протокол NEC достаточно строго относится к общей структуре сигнала; он начинается с пакета импульсов 9 мс, за которым идёт пауза 4,5 мс; затем следует сочетание address и address’ (логической инверсии), а заканчивается сигнал command и command’. Ничто из этого не оказалось применимо к сигналу, генерируемому пультом моего кондиционера: вначале шёл пакет импульсов 4,5 мс и пауза 4,5 мс; addressи address’ присутствовали только в первых двух датаграммах, но отсутствовали в третьей; вместо одного блока из command и command’ было два блока команд, а в некоторых ситуациях второй блок command’ заменялся другой структурой (иными словами, Toshiba отказалась от блока чётности command’ и использовала этот раздел под другие цели).

[4] Кстати, именно из-за этого строгого тайминга мне пришлось использовать LIRC для отправки сигналов с моей стороны. Когда я попробовал передавать сигнал при помощи только одного кода Elixir, мне не удавалось подобрать тайминг достаточно верно.

Благодаря упомянутому в предыдущем разделе декодеру (позже исправленному, чтобы он различал части начала и конца блока) мой процесс работы стал похож на нечто такое:

  • открываем SSH-подключение к моему приложению Nerves, чтобы получить доступ к приложению через IEx shell, не в bash или чём-то подобном;
  • вызываем функцию моего декодера, которая использует MuonTrap для запуска mode2, 3 секунды ждёт вывода, а затем прекращает mode2 (mode2 выполняется бесконечно, но меня интересует сканирование только одной команды за раз);
  • нажимаем кнопку на пульте дистанционного управления кондиционером для сканирования отправляемого кода; делаем упор на внесение наименьших возможных изменений[5] по сравнению с ранее отсканированной командой, чтобы с лёгкостью различать части сигнала;
  • берём декодированную строку CSV и вставляем её в мою огромную Google-таблицу.

[5] Например, на одном этапе я сканирую код, обозначающий режим охлаждения при 20,0°C с автоматической скоростью вентилятор. На следующем этапе я оставляю тот же режим и скорость вентилятора, но поднимаю температуру на один градус. Разница между этими двумя сигналами покажет, где в сигнале закодирована температура. (На самом деле, оказалось, что она закодирована в двух местах — половина градуса 20,5°C хранилась далеко от целой части.)

Когда я говорю «огромную», я подразумеваю действительно огромную Google-таблицу.

Делаем кондиционер умным с помощью Elixir и Nerves - 5

Эта таблица гораздо длиннее необходимого? Да, это так. Было ли весело её создавать. Да, было. Оценил ли я благодаря ей преимущества сверхширокого дисплея? Вам действительно нужен ответ на этот вопрос?[6]

[6] Если кто-то найдёт логику или причину подобного кодирования температуры, то я с удовольствием выслушаю. Она не увеличивается монотонно со значением температуры и я не смог найти никакой логики даже с учётом разной endianness.

Единственное, с чем было немного сложно разобраться, был самый последний байт — контрольная сумма. К счастью, это оказалась простая сумма байтов в третьей датаграмме (минус часть с переполнением); это даже не потребовало учёта различий в endianness.

Имея на руках готовую таблицу, можно было приступать к написанию (а потом и исправлению) модуля кодировщика, который сможет преобразовать любое состояние в представление сигнала, понятное кондиционеру. К счастью, кондиционер в моей гостиной уже был умным и в то же время понимал сигнал, испускаемый пультом кондиционера в спальне; поэтому я мог воспользоваться преимуществом цикла обратной связи: пробуем отправить сигналпроверяем в мобильном приложении умного кондиционера, понял ли он его так, как должен.

Спустя несколько часов я смог полностью управлять кондиционером из сессии SSH, а ещё через несколько часов у меня уже был очень простой, но функциональный веб-интерфейс, созданный при помощи Phoenix и LiveView[7]. Меня не перестаёт удивлять то, что благодаря всего двум строкам на LiveView можно синхронизировать состояние между двумя браузерными окнами на двух разных устройствах. Поистине волшебное чувство!

[7] Забавный факт: фундамент UI я сгенерировал, отправив фото пульта своего кондиционера на Claude.ai и попросив ИИ сгенерировать код на HTML и TailwindCSS, который бы был похож на предмет из реального мира.

Делаем кондиционер умным с помощью Elixir и Nerves - 6

Я решил сделать интерфейс пользователя на японском, и не спрашивайте меня, зачем.

Меня сильно впечатлила простота и увлекательность разработки моего первого IoT-устройства при помощи Nerves, поэтому я уже придумываю новые идеи для дальнейшей реализации.

Кроме того, я благодарю сообщество Elixir/Nerves в канале #nerves на официальном Discord-сервере Elixir. Оно стало источником вдохновения и оказало огромную поддержку!

Автор: ru_vds

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js