Создание умного дома и разработка своего протокола

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

Я тут решил забить на всю работу и заняться чем-то для души. Снова взялся за паяльник. Решил автоматизировать дома всё и вся. На старой-то квартире у меня был умный дом или что-то типа того — мог свет в комнате включать через Интернет и всё такое.

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

Было решено, что лучше делать много отдельных устройств, каждое из которых отвечает за строго определённую задачу, имея возможность с лёгкостью подключать их к какой-то общей сети. И чтобы у каждого устройства был адрес и свой набор команд. Что-то вроде CAN-шины в современных автомобилях. При этом хочется, чтобы сеть была децентрализованной, без мастер-устройства, чтобы соединялись все по одному проводу, легко реализовывалось без покупки дополнительного контроллера, ну и чтобы длинные провода не были проблемой.

На борту микроконтроллера есть всякие I²C, да UART, но они явно не удовлетворяют условиям. В итоге было решено разработать свой велосипед протокол.

Имеется доминантный и рецессивный сигналы, как в CAN. В случае с проводами (кто знает, может я ещё и радио или ИК свет буду использовать) доминантный — это прижимание линии данных к земле. В обычном состоянии линия данных имеет подтяжку к +5В. Таким образом, когда устройство видит, что линия уже прижата к земле, оно понимает, что какое-то другое устройство уже шлёт данные, и ждёт освобождения линии. Сами данные кодируются длиною доминантных сигналов. 1T — ноль, 3T — единица, пауза между ними — 1T. Каждая передача начинается инициализацией в виде длинного сигнала в 10T. После многочисленных экспериментов на микроконтроллерной многоножке…

…я решил, что оптимальное значение T = 0.000064 секунды. При этом потерь (почти?) нет. Я не думаю, что по этой сети будет передаваться большой объём данных. Выводить сообщения из твиттера на дисплей в туалете если только.

Сами же пакеты данных имеют следующее строение: 2 бита приоритет пакета (на случай, если устройства шлют их одновременно, что очень маловероятно), 8 бит — адрес отправителя, 8 бит — адрес получателя (0xFF — широковещательная рассылка), 8 бит — номер команды, 8 бит — длина поля данных в байтах, соответствующее число байт данных и 8 бит — контрольная сумма, куда же без неё.

В итоге получилась библиотека, которая реализует работу с этим протоколом полностью на уровне прерываний. Т.е. получается этакая многозадачность с точки зрения остальной программы. Достаточно один раз выполнить инициализацию, и микроконтроллер будет отвечать на пинги, даже если основная программа вошла в бесконечный цикл. Она не подвисает, даже если при отправке пакета линия занята, библиотека отправляет его, как только линия освобождается. А при получении пакета на свой адрес вызывается указанная функция.

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

Надо отдать должное одной маленькой коробочке. Нормального осциллографа у меня нет, но несколько лет назад я заказывал на ибее самый дешёвый осциллограф на USB, для компьютера. Всего на 1MHz. Когда получил и попробовал, решил, что это бесполезный китайский ширпотреб, и я зря выкинул деньги, и отложил в дальний ящик. А теперь решил достать… Если бы не он, я наверное до сих пор всё это отлаживал бы. А на нём сразу всё видно и понятно.

Получилась сесть из трёх проводов — земля, питание и данные. Да, не хотелось возиться с питанием каждого устройства, поэтому я решил подвести везде и 9-12 вольт постоянного тока, а на каждом устройстве уже ставить банальную КРЕНку.

Однако, всю эту сеть ещё надо как-то связать с компьютером и Интернетом. Для этого я сначала сделал девайс на COM-порт:

Работала эта штука откровенно хреново. И почему я не решил делать сразу на USB? Не знаю. Позже была куплена микросхемка FT232. Принципиально в QFN корпусе.

Впервые паял QFN корпус. получилось далеко не сразу, плата аж почернела от перегрева. Но заработало! Воткнул я это дело в роутер, ибо он работает круглосуточно, а связь мне нужна не просто с компьютером, но и с Интернетом.

