Автор статьи: Рустем Галиев

IBM Senior DevOps Engineer & Integration Architect. Официальный DevOps ментор и коуч в IBM

Привет Хабр! Недавно принял участие в достаточно интересном воркшопе и хотел бы поделиться приобретенными навыками.

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

Немного предисловия

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

Yocto Project — это набор инструментов, позволяющий создавать настраиваемые Linux-системы для встраиваемых устройств и систем. Он предоставляет набор средств, позволяющих разработчикам создавать специализированные Linux-дистрибутивы, оптимизированные под конкретные требования и характеристики встраиваемых устройств.

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

Основные компоненты Yocto включают в себя BitBake (система сборки пакетов), OpenEmbedded (набор рецептов для построения пакетов и файловой системы) и метаданные, определяющие параметры сборки, пакеты и конфигурации.

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

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

Что нам нужно из реквизита? Спасибо IBM, что заранее все предоставили.

  • Система-хост на базе Linux с минимум 60 ГБ доступного места на диске;

  • Релиз Yocto 3.1 (Dunfell) LTS;

  • Etcher для Linux;

  • Считыватель microSD-карт и сама карта;

  • Raspberry Pi 4;

  • Источник питания USB-C 5 В 3 А;

  • Ethernet-кабель и порт для сетевого подключения;

  • Wi-Fi маршрутизатор.

Начнем с BSP

Пакет поддержки платы (Board Support Package, BSP) представляет собой набор программного обеспечения, необходимого для поддержки определенного аппаратного оборудования в контексте разработки программного обеспечения. Этот пакет включает в себя драйверы устройств, загрузчики, файлы описания аппаратуры (device tree blobs), иногда дополнительное ПО для работы с железом и другие компоненты, обеспечивающие корректную работу операционной системы на конкретном оборудовании.

BSP обеспечивает связь между аппаратной платформой и операционной системой. Он включает конфигурации, необходимые для запуска операционной системы на конкретном устройстве, обеспечивая поддержку аппаратных компонентов, таких как процессоры, контроллеры устройств ввода/вывода (GPIO), интерфейсы коммуникации (например, Ethernet, USB), а также другие периферийные устройства.

В контексте проектов, использующих Yocto или OpenEmbedded, слои BSP добавляют поддержку конкретных устройств или семейств устройств в рамках сборки пользовательских Linux-образов. Они включают настройки сборки, необходимые для создания образов, оптимизированных под конкретные характеристики железа, что позволяет создавать настраиваемые операционные системы для различных встраиваемых устройств.

Использование BSP упрощает разработку, так как обеспечивает готовые инструменты и настройки, позволяющие быстро начать работу с определенной аппаратной платформой, избегая необходимости создания всего необходимого ПО и настроек с нуля.

Начнем с установки версии Yocto Dunfell в каталог с именем poky.

Клонирую репозиторий Yocto:

Открываю терминал и ввожу команду для клонирования репозитория Yocto версии Dunfell в новый каталог с именем poky:

git clone -b dunfell git://git.yoctoproject.org/poky.git poky

Перехожу в только что созданный каталог poky:

cd poky

Запускаю команду для инициализации окружения:

source oe-init-build-env

Это создаст новый каталог build и настроит рабочее окружение для сборки проекта Yocto.

Редактирую файлы bblayers.conf и local.conf в каталоге build/conf, чтобы настроить параметры проекта под мои требования.

bblayers.conf:

Если подробнее, то этот файл определяет, какие слои (layers) будут использоваться в вашем проекте Yocto. Слои содержат конфигурации и файлы для построения пакетов и образов. В bblayers.conf мы добавляем пути к каталогам слоев, которые мы хотим включить в сборку. Это позволяет Yocto узнать, откуда брать компоненты для построения проекта.

local.conf:

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

