Автоматизация автодома · Into unknown

Автоматизация автодома


March 7, 2024

Длиннопост о моих изыканиях по части автоматизации автодома. О пути, который я прошёл, и почему мне было необязательно по нему идти. :-) О недочётах, и о находках с победами.

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

Угрюмо заставляю буковки превращаться в прошивку микроконтроллера
Угрюмо заставляю буковки превращаться в прошивку микроконтроллера

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

Техническое задание

Я выдвигал следующие требования ко своей системе.

Стек технологий

Железо, на котором всё крутится

Разработку я веду на своём любимом, личном ноуте. Вот так просто, и без затей.

Запуск же сервисов “на постоянку” я должен делать на сервере, который настраиваю на постоянную бесперебойную работу. Он будет заниматься всей автоматизацией, собирать данные, и держать вечно запущенным GUI. В автобусе я закреплю его на одной из стен. Зная себя, он вечно будет с закрытым монитором, и подходить к нему буду только при большой необходимости. Ведь я делаю автоматизацию для того, чтобы всё происходило “само”, и я чтобы я лишний раз никакие кнопочки не нажимал.

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

Язык программирования

Тут без вариантов – C++!

Мой главный язык программирования! Я уже умею решать в нём широкий спектр задач. От прошивки микроконтроллера, до создания графического интерфейса. С ним трудно лишь самое первое время, когда отлаживаешь среду программирования, и заставляешь инструменты отладки работать. Потом – всё больше кода становится copy-paste. Не буквально, но концептуально – мало думаешь как будешь что-то отлаживать. Просто придерживаешься внутреннего стиля, и концентрируешься на решаемой задачке. Это не что-то новое – в каждом проекте так складывается. Раньше, или позже.

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

GUI

Графический интерфейс решил делать на Qt. Он мне достаточно знаком. Методики написания кода для него – тоже.

Когда/если придёт время – смогу добавить картинки, и будет как настоящая SCADA!

Source
Source

Паттерн PUB-SUB

Из работы с беспилотными тачками – очень понравился шаблон “PUB-SUB” (публикация-подписка). Когда какая-то нода публикует сообщение в абстрактный канал в удобное для себя время. Например, по адресу /can/node13. И другие ноды, кому это надо, на это сообщение подписываются. Отсюда естественным образом начинаешь программировать с мыслью в голове, что каждая нода (node – “узел”) может появляться, и исчезать. Ты не знаешь её состояния. Это естественным образом приводит к написанию логики, которая учитывает этот важный аспект. Так, мы реализуем слабосвязанность – когда узлы системы могут, независимо, появляться и исчезать. В моём случае, это может быть по разным причинам:

Безумно удобно публиковать отладочные данные в каналы – потом открываешь их, и быстро становится понятно что происходит у каждой из нод. Зная, кто от кого зависит, можно очень быстро, извне, неинвазивно увидеть что идёт не так.

GUI с открытой вкладкой 'Монитор' для наилегчайшего доступа к любому из каналов
GUI с открытой вкладкой 'Монитор' для наилегчайшего доступа к любому из каналов

В качестве либы, которая бы позволила перекидываться сообщениями без лишней мороки с соединениями – выбрал zeromq. Восхитительная штука! Я неоднократно программировал работу с сетью, и никогда ещё работа с нею не была настолько стабильной и простой! Кажется, что связь между компьютерами теперь – это магия, и абстракция!

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

Сериализация сообщений

Для упаковки структурированных сообщений использую nlohmann/json. Просто потому, что он текстовый – очень легко вывести в виде текста дабы разобраться что, чёрт возмьи, происходит. Лучше всего, в таких задачах, подходят protobuf тот или иной. Но с ним тяжелее старт, и для отладки надо отдельно прикручивать сериализатор, чтобы отображать структуру сообщения в текстовом виде.

Среда для разработки

Чтобы легко перенести наработки с одного сервера на другой – решил использовать docker для контейнеризации. Собрал образ на 900Мб, в котором собрал всё, что нужно для разработки и работы. И имею его одинакового на ноуте, и сервере.

Редактор – мой любимый vscode.

Написание прошивки микроконтроллера