В роутере же обычный Linux. После небольших танцев с бубном получилось подсосать к нему моё устройство. Далее пришлось вспоминать навыки программирования под никсы. Задача была не такая сложная — расшарить виртуальный COM порт в сеть с возможностью подключения к нему одновременно нескольких клиентов. Потом я сделал, чтобы при получении каждого пакета выполнялся простой скрипт. Таким образом, можно легко заставить роутер реагировать на различные события простым изменением shell-скрипта, без перезапуска демона. Потом сделал ещё и работу с fifo псевдофайлом, что дало мне возможность отправлять пакеты в сеть прямо из командной строки. Например, команда: echo «04010803» > fifo отправляет пакет с приоритетом 04 на устройство 01 с командой 08 (управление реле) и данными 03 (включить 1ю и 2ю лампы). Само собой, вручную это всё набирать мне не нужно, но сильно облегчает написание скриптов, которые всё автоматизируют. Под винду же была написана библиотека, которая подключается к демону на сервере и получает/отправляет пакеты. Полноценного софта ещё нет, но свет в комнате, телевизор и ресивер я уже могу включать горячими клавишами на клавиатуре, что очень облегчает жизнь.

Но потом я решил ещё написать свой загрузчик. Уже не представляю, как я мог бы обойтись без него. Он позволяет обновлять устройствам прошивку прямо по моей сети! Это маленькая програмка, которая сидит в конце памяти микроконтроллера и запускается перед основной программой. Вся её задача — подать признаки жизни, а затем либо запустить основную программу спустя несколько секунд, либо скачать и обновить прошивку, если поступает такая команда. С огромным трудом я уместил весь этот код в один килобайт. Счёт шёл буквально на байты. Реализована только самая основа — никаких проверок на свободность линии, ожиданий и прочих фишек. Только передача данных и проверка контрольной суммы, но для загрузчика этого вполне достаточно. Как же это здорово — экономить каждый байт памяти, работать напрямую с регистрами, оптимизировать код по полной… Низкоуровневое программирование приносит просто море удовольствия =)

Для компа же я написал соответствующую утилиту командной строки, которая отправляет устройству команду перезагрузки, команду перехода в режим прошивки и саму прошивку. Достаточно прописать её в Makefile проекта, и…

Нажатием одной клавиши в Programmer’s Notepad я могу обновить прошивку любому работающему устройству в своей квартире. Без отключения, без паяльника, не отходя от компьютера. И весь процесс занимает 10-20 секунд. Это просто мегаудобно. Конфигурацию некоторых устройств гораздо проще поменять, изменяя саму прошивку, чем предусматривать всё заранее. Например, добавить в настенный выключатель реакцию на широковещательные пакеты, которые рассылает ДУ приёмник, что позволяет теперь включать люстру с пульта от DVD плеера. Напоминаю, что наличие мастера для этого не требуется, девайсы общаются друг с другом напрямую 🙂 При этом нельзя убить устройство неудачной прошивкой — загрузчик всегда запускается раньше. Теперь каждое новое устройство достаточно прошить простеньким шаблоном, сделав минимальные изменения (ноги и ID устройства), после чего можно смело ставить его на своё место, а потом уже начинать писать для него прошивку 🙂

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

Проект на GitHub: https://github.com/ClusterM/clunet

Комментарии

  1. Здравствуй Алексей !
    Очень понравилась твоя реализация сети для умного дома. Сейчас пытаюсь понять как все работает, ну а на будущее планы по портированию твоего протокола на архитектуру STM8.
    Хочу попросить у тебя исходники клиента для прошивки по сети и демона на маршрутизаторе.

    Удачи во всех твоих начинаниях!

  2. Будет ли статья со схемами и примером реализации для таких начинающих идиотов как я? По статье я понял общий принцип реализации, но моего LVL не хватает для попытки воспроизведения устройства. Больше понравилась как не дешевизна, а как простота и ничего лишнего по сути нет.

  3. Тоже нужна была сеть между устройствами автоматики дома. Думал сначала делать сеть на базе стандартного физического интерфейса. Думал или I2C или UART. Но на базе их коряво получается.
    CAN повторять программно думал, но у него куча недостатков. Много служебных данных, нет адреса отправителя, много памяти потратится у мелкого контроллера и тд.
    Твой интерфейс мне идеально подходит.(я уж на ты, давно на ютубе тебя смотрю) Был бы стандарт такой сети, народный что ли стандарт.
    Но есть вопрос. Поле DATA_LEN в посылке зачем? Ведь всё равно можно вычислить после приёма посылки сколько бит данные. Динамически то контроллеры мелкие обычно себе память не выделяют и не распределяют. Да и если бы умели, то данные медленно идут, можно и успеть выделить. 8 бит DATA_LEN это в среднем 24t, в среднем чуть меньше 15%.

  4. Алексей, здравствуйте. Расскажите пожалуйста поподробнее » расшарить виртуальный COM порт в сеть с возможностью подключения к нему одновременно нескольких клиентов»? И это не единственный вопрос. Не получается у меня подружиться с UNIX подобным исистемами.

Добавить комментарий