К примеру, в bblayers.conf мы добавляем пути к слоям, чтобы Yocto знал, где искать рецепты пакетов, а в local.conf мы можем указать, какие образы создать, какие компоненты исключить из образа, настроить параметры ядра и т.д.

Например:

Выбор целевого образа:

IMAGE_FSTYPES = "ext4"
IMAGE_INSTALL_append = " package-name"

Настройка ядра Linux:

MACHINE_FEATURES_append = " bluetooth"

Настройка переменных среды:

export HTTP_PROXY = "http://proxy.example.com:8080"

Я же добавлю только одну конфигурацию в local.conf

MACHINE ?= "qemuarm"

Указываю тип образа, который хочу собрать, используя команду bitbake

bitbake core-image-minimal

Ожидаю завершения сборки:

Жду, пока сборка образа завершится. В результате образ будет доступен в директории tmp/deploy/images/raspberrypi4-64.

Сборка существующего BSP


Прежде чем продолжить, нам также нужно склонировать следующие зависимые слои на один уровень выше от этого каталога poky, чтобы каталоги слоя и poky находились рядом друг с другом:

$ git clone -b dunfell git://git.openembedded.org/meta-openembedded
$ git clone -b dunfell git://git.yoctoproject.org/meta-raspberrypi

Обратите внимание, что имя ветки зависимых слоев соответствует релизу Yocto для совместимости. Держите все три клонирования актуальными и синхронизированными с их удаленными версиями, используя периодические команды git pull. Слой meta-raspberrypi является BSP для всех Raspberry Pi. После того как эти зависимости на месте, можно собрать образ, настроенный для Raspberry Pi 4. Но прежде чем мы это сделаем, давайте изучим рецепты для общих образов Yocto:

Сначала перейдите в каталог, где вы склонировали Yocto:

$ cd poky

Затем перейдите в каталог, где находятся рецепты стандартных образов:

$ cd meta/recipes-core/images

Отобразите список рецептов для образов:

$ ls -1 core*

Показать рецепт для core-image-base:

$ cat core-image-base.bb

Показать рецепт для core-image-minimal:

$ cat core-image-minimal.bb

Показать рецепт для core-image-minimal-dev:

$ cat core-image-minimal-dev.bb

Перейдите в каталог classes внутри poky/meta:

$ cd ../../classes

Наконец, отобразите файл класса core-image:

$ cat core-image.bbclass

Важно отметить, что в этом классе доступен длинный список доступных IMAGE_FEATURES, включая упомянутую функцию dev-pkgs.

Стандартные образы, такие как core-image-minimal и core-image-minimal-dev, не зависят от конкретной машины. Мы создали core-image-minimal как для эмулятора QEMU Arm, так и для платы BeagleBone Black. Мы могли бы сделать тоже самое для образа core-image-minimal для Raspberry Pi 4. В отличие от этого, слой BSP включает рецепты образов, предназначенные для конкретной платы или серии плат.

Теперь давайте рассмотрим рецепт rpi-test-image внутри слоя BSP meta-raspberrypi, чтобы увидеть, как поддержка Wi-Fi и Bluetooth добавляется к core-image-base для Raspberry Pi 4:

Сначала перейдите на уровень выше от каталога, где вы клонировали Yocto:

$ cd ../../..

Затем перейдите в каталог внутри слоя BSP meta-raspberrypi, где находятся рецепты образов для Raspberry Pi:

$ cd meta-raspberrypi/recipes-core/images

Отобразите список рецептов образов Raspberry Pi:

$ ls -1

Покажите рецепт rpi-test-image:

$ cat rpi-test-image.bb

Обратите внимание, что переменная IMAGE_INSTALL была переопределена для добавления packagegroup-rpi-test и включения этих пакетов в образ.

Перейдите в соседний каталог packagegroups внутри meta-raspberrypi/recipes-core:

$ cd ../packagegroups

И, наконец, отобразите рецепт packagegroup-rpi-test:

$ cat packagegroup-rpi-test.bb