Это был самый стрессовый, и растянутый во времени этап. Дело в сложности отладки. Особенно первые шаги – когда мы пытаемся хотя бы просто светодиод зажечь. Для этого код должен не только скомпилироваться, но его надо загрузить в нужное место. И правильно разместить ссылки на фукнции в векторе прерываний. Когда светодиод загорелся – движешься вперёд осторожными одиночными шагами. Так как, если написал сразу большой кусок логики, бывает сложно разобрать где именно ошибся. Пошаговая отладка по SWD имеет место быть, но, зачастую, не позволяет отловить фундаментальные ошибки. Отладка аппаратных устройств сильно отличается от чисто программных продуктов – реальный мир здесь может дать поддых сильно, но ты не будешь знать откуда именно.

Стек для написании прошивки следующий:

Огромным ограничением оказалось отсутствие операций с плавающей точкой (ARM Cortex-M0 на wiki)! Это сумашедшая подножка, о которую я споткнулся в самый последний момент! Поразительно насколько всё поменял один лишь этот факт. Если б не эта проблема – конфигурация была бы совершенно иной! Поразительно, насколько просто можно было это проверить. Но это сейчас так кажется – тогда, в начале пути, ты ходишь в тёмной комнате. Если б не отсутствие FPU – я сделал бы систему децентрализованной. И по шине передавалась только телеметрия (по RTR-запросу), и необходимый минимум сообщений между нодами, которым это явно необходимо. Для сбора статистики сделал бы отдельную ноду, которая собирала б периодически эту телеметрию, и сохраняла в самодельном формате на ту же SD-карточку. Это можно делать по SPI – для этого даже отдельных микросхем не надо. При необходимости, достаём её, и смотрим что нас интересует. Даже небольшой карточки хватит на очень длительный интервал. Да и с отсмотром в реальном времени тоже можно было бы что-то нехитрое придумать. Надёжность системы была бы НАМНОГО ближе к моим пожеланиям. Но, я облажался. У меня есть мысли сделать проект uni_v3, в которой текущие претензии были бы учтены:

Но, пока текущий подход работает, и у меня и так полно других подзадач – будем работать с тем, что есть. “Нет ничего более постоянного, чем временное” (с) – я готов к тому, что текущая неидельная реализация вполне может оказаться достаточно стабильной, чтобы я не хотел ничего в ней менять в течение продолжительного времени. И это нормально!

Шина микроконтроллеров

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

Пока остановился на скорости $125\frac{kbit}{s}$. Это позволит публиковать мне не менее:

\(\frac{125 \cdot 10^3 \frac{bit}{s}}{157 \frac{bit_{max}}{message}} = 796 \frac{msg}{s}\).

При такой низкой, относительно, частоте, разного рода помехи и неаккуратности при реализации будут играть малую роль. Это уменьшает мои шансы, что мне придётся отлаживать “физику” – распространение сигнала по шине. Я ожидаю около 15 нод на весь автобус. Это даёт мне “бюджет” в 50 сообщений/секунду для каждой ноды. Это довольно много!

У ноутбуков нет интерфейса для подключения к CANbus. Необходим был конвертер интерфейса, который открыл бы доступ к CAN-шине с ноутбука.

ELM327

Я начал с экспериментов с отладочных USB-девайсин ELM327.

Сверх-дешёвый ELM327 для диагностики авто
Сверх-дешёвый ELM327 для диагностики авто

Они используются в автодиагностике – подключаются к CAN-шине авто, и позволяют оперировать с кодами ошибок вроде check engine, читать телеметрию. Этот подход оказался мертворождённым:

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

У ELM327 есть только два преимущества – их полно, как грязи, и они дёшево стоят.

CANET200

Поиски альтернативы отладочному интерфейсу для работы с CANbus привели меня к неприятному осознанию – довольно мало существует надёжных устройств, которые адаптированы для работы с ПК. А те, что есть, – индустриального уровня, и с корпоративными ценниками. Скажем, 20-40круб за устройство. Плюс, проприетарный драйвер под Window. А у меня Linux! Есть устройства PCI и PCI-Express, но у меня ноутбук! Да и собирать стационарник только ради этого казалось чрезмерным.

На своё счастье на своём любимом Ozon я наткнулся на идеально подходящий девайс – CANET200!

CANET200 собственной персоной!
CANET200 собственной персоной!

Удивительно насколько хорошо он отвечает всем моим, даже смелым, хотелкам!

