CI/CD для микроконтроллеров в Wiren Board

Расшифровка нашего доклада с вебинара «Конвеерум 2021, Испытания и тестирование»

Введение

У нас работает чуть больше 50 человек и мы массово выпускаем более 50 наименований продуктов от 500 до 2000 штук/артикул/год. Часть продуктов работает на Linux, но есть и embedded-устройства, построенные на микроконтроллерах Cortex M0 (STM32 и GD32).

В процессе работы мы сталкиваемся с проблемами массового производства и организации разработки для такого производства.

О спикере: Евгений Богер (@evgeny_boger) — технический директор и сооснователь Wiren Board. Занимается встраиваемыми системами и, в особенности, встраиваемыми Linux.

О чём пойдёт речь

Я расскажу об опыте внедрения CI/CD в нашей компании и о том, зачем это было нужно нам и почему может быть полезно вам.

CI (Continuous Integration) — это про быструю интеграцию изменений, которые возникают в программном коде при разработке новых фич и исправлении багов.

CD (Continuous Delivery) — это про быстрое развёртывание изменений в программном коде на пользователей. Зачем это нужно нам и может быть полезно вам?

Просто и плохо

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

Надо заметить, что это удел не только ардуинщиков и стартапов, но и некоторых компаний на рынке. И это разумно, когда за продукт есть ответственный, который всё делает.

Плюс такого решения — огромная производительность без затрат на коммуникацию внутри команды.

Минусы:

  • bus factor == 1 — если единственного разработчика сбил автобус, то для бизнеса будет всё плохо;
  • вы не можете масштабировать хороший продукт — не хватит ресурсов единственного разработчика;
  • если разработчик ушёл в отпуск, то вы не можете развивать продукт и закрывать в нём баги.

При таком подходе CI/CD поможет в решении некоторых задач.

Сложно и хорошо

Если вы хотите, чтобы несколько разработчиков работали над несколькими проектами, использовали наработки друг друга и было легко масштабировать продукт — без CI/CD жить не получится.

Плюсы:

  • bus factor == 0 — над продуктом работают несколько разработчиков и могут заменить друг друга;
  • масштабирование продукта — вы легко сможете добавить в команду новых разработчиков;
  • если разработчик ушёл в отпуск, то его функции берёт на себя коллега и продукт будет развиваться дальше.

Минусы — затраты на коммуникацию внутри команды, необходимо обязательно внедрять CI/CD.

Качество кода и процессы

Задача

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

Инструменты

Для решения этой задачи мы используем инструменты:

  • Git — система контроля версий;
  • продуманные и оформленные в Style guide гайдлайны по структуре проекта;
  • Code review — мы внедрили его на этапе внедрения фичи или правки бага;
  • Workflow — инструкции для всех участников проекта: как выполнять ту или иную задачу, что делать в определённой ситуации.

Я считаю, что если чего-то из списка выше у вас нет — пока думать о CI/CD рано, но можно внедрить эти инструменты и станет лучше.

Style guide

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

Code review

Нам очень понравился Code review, который мы внедрили на всех этапах работы над проектом и он помогает нам:

  • повысить качество кода;
  • сделать код понятнее;
  • отловить самые глупые ошибки;
  • познакомить разработчиков с проектом;
  • обучать новых разработчиков.
Code review
Обсуждение изменений в коде прошивки устройства

Оформление коммитов

Коммиты в нашем понимании должны быть:

  • гранулярные — работа разбита на кусочки;
  • атомарные — после каждого коммита проект должен работать;
  • понятные — зачем, что и как делает коммит;
  • rebase перед merge — помогает сохранить историю коммитов читаемой.
Пример истории коммитов одного из наших проектов
Пример истории коммитов одного из наших проектов

Воспроизводимость сборки

Задача

Первое, что нужно решить в CI — воспроизводимость сборки, она плохая, когда код:

  • находится на машине у единственного разработчика;
  • собирается в конкретной IDE, конкретной версии и с определёнными настройками;
  • с использованием каких-то библиотек, о которых не помнит даже сам разработчик;
  • с какими-то настройками и файлами;
  • собирается каким-то компилятором неизвестной версии.

Такой подход хорошо работает до тех пор, пока не потребовалось добавить ещё одного разработчика. Пример из нашего опыта до внедрения CI: новый разработчик неделю настраивал у себя среду разработки.

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

Тайное знание в голове избранных разработчиков — плохо.

