Сразу скажу, что пост ориентирован скорее на обычных людей, чем на тех, кто в теме.
Я тут решил забить на всю работу и заняться чем-то для души. Снова взялся за паяльник. Решил автоматизировать дома всё и вся. На старой-то квартире у меня был умный дом или что-то типа того — мог свет в комнате включать через Интернет и всё такое.
На этот раз я решил учесть свои ошибки. Основной проблемой было то, что раньше у меня за всё отвечало одно устройство, к которому были подключены датчики температуры, движения, дисплей, кнопки и прочее. Всё это было здорово, но в итоге устройство выполняло только тот функционал, который был заложен в него изначально. Нельзя было так просто взять и подключить какой-то новый датчик, не переделывая это устройство.
Было решено, что лучше делать много отдельных устройств, каждое из которых отвечает за строго определённую задачу, имея возможность с лёгкостью подключать их к какой-то общей сети. И чтобы у каждого устройства был адрес и свой набор команд. Что-то вроде 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. Принципиально в FQN корпусе.
Впервые паял QFN корпус. получилось далеко не сразу, плата аж почернела от перегрева. Но заработало! Воткнул я это дело в роутер, ибо он работает круглосуточно, а связь мне нужна не просто с компьютером, но и с Интернетом.
В роутере же обычный Linux. После небольших танцев с бубном получилось подсосать к нему моё устройство. Далее пришлось вспоминать навыки программирования под никсы. Задача была не такая сложная — расшарить виртуальный COM порт в сеть с возможностью подключения к нему одновременно нескольких клиентов. Потом я сделал, чтобы при получении каждого пакета выполнялся простой скрипт. Таким образом, можно легко заставить роутер реагировать на различные события простым изменением shell-скрипта, без перезапуска демона. Потом сделал ещё и работу с fifo псевдофайлом, что дало мне возможность отправлять пакеты в сеть прямо из командной строки. Например, команда: echo «04010803» > fifo отправляет пакет с приоритетом 04 на устройство 01 с командой 08 (управление реле) и данными 03 (включить 1ю и 2ю лампы). Само собой, вручную это всё набирать мне не нужно, но сильно облегчает написание скриптов, которые всё автоматизируют. Под винду же была написана библиотека, которая подключается к демону на сервере и получает/отправляет пакеты. Полноценного софта ещё нет, но свет в комнате, телевизор и ресивер я уже могу включать горячими клавишами на клавиатуре, что очень облегчает жизнь.
Но потом я решил ещё написать свой загрузчик. Уже не представляю, как я мог бы обойтись без него. Он позволяет обновлять устройствам прошивку прямо по моей сети! Это маленькая програмка, которая сидит в конце памяти микроконтроллера и запускается перед основной программой. Вся её задача — подать признаки жизни, а затем либо запустить основную программу спустя несколько секунд, либо скачать и обновить прошивку, если поступает такая команда. С огромным трудом я уместил весь этот код в один килобайт. Счёт шёл буквально на байты. Реализована только самая основа — никаких проверок на свободность линии, ожиданий и прочих фишек. Только передача данных и проверка контрольной суммы, но для загрузчика этого вполне достаточно. Как же это здорово — экономить каждый байт памяти, работать напрямую с регистрами, оптимизировать код по полной… Низкоуровневое программирование приносит просто море удовольствия =)
Для компа же я написал соответствующую утилиту командной строки, которая отправляет устройству команду перезагрузки, команду перехода в режим прошивки и саму прошивку. Достаточно прописать её в Makefile проекта, и…
Нажатием одной клавиши в Programmer's Notepad я могу обновить прошивку любому работающему устройству в своей квартире. Без отключения, без паяльника, не отходя от компьютера. И весь процесс занимает 10-20 секунд. Это просто мегаудобно. Конфигурацию некоторых устройств гораздо проще поменять, изменяя саму прошивку, чем предусматривать всё заранее. Например, добавить в настенный выключатель реакцию на широковещательные пакеты, которые рассылает ДУ приёмник, что позволяет теперь включать люстру с пульта от DVD плеера. Напоминаю, что наличие мастера для этого не требуется, девайсы общаются друг с другом напрямую :) При этом нельзя убить устройство неудачной прошивкой — загрузчик всегда запускается раньше. Теперь каждое новое устройство достаточно прошить простеньким шаблоном, сделав минимальные изменения (ноги и ID устройства), после чего можно смело ставить его на своё место, а потом уже начинать писать для него прошивку :)
Сейчас у меня уже шесть устройств в моей сети, и пока что всё работает идеально :) Были проблемы только с шумом в питании, но меня опять же спас мой осциллограф. Надо будет купить полноценный.
Автор: ClusterM