Мои минимальные хотелки: работа с CAN1 по UDP, чтобы не надо было менеджить соединением. Идеал!

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

Так или иначе, эту задачку я решил!

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

Очень долго мы писали, наши глазоньки устали

Много времени ушло на выработку методики и детали архитектуры. Как именно передавать данные, где какой функционал хранить. Спустя десяток итераций, путём реализации одного модуля за другим, подход удалось отточить.

Отладка работы по CANbus с помощью логического анализатора
Отладка работы по CANbus с помощью логического анализатора

Довольно давно я приобрёл логический анализатор “на всякий случай”. Дешёвый – <1круб. У меня были от него ожидания гораздо меньше, чем от осциллографа. Но на этапе отладки работы по CAN он оказался просто незаменим! С его помощью удалось супер-быстро и легко проверить корректность отправки данных по шине. Убедиться, что данные передаются без ошибок и устройчиво. Даже при максимальной нагрузке на шину, когда начинает играть роль арбитраж при одновременной отправке данных с нескольких подключённых устройств.

Логический анализатор: аналог Saleae. Программа на Linux: Pulse View. При подключении анализатора выбираем устройство fx2lafw. Дюже забавно, что 100% этой связки – это Open Source. Всё это указываешь, и всё работает очень стабильно, прямо “из коробки”! Такие победы здорово вдохновляют быстро двигаться дальше!

Интерфейс Pulse View, в которой работаю с логическим анализатором
Интерфейс Pulse View, в которой работаю с логическим анализатором

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

Пример расшифрованного CAN-сообщения
Пример расшифрованного CAN-сообщения

Имея такую расшифровку сообщения, напрямую полученного из “физики”, позволило почти мгновенно устранить пару мелких логических ошибок в программировании, и, даже, проверять мелкие гипотезы. Сразу наблюдая что, в действительности, отправляется на шину.

Чёрный ящик

“Чёрный ящик” плохо подходит для описания. Это просто самописец, с помощью которого я могу отлаживать длительно идущие процессы, и анализировать долго собираемую статистику.

Dashboard для моих микроконтроллеров
Dashboard для моих микроконтроллеров

На примере выше можно увидеть, что они сами публикуют свои данные, и они попадают в систему. Мне даже делать ничего не надо. По температуре самого чипа видны дневные флуктуации температуры в комнате – ну разве не здорово? Пока писал об этом – обнаружил ошибку в работе алгоритма насоса. Он почему-то самопроизвольно перебирает разные мощности насоса. За короткое время я это не обнаружил. Но вот оставил его на некоторое время – и такие эффекты проявились явной “пилой” на одном из графиков. В другой день я зашёл, и увидел, как регулярно съедалась память, и место на диске. По времени удалось понять с каким именно моим действием это было связано, что позволило быстро локализовать и устранить проблему. По факту наличия данных можно будет судить о стабильности подключения ноды. Если она постоянно теряет контакт – то будут пропуски. Ради такого и собираю всю эту шелуху! Когда система стабильно заработает – я сюда вряд ли буду заходить кроме как иногда утолить кусочек любопытства.

Оглядываясь назад – прошёл бы тем же путём?

“Знал бы прикуп – жил бы в Сочи” (с)

Я точно усомнился в том, что стоило делать систему централизованной. Это уже заставляет меня тревожиться, и резко увеличило стоимость. Как денежную, так и количество потраченных сил. Тот же результат можно было получить вложившись в децентрализованные ноды, каждая из которых сама вычисляет всё что ей необходимо. Я уже успешно это делал даже без операций с плавающей точкой, и вообще без операций деления на самой ноде – только с помощью +->><<*~ над целыми числами. Это взрывает мозг, но я больше времени потратил на наладку централизованного подхода, чем на всю работу над прошивкой микроконтроллеров. F-f-f-фрустрация!

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

Хотяяяя… кто знает, вдруг эта простота так же обманчива? :-)

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

Вывод

Была проделана огромная работа. Удалось сделать всё почти в точности то, что планировалось. Горд своими способностями, и доволен полученными результатами!

Хотелось бы, чтобы не надо было тратить на мой подход такого количества ресурсов.

Пришлось самому освоить fullstack в различных направлениях:

Uff! Спасибо, что дочитали!