Конфигурации

Конфигурации — это про то, как иметь код, который может работать на всех ревизиях и типах устройств.

Для нас нормальная ситуация, когда устройство в производстве и имеет 10–20 ревизий за время своей жизни. Часть ревизий может поддерживаться нами одновременно. Часто программный код разделяется несколькими похожими по функциональности или внутренней реализации устройствами. И весь этот зоопарк устройств надо как-то поддерживать.

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

  • не Makefile;
  • не «дефайны» компилятора;
  • не файлы конфигурации IDE;
  • не «ифдефы» на модели в коде.

Мы сделали это по-своему, но сперва я расскажу, как можно было бы сделать по-другому.

Два пути решения одной задачи
Выполнение кода можно привязать к модели или к функции устройства. Мы выбрали второй вариант

Плохо. Если вы хотите добавить новую модель или ревизию устройства, то кажется очевидным добавить в код условие, которое в зависимости от модели или ревизии будет выполнять определённый кусок кода. Например, если у нас модель MODEL_A, то конвертируем температуру.

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

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

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

Пример файла конфигурации устройства
Файл config.h с описанием устройств

Сборочная машина

Разработка у нас вся на Linux, код мы пишем на plain C в Visual Studio Code и собираем компилятором gcc. Нам показалось удобным, когда сборка делается на отдельной сборочной машине и автоматически.

Плюсы — хорошо интегрируется в процесс CI, потому что вы можете это автоматизировать, и это надо автоматизировать.

У нас отдельная физическая сборочная машина, которая при сборке делает git clone проекта и всех библиотек, а потом собирает проект. Таким образом, нет проблемы потерянных файлов, которые забыли закомментить, нет непонятных конфигураций и непонятных библиотек. Если вы это забыли или неправильно прописали в вашем коде — это просто не соберётся на машине. Это такой клин-билд с нуля.

Собирается так вообще всё, то есть вы сделали коммит в ветку, и машина это собрала, соответственно, один из этапов нашего тестирования — прошивать в железки собранные бинарники прошивок с нашей сборочной машины на случай, если у разработчика собралось как-то не так.

Сделали мы это всё на Jenkins — это старое решение, сейчас есть новые облачные, но зато он умеет всё и настраивается очень гибко.

Выглядит это примерно так: в проекте есть ветки (Branch), и у каждой ветки есть статус, когда она собрана в последний раз. Внутри каждой ветки можно посмотреть все билды (bilds), какие этапы сборки были провалены, сколько времени занял каждый этап. Здесь же можно посмотреть логи и скачать артефакты сборки, то есть готовые пакеты или прошивку.

Первый блин комом. Когда мы начали всё это делать — у нас был Eclipse. Мы запускали Eclipse в режиме Headless — у нас он использовал gcc и генерировал из своих проектов Makefile. Теоретически, Eclipse и сгенерированный им Makefile можно было запускать на билд-системах, что мы и делали.

Но это принесло много страданий:

  • Eclipse написан на Java, поэтому он тяжёлый;
  • у него есть проблема с версиями — его тяжело устанавливать и обновлять на билд-системе;
  • Eclipse ограничивает разработчиков в выборе IDE;
  • его конфигурация — это xml-файлы, которые очень тяжело ревьювить и отвратительно держать в системе контроля версий.

Поэтому мы переехали на самописную систему сборки.

Самописная система сборки. Она у нас очень компактная и представляет из себя gcc, make и 150 строчек Makefile-инклюдов для всех наших проектов, которые подключены как git-сабмодуль.

Несмотря на свой малый размер, она умеет делать то, что нам нужно:

  • умеет собирать разные таргеты, то есть один make может собрать под все ревизии, под все устройства подряд;
  • упаковывает бинарники для использования при прошивке;
  • формирует версии;
  • запускает тесты.

Это я про то, что если перед вами стоит дилемма — надо использовать Makefile или нет — надо. Makefile — это здорово, причём мы обошлись даже обычным make. В итоге для каждого проекта у нас есть очень компактный Makefile, в котором есть список моделей, таргетов, библиотек и файлов.

Пример файла конфигурации
Пример Make-файла одного из проектов

Версии

Система версий. Очевидно, что версии должны быть. Также очевидно, что многие этим пренебрегают: делают инкрементальные номера билдов, кодируют дату сборки. Это всё очень плохо: неудобно для техподдержки, неудобно для Errata, неудобно для чейнджлогов (списков изменений).

