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

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

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

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

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

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

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

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

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

Наверно, не самое лучшее соединение в моей жизни, но должен сказать, что прошло много лун с тех пор, как я в последний раз держал в руках паяльник.[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 для анализа!

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-таблицу.

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

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

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

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

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

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

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

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

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

Telegram-канал со скидками, розыгрышами призов и новостями IT ?

Комментарии (26)


  1. diakin
    29.05.2024 13:15
    +2

    и поднимусь на второй этаж

    Кучеряво живут разработчики в Японии!


    1. Semy
      29.05.2024 13:15
      +3

      Если это Токио, то там может быть очень крохотная квартирка с кроватью на втором этаже. Типа такого: https://wl-adme.cf.tsp.li/resize/728x/webp/1a6/bc6/d2ae6c5096aeb5caa3c95ee4c3.jpg.webp


      1. DGN
        29.05.2024 13:15
        +1

        А как там дышать, наверху? Всегда удивляла такая конфигурация.


  1. GennPen
    29.05.2024 13:15
    +7

    дистанционном управлении кондиционером воздуха в моей спальне через Интернет

    И чем это отличается от управления пультом? Это не добавляет "умности" кондиционеру, а только расширяет область его управления.

    И для этого достаточно было бы обычной ESP32.


    1. ArtPlotnikov
      29.05.2024 13:15
      +3

      Тоже подумал про это. ESP32 или вообще arduino nano с bluetooth сойдёт, а Raspberry для этого как микроскопом забивать гвозди.


      1. GennPen
        29.05.2024 13:15
        +3

        На RP Pi Zero можно было бы вообще поднять MQTT, на который натравить Sleep As Android и с помощью этого всего сделать автоматический вход и выход в ночной режим, без всяких пультов. Вот это более-менее напоминало бы "умный" кондиционер.


        1. ArtPlotnikov
          29.05.2024 13:15
          +2

          Если сервер MQTT на Raspberry, то надо ещё доступ извне открыть (динамический DNS или купить IP у провайдера). Будем рациональными) Нужно для зубной щётки сделать подставку с датчиком/контактом/герконом и ардуиной с модулем RF. Если на часах ночное время и щётку сняли с подставки, то ардуина отправляет сигнал другой ардуинке, которая запускает кондиционер. В этом случае не зависим от интернета.


          1. GennPen
            29.05.2024 13:15
            +1

            Если сервер MQTT на Raspberry, то надо ещё доступ извне открыть (динамический DNS или купить IP у провайдера).

            Зачем, если клиент внутри домашней сети?


            1. ArtPlotnikov
              29.05.2024 13:15
              +1

              Тогда и MQTT не нужен ради одной команды "включи кондёр". Простой Webhook сойдёт.


          1. BaboonR800
            29.05.2024 13:15

            А зачем mqtt сервер дома. Можно и облачный и бесплатный


    1. ginkage
      29.05.2024 13:15
      +2

      Да даже ESP8266 достаточно: https://habr.com/ru/articles/419963/


      1. GennPen
        29.05.2024 13:15

        Да, более чем достаточно. Но у ESP32 есть аппаратный модуль RMT, через который очень удобно работать с приемом/отправкой ИК-сигналов. Да и по цене он не на столько дороже.


        1. ginkage
          29.05.2024 13:15
          +1

          Всё так. При желании можно было бы даже ZigBee использовать, с той же ESP32-C6, и всё равно было бы столь же недорого, плюс стандартные протоколы для климат-контроля.

          А вот Raspberry Pi Zero тут откровенно избыточно.


  1. sekuzmin
    29.05.2024 13:15
    +1

    Nerves — это одна из самых существенных библиотек Elixir. Phoenix позволяет писать веб-серверы, превосходящие по скорости гепарда

    Нужно всего лишь дождаться, пока Raspberry Pi Zero загрузит операционку и стартанет вот это вот все.

    Меня сильно впечатлила простота и увлекательность разработки моего первого IoT-устройства при помощи Nerves

    Толи еще будет если открыть для себя MicroPython для Raspberry Pico или esp32.


  1. safari2012
    29.05.2024 13:15

    Когда-то, много лет тому назад я спросил брата, что он хочет получить от меня на новоселье. Он подумал и сказал, хочу дистанционное управление кондеями, чтобы приехал и с работы и свежо...

    Я сваял схему с ESP8266, ИК передатчиком и приемником и датчиком температуры для обратной связи и облачным blynk на борту. Так я начал увлекаться IOT и DYI.

    А брат, получив девайс и поигравшись сказал "круто", купил себе Broadlink, чтобы заодно управлять всем остальным в гостиной и все остались довольны :)


  1. vagon333
    29.05.2024 13:15
    +1

    Может недопонял, но современные кондиционеры имеют опцию управления через WiFi.
    Неделю назад ставил сплит со стандартной опцией WiFi.
    Если что, летом у меня ад и это был 4ый сплит, вдобавок к центральному.


    1. GARANDDMI
      29.05.2024 13:15
      +1

      /Я включаю кондиционер через "умную" WiFi розетку.


      1. vagon333
        29.05.2024 13:15

        Прекрасное решение, но WiFi розетка не знает температуру и работает как On/Off, что не всегда приемлемо для современного кондиционера c inverter (управления компрессором по DC с изменяемым напряжением).
        WiFi в кондиционере регулирует режим, температуру, влажность и, конечно, On/Off.


    1. Sigest
      29.05.2024 13:15
      +1

      Так это видимо совсем недавно появилось. 5 лет назад ставил себе сплит кондеры и никакого управления по вайфай не было. Я не стал себе паять всякие малинки, купил обычный китайский ИК передатчик с возможностью подключения к вайфаю, и теперь удаленно управляю. Бонусом еще пару устройств в комнате повесил на этот дешевый ик девайс


      1. ABy
        29.05.2024 13:15

        Сейчас даже в бюджетных моделях на плате управления встречается разъем для подключения wi-fi модуля, но самого модуля в комплектации кондиционера нет.


    1. anton_tereshko
      29.05.2024 13:15
      +1

      не все современные кондеи умные, 2 недели назад ставил Daikin split + 2 головы и управление по wi-fi только в мечтах моих


    1. Javian
      29.05.2024 13:15
      +3

      Некоторые за это денег хотят, иначе превратятся в тыкву через месяц. И обычного ИК пульта у них нет. -  Daichi Alpha - кондиционер по подписке

      Облачный кондиционер работает только при наличии двустороннего доступа к интернет через Wi-Fi* и оплаченной подписке на Облачный сервис Даичи


      1. ABy
        29.05.2024 13:15

        Производители кондиционеров наверное думают: "Ну а чем мы хуже производителей принтеров?"


  1. De_Gun
    29.05.2024 13:15

    Простите, я конечно понимаю, что вы сделали это решение своими руками... Но точно не проще бы было готовое решение купить?

    https://aliexpress.ru/item/1005004516014637.html?sku_id=12000030593071117&spm=a2g2w.productlist.search_results.0.e2177537QchYgH


  1. roman901
    29.05.2024 13:15

    На самом деле - для подобных самоделок вообще больше не понимаю зачем писать какой-то код. Кусок ямла на 15-20 строк с помощи esphome.io превращается в офигенное умное устройство с интеграцией в HomeAssistant...


  1. venanen
    29.05.2024 13:15

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