Обратите внимание, что пакеты connman, connman-client и bluez5 включены в список зависимостей времени выполнения, чтобы полностью включить поддержку Wi-Fi и Bluetooth.

Наконец, давайте соберем образ rpi-test-image для Raspberry Pi 4:

Сначала перейдите на уровень выше от каталога, где вы клонировали Yocto:

$ cd ../../..

Затем настройте ваше рабочее окружение BitBake:

$ source poky/oe-init-build-env build-rpi

Это настраивает множество переменных среды и помещает вас во вновь созданный каталог build-rpi.

Затем добавьте следующие слои в ваш образ:

$ bitbake-layers add-layer ../meta-openembedded/meta-oe
$ bitbake-layers add-layer ../meta-openembedded/meta-python
$ bitbake-layers add-layer ../meta-openembedded/meta-networking
$ bitbake-layers add-layer ../meta-openembedded/meta-multimedia
$ bitbake-layers add-layer ../meta-raspberrypi

Порядок добавления этих слоев важен, поскольку слои meta-networking и meta-multimedia зависят от слоя meta-python. Если bitbake-layers add-layer или bitbake-layers show-layers начнут выдавать ошибки парсинга, удалите каталог build-rpi и перезапустите процесс сначала.

Убедитесь, что все необходимые слои были добавлены в образ:

$ bitbake-layers show-layers

В списке должно быть всего восемь слоев: meta, meta-poky, meta-yocto-bsp, meta-oe, meta-python, meta-networking, meta-multimedia и meta-raspberrypi.

Изучите изменения, внесенные предыдущими командами bitbake-layers add-layer в bblayers.conf:

$ cat conf/bblayers.conf

В переменной BBLAYERS должны быть прописаны те же восемь слоев, что и на предыдущем шаге.

Перечислите машины, поддерживаемые слоем BSP meta-raspberrypi:

$ ls ../meta-raspberrypi/conf/machine

Обратите внимание на наличие конфигураций машин raspberrypi4 и raspberrypi4-64.

Добавьте следующую строку в ваш файл conf/local.conf:

MACHINE = "raspberrypi4-64"

Это переопределяет следующее значение по умолчанию в вашем файле conf/local.conf:

MACHINE ??= "qemux86-64"

Установка переменной MACHINE на raspberrypi4-64 гарантирует, что создаваемый образ будет работать для Raspberry Pi 4.

Теперь добавьте ssh-server-openssh к списку EXTRA_IMAGE_FEATURES в вашем файле conf/local.conf:

EXTRA_IMAGE_FEATURES ?= "debug-tweaks ssh-server-openssh"

Это добавляет SSH-сервер к образу для локального сетевого доступа.

И, наконец, соберите образ:

$ bitbake rpi-test-image

Сборка может занять от нескольких минут до нескольких часов при первом запуске, в зависимости от количества ядер CPU в вашем рабочем окружении. TARGET_SYS должен быть aarch64-poky-linux, а MACHINE должен быть raspberrypi4-64, так как этот образ предназначен для 64-битных ядер Arm Cortex-A72 в Pi 4.

После завершения сборки должен появиться файл с именем rpi-test-image-raspberrypi4-64.rootfs.wic.bz2 в каталоге tmp/deploy/images/raspberrypi4-64:

$ ls -l tmp/deploy/images/raspberrypi4-64/rpi-test*wic.bz2

Обратите внимание, что rpi-test-image-raspberrypi4-64.rootfs.wic.bz2 является символической ссылкой, указывающей на фактический образ в том же каталоге. К имени образа добавляется целое число, обозначающее дату и время сборки, перед расширением wic.bz2.