Мы решили задавать версии вручную при вливании изменений в стабильную ветку репозитория и нумеровать их по Semver (Semantic Versioning).

Semver или Семантическое версионирование — это система нумерации, при которой у вас есть три циферки MAJOR.MINOR.PATCH и каждая что-то значит:

  • MAJOR — большие изменения;
  • MINOR — маленькие изменения;
  • PATCH — исправления.

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

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

Информация для отладки
Отладочная информация, которую предоставляет наше устройство

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

Несмотря на то, что в Git есть все коммиты, они красивые и подробные — мы при релизе, в момент вливания ветки и присваивания версии пишем человеческим языком, разработчик для разработчика:

  • что там поменялось;
  • какие изменения были;
  • какие функции добавились;
  • какие были затронуты.

Это хранится текстом в том же репозитории, в файле changelog и упрощает нам жизнь.

Фрагмент файла changelog
Фрагмент changelog-файла прошивки одного из устройств

Внешняя Errata. Чем быстрее у вас разработка, тем больше у вас будет багов — это данность. Вы можете уменьшить количество багов, внедрить все методы, а потом у вас просто одна ручка «Разрабатывать меньше» или «Разрабатывать больше».

Если вы ручку выкрутили в «Разрабатывать больше», то у вас появятся баги и вы должны на них реагировать. Методики CI/CD помогут вам быстро выявлять и исправлять баги, а также доставлять исправления клиентам. Но мы ещё делаем Errata.

Errata у нас — это публичный список ошибок, который предназначен не только для наших инженеров техподдержки, но и для пользователей. Публичная Errata не распространена в мире софта или в мире закрытых от всех корпораций, но распространена у производителей микроконтроллеров: они делают часть Errata публичной.

Мы делаем Errata публичной всю, так как я считаю, что если вам что-то стыдно туда писать — не нужно было этого делать. Все Errata доступны у нас на сайте, в документации на устройства.

Минусы в том, что пользователи увидят, что было много ошибок в продукте.

Плюсы — пользователь начнёт вам доверять, так как если в WB-MSW v.3 написано 6 ошибок, то их там шесть, а не 66.

Фрагмент Errata датчика WB-MSW v.3
Фрагмент публичной Errata датчика WB-MSW v.3

Тестирование при интеграции

Задача

Если сравнивать тестирование при интеграции с другими видами автоматизированного тестирования, то оно самое полезное: его проще писать, оно хорошо покрывает код и выявляет много ошибок. Но вместе с тем, это самое тяжёлое во всём CI, про это пишут отдельные книжки, а в Embedded оно особенно тяжело:

  • микроконтроллер нельзя изолировать, его API — это регистры, DMA, прерывания;
  • сложно эмулировать: куча внешних устройств, например, если вы подключили аналоговый датчик звука, то вы должны его в вашей системе эмуляции реализовать.

Это больно, сложно и мы пока не решили эту задачу хорошо, но каждый день решаем всё лучше и лучше.

Важно, что интеграционное тестирование у нас затрагивает не только периферию на плате, но ещё, например, соответствующий софт на ПЛК, который с тестируемыми железками будет взаимодействовать.

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

Ручное тестирование по чек-листам

Ручное тестирование — это чек-листы, по которым разработчику нужно внимательно пройти при интеграции. У нас нет выделенных тестировщиков и мы тестирование поручаем тому же разработчику, который делал эту функциональность. По нашему опыту это сильно уменьшает количество взаимодействий внутри команды и увеличивает качество кода, отправленного на интеграцию.

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

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

Функциональное тестирование производства

Задача. У нас есть производство и на нём мы внедрили выходное функциональное тестирование в конце конвейера. Для этого мы используем специальные оснастки и свой фреймворк тестирования.

Также эти стенды мы используем при разработке прошивок — перед интеграцией мы выполняем функциональное тестирование устройства.

Тут есть довольно хитрый момент — у производства задачи тестирования не такие как у разработки. Возьмём, к примеру, устройство управления кондиционером, которое по ИК отправляет команды. Отправка команд по ИК может сломаться при производстве в трёх местах:

  1. бракованный или неприпаянный ИК-светодиод;
  2. нерабочая флеш-память, которая не сохранит при пропадании питания записанную в устройство команду;
  3. флеш-память может быть не того размера: припаяли не ту, она работает, но при сохранении большого объёма данных она не сохранит их.

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

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

