Zephyr против bare-metal: год спустя
Год назад я перевёл одно небольшое промышленное устройство с честно написанной от руки прошивки на Zephyr RTOS. Прошёл год. Расскажу, что получилось, что не очень, и стоило ли оно того. Спойлер: да, но не в тот момент, когда я об этом думал.
Контекст
Устройство — промышленный датчик с RS-485 наружу, парой аналоговых входов, BLE-радио для местной диагностики и небольшим экраном. Микроконтроллер — nRF52840. До переезда: main.c на 4500 строк, всё в суперлуппе с ручным state machine, прерывания пишут в кольцевые буферы, всё в одном .elf. Работало. Девять месяцев в production без ребутов на тестовых стендах.
Зачем я полез в RTOS? Три причины:
- BLE-стек от Nordic (SoftDevice) — это, по сути, тоже RTOS, только не моя. Когда у меня было два cooperative-event-loop'а — мой и SD — они начинали друг другу мешать в неочевидных местах.
- Нужно было добавить TCP-стек поверх ESP32, который висит на UART как модем. Реализовывать TCP вручную я не собирался.
- Архитектор проекта (то есть я) очень устал от того, что каждый периферийный драйвер пишется заново с нуля.
Что получилось
Готовые драйверы
Драйверы для I2C, SPI, UART, ADC, GPIO работают, имеют единый API, и их не нужно писать. Это самая большая выгода, и она реализуется в первые две недели. Если бы я переезжал ради одного этого, было бы оправдано.
Девайс-три (DTS) поначалу выглядит как оккультная практика, но через месяц-два до тебя доходит, что это корректный способ описывать оборудование.
BLE начал работать предсказуемо
Главная боль раньше. Со старым SoftDevice'ом я никогда не был уверен, в какой момент я могу делать что-то «тяжёлое» в main loop, не нарушив тайминги стека. У Zephyr с Bluetooth Host есть ясные API, ясные потоки, и стек ругается понятными ошибками.
Тесты на хосте
Native posix backend Zephyr позволяет собирать большую часть моей логики как обычный linux-бинарник и гонять unit-тесты на CI. Раньше у меня была отдельная «testbench», и я её ненавидел. Сейчас не ненавижу.
Что не очень
Размер бинарника
Старая прошивка: 96 КБ. Новая, с тем же функционалом: 244 КБ. Ужал до 198 КБ — но это всё ещё в два раза больше, чем было. Если у тебя 64 КБ flash — забудь про Zephyr.
Время сборки
Чистая сборка bare-metal: 4 секунды. Чистая сборка Zephyr: 38 секунд. Инкрементальная — обе в районе секунды.
Кривая обучения
Месяц мне понадобился, чтобы перестать ругаться на Kconfig. Два — на DTS. Три — чтобы я начал писать драйверы под Zephyr-style правильно.
Документация
Документация Zephyr — это википедия. Там есть всё. Но «есть всё» означает, что когда тебе нужен конкретный ответ, ты находишь страницу с тремя способами сделать одно и то же, причём один устарел два релиза назад.
Сюрпризы
Логика стала чище
Это удивило меня больше всего. Когда у тебя BLE в одном thread'е, телеметрия в другом, бизнес-логика в третьем, и они общаются через message queue — становится проще ответить на вопрос «что вообще здесь происходит».
Watchdog работает «бесплатно»
В Zephyr есть встроенный механизм task watchdog: каждый поток отмечается, и если кто-то перестал — общий перезапуск. Десять строк Kconfig.
Я наконец стал писать тесты
Не потому что Zephyr заставил, а потому что инфраструктура стала ниже трения. Это, наверное, влияет на качество прошивки больше, чем сам RTOS.
Стоило ли
Да. Но я бы сэкономил себе три недели нервов, если бы понимал заранее: первые два месяца ты будешь медленнее, а не быстрее. Это инвестиция, не upgrade.
Если решились
- Не пытайтесь портировать всё за раз. Сначала переезд, потом рефакторинг.
- Включите
CONFIG_THREAD_ANALYZERсразу. - Не воюйте с Kconfig. Если вам нужно что-то странное — наверное, вам это не нужно.
- Читайте
samples/. Там есть примерно всё.