Теперь запишите этот образ на microSD-карту с помощью Etcher и загрузите его на ваш Raspberry Pi 4:

  • Вставьте microSD-карту в ваш компьютер.

  • Запустите Etcher.

  • Нажмите "Flash from file" в Etcher.

  • Найдите образ wic.bz2, который вы собрали для Raspberry Pi 4, и откройте его.

  • Выберите microSD-карту, которую вы вставили на Шаге 1.

  • Нажмите "Flash from Etcher", чтобы записать образ.

  • Извлеките microSD-карту, когда процесс записи будет завершен.

  • Вставьте microSD-карту в Raspberry Pi 4.

  • Подайте питание на Raspberry Pi 4 через порт USB-C.

  • Убедитесь, что ваш Pi 4 успешно загрузился, подключив его к Ethernet и наблюдая, как индикаторы сетевой активности мигают.

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

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


  1. Sergey78
    02.12.2023 15:55
    +4

    Про yocto есть неплохая и чуть более подробная серия видео от digi key

    https://youtu.be/9vsu67uMcko?si=tFOh0GRz6Wb_3VBb

    Там вроде первые пару видео про buildroot, а дальше несколько видео по yocto.

    А зачем нужен "Etcher для linux", если есть чудесный dd?


    1. okhsunrog
      02.12.2023 15:55

      Вам не нужен dd просто для запись образа на флешку. Да, это мощный инстумент, но в этом случае он абсолютно лишний. Достаточно cp / cat.
      В статье USB Flash Installation Media в Arch Wiki пару месяцев назад увидел пару ссылок. Может, и вам будет интересно, для себя много нового открыл. Раньше тоже бездумно использовал dd где надо, и где не надо.
      https://www.vidarholen.net/contents/blog/?p=479
      https://unix.stackexchange.com/questions/224277/is-it-better-to-use-cat-dd-pv-or-another-procedure-to-copy-a-cd-dvd/224314#224314


      1. edo1h
        02.12.2023 15:55

        Это всё вкусовщина, из разряда «cat file | program vs program < file vs < file program».
        Какие реальные недостатки есть у dd?


  1. iggr63
    02.12.2023 15:55
    +1

    Короткая но хорошая пошаговая инструкция. Теперь буду знать как наш встроенный линукс для А10 был построен. Спасибо.


  1. buldo
    02.12.2023 15:55
    +1

    dunfell - это же совсем уж старый релиз...

    Собрать образ yocto - дело не сложное, а вот как везти разработку-то? В том смысле, чтобы приложение прям разрабатывать, не пересобирая каждый раз образ, чтобы понять, что оно падает


    1. FoxTrot75
      02.12.2023 15:55
      +1

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


      1. buldo
        02.12.2023 15:55
        +1

        Это я понимаю. Нужна такая же пошаговая инструкция, как его использовать. Вот у меня есть CLion, вот на руках тулчейн, который мне yocto создал одной командой. Что дальше?

        Как заставить ide работать с тулчейном. Как при запуске дебага выгружать бинарь на плату и стартовать удалённую отладку?

        Статей "давайте соберём yocto для raspberry" много. А вот таких, как дальше разработку вести - таких я не помню.


        1. Sergey78
          02.12.2023 15:55

          Не знаю как лучше и правильно, поскольку других ответов нет, поделюсь как я делал: у меня сборка на отдельном удаленном "сервере". Когда я хотел попробовать изменения, просто коммитил в git. В yocto код приложения забирался с git-а и собирался. Т.е. локально тулчейн я не использовал. Код на С, поэтому просто проверить, что оно собирается без ошибок я могу при помощи того же gcc под x86.

          Насколько помню, в yocto приложения собираются в пакеты. Там, если не путаю, по-умолчанию rpm, но можно и deb использовать или opkg. В моем случае, целевая система была сильно минималистичная и приложение не сложное. Я просто по ssh забирал бинарник приложения из каталога сборки yocta на целевую систему.


        1. FoxTrot75
          02.12.2023 15:55

          Как заставить ide работать с тулчейном.

          Я для сборки использую cmake с указанием файла настроек тулчейна:

          -DCMAKE_TOOLCHAIN_FILE=../toolchain.cmake

          Вот такого содержания:

          toolchain.cmake
          set(CMAKE_NO_SYSTEM_FROM_IMPORTED 1)
          
          set(CMAKE_SYSTEM_NAME                   Linux)
          set(CMAKE_SYSTEM_PROCESSOR              arm)
          
          set(CMAKE_SYSROOT           /opt/poky-vm/3.1.3/sysroots/cortexa9t2hf-neon-poky-linux-gnueabi)
          
          set(CMAKE_PREFIX_PATH       /opt/poky-vm/3.1.3/sysroots/x86_64-pokysdk-linux/usr/bin)
          set(CMAKE_C_COMPILER        /opt/poky-vm/3.1.3/sysroots/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-gcc)
          set(CMAKE_CXX_COMPILER      /opt/poky-vm/3.1.3/sysroots/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-g++)
          set(CMAKE_OBJDUMP           /opt/poky-vm/3.1.3/sysroots/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-objdump)
          
          set(CMAKE_C_FLAGS           "${CMAKE_C_FLAGS} -mthumb -mfpu=neon -mfloat-abi=hard -mcpu=cortex-a9 -fstack-protector-strong -fdata-sections -ffunction-sections -Wl,--gc-sections")
          set(CMAKE_CXX_FLAGS         "${CMAKE_CXX_FLAGS} ${CMAKE_C_FLAGS}")
          
          set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
          set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
          set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
          set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
          
          set(OE_QMAKE_PATH_EXTERNAL_HOST_BINS /opt/poky-vm/3.1.3/sysroots/x86_64-pokysdk-linux/usr/bin/)
          

          У вас скорее всего будут другие пути и флаги сборки.

          Флаги сборки можно подсмотреть в логах сборки какого-нибудь пакета в самой yocto.

          Как при запуске дебага выгружать бинарь на плату и стартовать удалённую отладку?

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


  1. unk1500
    02.12.2023 15:55

    Прошу прощения за, возможно, странный вопрос. Недавно столкнулся с этой сборочной системой, для получения прошивки размером 16МБ потребовались шесть часов работы всех ядер i9, 24 ГБ ОЗУ и 120 ГБ дискового пространства. Причём на запуске с меньшим количеством ОЗУ машина умерла с сообщением "Out of Memory".

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


    1. Sergey78
      02.12.2023 15:55
      +2

      Так используйте меньше потоков. Условно, если у вас каждый поток сборки использует 1гб памяти, а собираете вы в 20 потоков, то 20Гб оно и съест :) Система сборки первый раз собирает и тулчейн и ядро и все что там у вас есть. Последующие сборки будут собирать только то, что изменилось.


    1. FoxTrot75
      02.12.2023 15:55

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

      А инкрементные сборки уже делаются супер быстро.


    1. buldo
      02.12.2023 15:55
      +1

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

      P. S. Ощущение, что у вас собиралось много лишнего. По опыту с малинкой 80ГБ диска + несколько часов сборки - это консольный образ на 160 метров с systemd


  1. RikkiMongoose
    02.12.2023 15:55
    -2

    А можно выпустить русский форк и назвать его Yopta?


  1. NAI
    02.12.2023 15:55

    Стоило упомянуть про приоритетность слоев, непонятно что произойдет если в одном слое мы включаем BT, а в другом выключаем. Как они применятся- по приоритету? По алфавиту?

    Теперь добавьте ssh-server-openssh к списку EXTRA_IMAGE_FEATURES в вашем файле conf/local.conf:

    В целом, концепция спорная. Когда вы едины в двух лицах - и разраб, и сборщик, то да можно все пихать в local.conf, когда у вас две\три\четыре отдельные команды которые ведут разработку самостоятельно, то проще чтобы у каждой был свой слой(репа) со своими зависимостями\доп. ПО.