Например, стенд тестирования датчиков WB-MSW v.3 проверяет децибелы и поэтому обшит поролоном. Звуковой тракт датчика WB-MSW v.3 устроен так: есть микрофон, стоят операционники и у них есть три параметра, которые могут повлиять на результат. Их проще тестировать — нужно вывести тест-поинты, подключить JIG и посмотреть значения.

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

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

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

Юнит-тестирование

Юнит-тесты — это хорошо, они позволяют тестировать алгоритмы, преобразование данных и т.п.

Мы смогли адаптировать их для автоматизированного тестирования прошивок. Проблема какая — здесь юнит-тесты должны отделяться от аппаратно зависимых частей. Обычно при правильной архитектуре кода это получается само — алгоритмы, бизнес-логика, преобразование данных выделяются в отдельные, аппаратно независимые модули.

На необходимость юнит-тестирования есть много мнений, но в целом, если вы сделали адекватную архитектуру, то в ней найдутся вещи, которые можно отделить от аппаратных частей и протестировать.

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

Наши юнит-тесты — это main, который собирается уже под x86 на хост-машине. При этом он собирается с хедерами и си-файлами, которые мы хотим тестировать. Внутри он делает какой-то тест и выдаёт либо ошибку, либо статус «всё хорошо».

Папка с юнит-тестами в репозитории
Папка с юнит-тестами в репозитории

Развёртывание

Задача

Теперь о CD — это проще, это развёртывание или доставка до клиентов.

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

Обычно наши устройства подключены по RS-485 к нашему контроллеру или стороннему ПЛК, и у них нет своего интернета. Учитывая это, мы постарались сделать развёртывание максимально безболезненным для наших клиентов.

В нашей инфраструктуре обновления прошивок для этого сделано: загрузчик, который загружает прошивки, система сборки, система доставки и некоторые инструменты для обновления прошивок.

Загрузчик

Так как наши устройства работают в полях, то мы хотим обновлять прошивки в инсталляциях по RS-485. Наши устройства не надо доставать из инсталляции и везти к нам офис или подключать к компьютеру на месте. В идеале — написали команду и всё обновилось прямо в инсталляции.

Частая схема обновления прошивки — это когда новую прошивку складываете где-то рядом, переключаетесь на неё и, если что-то пошло не так, то вы откатываетесь на предыдущую прошивку. Этот метод называется A/B.

В наших устройствах ограниченные ресурсы (16 Кбайт флеш, 4Кбайт RAM) и в них нет места для обновления методом A/B. Из-за этого нам приходится обновлять флеш «по живому». При этом мы хотели, чтобы в случае если обновление пойдёт не так (пропало питание или что-то такое), то устройство не «окирпичивалось». То есть, несмотря на то, что места для A/B нет и пишем мы по живому — базовую функциональность мы должны сохранять.

Другая задача — защита наших устройств от копирования. Так получается, что в нашей разработке 5 к 1 соотношение по разработке алгоритмов, прошивки, продвижению к разработке железа. Поэтому нам было бы очень грустно, если бы нашу прошивку копировали и делали на такой же STM-ке похожее реле, но плохо спаянное, с плохими компонентами, со старой прошивкой и продавали бы это. Поэтому мы эти прошивки охраняем, а значит распространять их нужно шифрованными по закрытому каналу.

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

Поэтому мы написали свой загрузчик, он реализует подмножество Modbus RTU по RS-485. Мы так сделали, так как у нас основной протокол устройств Modbus RTU и потому, что они используются с ПЛК сторонних производителей, где есть доступ только поверх Modbus-абстракций. То есть у вас нет команды «записать два байта в шину», но есть команда «записать регистр».

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

Далее загрузчик расшифровывает блоки прошивки, проверяет сигнатуру устройства и, если она совпадает, складывает прошивку во флеш. Сигнатура устройства — это идентификатор, который отличает одно устройство от другого, физически несовместимого. Например, аппаратную ревизию 4.9 от ревизии 4.10.

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

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

Всё это мы написали на plain C и уложили в 4 Кбайта: свой код инициализации, векторных прерываний и т.д.

Сборка прошивок

Прошивки собираются автоматизированно и на том самом билд-сервере, тем самым Jenkins, про которые я рассказывал в части по CI.

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

Если у разработчиков возникает желание очень плотно разрабатывать: наделать дебаг-принтов, собирать прошивку каждые две секунды, то у них есть режим сборки без шифрования. Такую сборку можно использовать только внутри компании на специальных устройствах — так мы предотвращаем её утечку.

В каждой сборке есть артефакты, то есть подписанные шифрованные файлы прошивки. Они сделаны под все сигнатуры, под все таргеты и под все версии устройств, которые были.

Структура наименования файлов прошивок
Структура наименования автоматически собранных файлов прошивок

Доставка

Как это разъезжается по клиентам. Для начала с билд-машины файлы прошивок нужно выложить в интернет. Мы выкладываем на файловое хранилище Amazon S3 — нам так удобно, но непринципиально.

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

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

Зачем? Ну, например, потому что удобно, когда вам пользователь сообщил про какой-то баг, вы его начали исправлять, сделали для этого ветку. И в момент, когда вы у себя проверили, что работает — вы можете ещё пользователю сразу отправить ссылку на ветку и попросить его проверить у себя, исправлен ли баг. Так вы не затронете остальных клиентов, а этот пользователь будет ещё и следить за вашей разработкой. То есть он вам скажет, что это вот не починилось, вы сделаете ещё одну итерацию и в той же ветке ему прилетит обновление.

Инструментарий обновления. Мы написали на Python утилиту для наших и совместимых контроллеров — wb-mcu-fw-updater. Вы просто запускаете её из консоли и говорите ей, чтобы она обновила все устройства на шине. Утилита проверит версии прошивок в устройствах, посмотрит список прошивок на сервере и, если нужно обновить устройство, обновит его. В конце выдаст отчёт, в котором скажет, какие устройства были обновлены, а какие нет и почему.

Обычно когда у клиентов возникают известные проблемы с устройством, то мы говорим: «Выполните команду обновления», — и в инсталляции что-то происходит, и дальше всё работает хорошо. Это и есть цель всего мероприятия по CD.

Пример работы утилиты wb-mcu-fw-updater
Пример работы утилиты wb-mcu-fw-updater

Ещё у нас есть низкоуровневая утилита wb-mcu-fw-flasher, которую можно использовать для совсем embedded-железа, например, роутера на WRTI и с 8 Мбайт оперативки. Эта утилита написана на C, маленькая, простая, и она просто умеет скормить файл прошивки устройству через RS-485. Также она служит референс-реализацией нашего открытого протокола обновления. Если вы хотите его реализовать, можно посмотреть как мы это сделали в этой утилите.

Резюме

В докладе я говорил о том, зачем вообще CI/CD может понадобиться, когда про него надо думать, а когда про него думать рано. А рано про него думать, если вы у себя процессы не настроили вокруг качества кода, то есть Code review, использование Git и т.д.

CI начинается с воспроизводимости сборки. Основная часть CI — тестирование, а в embedded тестирование организовать особенно тяжело. Мы у себя сделали, как смогли: часть — юнит-тестирование, часть — автоматизированное функциональное тестирование, часть — вообще ручное.

Ну и самая простая, но важная задача — публикация прошивок. То есть если ваши прошивки не доезжают до клиентов, тогда зачем вы их быстро делали? Зачем вообще напрягаться, если доставка у вас занимает полгода, пока курьер съездит до вашей инсталляции. Поэтому по поводу развёртывания — это просто, но очень важно.

Полезные ссылки

Мы подобрали интересные ссылки, которые помогут продолжить знакомство с нашей компанией:

Евгений Богер, Wiren Board 2021 г.

Условия покупки

Общие положения

1. Настоящие Правила продажи разработаны на основании Гражданского кодекса РФ, Закона РФ от 7 февраля 1992 г. № 2300-1 «О защите прав потребителей», постановления Правительства РФ от 27 сентября 2007 г. № 612 «Об утверждении правил продажи товаров дистанционным способом» и иных нормативно-правовых актов. Настоящие правила регулируют порядок розничной купли-продажи Товаров через Интернет-магазин, являясь публичной офертой, адресованной физическим и юридическим лицам («Покупателям»).

2. Покупатель принимает условия, изложенные в настоящих правилах путем оформления Заказа на сайте Интернет-магазина.

Заказ считается оформленным надлежащим образом в случае, если Покупателем предоставлена Продавцу следующая информация: Фамилия, Имя, Покупателя, телефон, адрес по которому следует доставить Товар, наименование Товара, количество.

2.1. Покупатель предоставляет Продавцу свое согласие на обработку персональных данных, а также право использования всех переданных в Заказе персональных данных с целью выполнения своих обязательств перед Покупателем, формирования и развития программы лояльности для Покупателей, информирования Покупателей о новостях Интернет-магазина.

3. При продаже товаров дистанционным способом Продавец предлагает Покупателю услуги по доставке Товаров Службами доставки. При этом конкретная Служба доставки для каждого Заказа определяется Покупателем при оформлении заказа.

Доставка осуществляется в будние дни с 9:00 до 18:00. Сроки доставки зависят от выбранного региона и составляют от 1 до 14 рабочих дней (не считая дня заказа). Срок доставки может быть увеличен в случаях, предусмотренных правилами работы Службы доставки. Доставка осуществляется без демонстрации. Если Вы не согласны со сроками доставки, то Вы вправе отказаться от своего заказа в любое время до его получения.

Ознакомиться с тарифами на доставку по регионам можно на сайте курьерских служб: СДЭК, КСЭ, EMS.

В случае отказа Покупателя от Товара с Покупателя взимается стоимость доставки в соответствии с тарифом согласно пункту 21 Постановления Правительства Российской Федерации от 27.09.2007 № 612 «Об утверждении правил продажи товаров дистанционным способом».

При получении Товара от Службы доставки Покупатель обязан проверить внешний вид упаковки Товара (на отсутствие деформации, вскрытия). При получении Товара Покупатель ставит свою подпись в квитанции о доставке в графе: «Я подтверждаю, что отправление принято в закрытом виде, отсутствуют внешние повреждения упаковки, перевязи, печатей (пломб), вес отправления соответствует весу, определенному при его приеме».

В случае обнаружения каких-либо дефектов на упаковке Товара Покупатель обязан сделать об этом отметку в квитанции курьера в графе доставка. После получения Заказа и проставления подписи Покупателем в квитанции в графе доставка без указания претензии к внешнему виду упаковки Товара, претензии к внешним дефектам упаковки (ее вскрытию и возможной некомплектности Товара) не принимаются.

4. Оплата заказанных Товаров осуществляется:

  • Банковским переводом по счету выставленному для Покупателя.
  • Пластиковой картой по ссылке, сгенерированной для Покупателя.
  • Оплата по QR-коду, сгенерированному для Покупателя (оплачивая заказ данным способом, вы соглашаетесь с условиями Договора публичной оферты).

5. Обработка заказов производится в рабочие дни с 10:00 до 18:00 по Московскому времени. В случае если Вы оформили заказ после 18:00 по Московскому времени, менеджеры свяжутся с Вами на следующий рабочий день после 10:00 часов утра.

6. Товар надлежащего качества, Вы можете вернуть согласно условиям возврата.

Для этого необходимо написать электронное письмо с заявлением на возврат/обмен на info@wirenboard.com.

Обращаем внимание, что при отказе Покупателя от Товара, Продавец возвращает сумму, уплаченную Покупателем за Товар, за исключением расходов Продавца на доставку от Покупателя возвращенного Товара, не позднее, чем через 10 дней с даты предъявления соответствующего требования.

7. Гарантийный срок на Товар устанавливается в размере 24 месяцев с даты отгрузки.

8. Риск случайной гибели или случайного повреждения Товара переходит к Покупателю в момент передачи ему Товара и проставления Покупателем подписи в документах, подтверждающих доставку Заказа. Право собственности на Товар переходит к Покупателю в момент передачи Товара, при условии его полной оплаты.

9. К отношениям между Покупателем и Продавцом применяется законодательство Российской Федерации.

10. Продавец оставляет за собой право вносить изменения в настоящие Правила продажи, в связи с чем Покупатель обязуется регулярно отслеживать изменения в Правилах, размещенных на сайте Интернет-магазина. Уведомление об изменении настоящих Правил продажи Продавец обязан разместить не позднее, чем за 7 (семь) календарных дней до даты их вступления в силу.

11. Покупатель гарантирует, что все условия настоящих Правил продажи ему понятны, и он принимает их безусловно и в полном объёме.

12. Недействительность какого-либо положения настоящих Правил не влечет за собой недействительность остальных положений.

13. Все возникающее споры Стороны будут стараться решить путем переговоров, при недостижении соглашения спор будет передан на рассмотрение в арбитражный суд г. Москвы в соответствии с действующим законодательством РФ.