Пишем прошивку Bluetooth Low Energy на Zephyr OS: полное руководство для разработчиков

Zephyr OS даёт открытый BLE-стек, прошедший сертификацию Bluetooth SIG. Разбираем, как написать прошивку для периферийного устройства, настроить сервисы и управлять им со смартфона.

Обложка: Пишем прошивку Bluetooth Low Energy на Zephyr OS: полное руководство для разработчиков

Ваши беспроводные наушники только что подключились к телефону, смарт-часы синхронизировали пульс, а датчик температуры в соседней комнате отправил показания на шлюз. Всё это работает через Bluetooth Low Energy, и всё чаще прошивку под капотом таких устройств пишут на Zephyr OS.

Если вы разрабатываете встраиваемые системы или хотите войти в IoT, этот материал — практическое руководство по созданию BLE-периферии с нуля. Мы разберём, как устроен стек, как настроить окружение и как написать код, который управляет железом со смартфона.

Что такое Zephyr OS и зачем она для BLE

Zephyr OS — открытая операционная система реального времени под управлением Linux Foundation. Лицензия Apache 2.0, поддержка более 600 плат на ARM, RISC-V, x86 и других архитектурах. Главное для нас: внутри неё живёт полноценный BLE-стек, прошедший официальную сертификацию Bluetooth SIG. Рекомендуем также обзор «Первый RISC-V RVA23 чип входит в mainline Linux».

Ключевые выводы

Zephyr OS включает сертифицированный BLE-стек с открытым кодом — от приложения до радиоконтроллера.

GAP управляет обнаружением и соединениями, GATT — структурой данных (сервисы и характеристики).

Для старта понадобятся плата nRF52840 DK (~40 USD) и смартфон с приложением nRF Connect.

Прошивка собирается через west — единый инструмент для клонирования, сборки и прошивки.

Оптимизация энергопотребления: интервал advertising, peripheral latency, System OFF и отключение неиспользуемых периферий.

Это означает, что вы работаете не с хобби-реализацией, а со стеком, который прошёл conformance-тестирование. От прикладного кода до радиорегистров — всё открыто. Никаких бинарных блобов. Nordic Semiconductor, чьи чипы доминируют на рынке BLE, построила свой nRF Connect SDK поверх Zephyr. Когда инженеры Nordic пишут прошивку, они используют Zephyr.

Стек поддерживает Bluetooth 5.x (2M PHY, Coded PHY для дальней связи, Extended Advertising), Bluetooth Mesh, LE Audio с кодеком LC3, Direction Finding и все стандартные профили. Если вы делаете BLE-продукт в 2025–2026 годах — Zephyr входит в тройку лучших платформ.

Как работает BLE: два слоя, которые нужно знать

Прежде чем писать код, нужна ментальная модель. BLE — это не «классический» Bluetooth для аудио. Он создан для коротких, редких сеансов связи: датчик проснулся, отправил 20 байт, уснул. Такое устройство может работать от таблеточной батарейки годами.

Разработчик взаимодействует с двумя слоями:

  • GAP (Generic Access Profile) — отвечает за обнаружение и соединения. Периферия широковещательно рассылает пакеты (advertising), а central (обычно телефон) их слушает и инициирует подключение.
  • GATT (Generic Attribute Profile) — отвечает за обмен данными после соединения. Данные организованы в иерархии: устройство предоставляет сервисы, внутри которых находятся характеристики — единицы данных со свойствами Read, Write, Notify, Indicate.

Каждый сервис и характеристика идентифицируются UUID. Стандартные (от Bluetooth SIG) — 16-битные, пользовательские — 128-битные. Хорошо спроектированный набор сервисов — это API вашего устройства.

Ключевые выводы

Настройка окружения

Для работы понадобится Linux (Ubuntu 22.04+), macOS или Windows с WSL2. Идеальная плата для старта — Nordic nRF52840 DK. Она недорогая, широко доступна и имеет встроенный отладчик J-Link. А ещё посмотрите, как создать самодельные устройства на Raspberry Pi, ESP32 и Node-RED.

Установка зависимостей

На Ubuntu устанавливаем toolchain и утилиты:

			sudo apt update
sudo apt install --no-install-recommends git cmake ninja-build gperf \
  ccache dfu-util device-tree-compiler wget python3-dev python3-pip \
  python3-setuptools python3-tk python3-wheel xz-utils file \
  make gcc gcc-multilib g++-multilib libsdl2-dev libmagic1
		

Устанавливаем west — CLI для управления многорепозиторным workspace:

			pip3 install west
		

Инициализируем workspace и скачиваем модули (HAL-ы, криптобиблиотеки, Bluetooth controller):

			west init ~/zephyrproject
cd ~/zephyrproject
west update
		

Устанавливаем Python-зависимости и SDK с кросс-компиляторами:

			pip3 install -r ~/zephyrproject/zephyr/scripts/requirements.txt

wget https://github.com/zephyrproject-rtos/sdk-ng/releases/download/v0.16.8/zephyr-sdk-0.16.8_linux-x86_64.tar.xz
tar xvf zephyr-sdk-0.16.8_linux-x86_64.tar.xz
cd zephyr-sdk-0.16.8
./setup.sh
		

Добавляем в ~/.bashrc:

			export ZEPHYR_BASE=~/zephyrproject/zephyr
source ~/zephyrproject/zephyr/zephyr-env.sh
		

Первое приложение: простой beacon

Начнём с минимального примера: устройство, которое только объявляет о себе по радио и не принимает соединений. Это база для iBeacon, Eddystone и отслеживание активов.

Создаём структуру проекта:

			mkdir -p ~/my_ble_apps/beacon/src
		

Файл CMakeLists.txt:

			cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(ble_beacon)
target_sources(app PRIVATE src/main.c)
		

Конфигурация prj.conf:

			CONFIG_BT=y
CONFIG_BT_BROADCASTER=y
		

Код src/main.c:

			#include <zephyr/kernel.h>
#include <zephyr/bluetooth/bluetooth.h>

static const struct bt_data ad[] = {
    BT_DATA_BYTES(BT_DATA_FLAGS, BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR),
    BT_DATA_BYTES(BT_DATA_NAME_COMPLETE,
                  'M', 'y', 'B', 'e', 'a', 'c', 'o', 'n'),
};

int main(void)
{
    int err;

    printk("Starting BLE Beacon\n");

    err = bt_enable(NULL);
    if (err) {
        printk("Bluetooth init failed (err %d)\n", err);
        return 0;
    }

    printk("Bluetooth initialized\n");

    err = bt_le_adv_start(BT_LE_ADV_NCONN, ad, ARRAY_SIZE(ad), NULL, 0);
    if (err) {
        printk("Advertising failed to start (err %d)\n", err);
        return 0;
    }

    printk("Beacon is advertising\n");
    return 0;
}
		

Что происходит: bt_enable(NULL) инициализирует стек синхронно. bt_le_adv_start с параметром BT_LE_ADV_NCONN запускает неконнективный advertising. Массив ad содержит флаги (general discoverable, no BR/EDR) и имя устройства.

Собираем и прошиваем:

			west build -b nrf52840dk/nrf52840 ~/my_ble_apps/beacon
west flash
		

Открываем nRF Connect на смартфоне, сканируем и видим «MyBeacon» в списке устройств. Прошивка работает.

Кастомный сервис: управляем LED со смартфона

Beacon полезен, но скучен. Настоящая магия начинается, когда телефон подключается к устройству и управляет им. Сделаем сервис, который позволяет включать LED на плате и читать состояние кнопки.

Конфигурация prj.conf:

			CONFIG_BT=y
CONFIG_BT_PERIPHERAL=y
CONFIG_BT_DEVICE_NAME="Zephyr LED"
CONFIG_BT_DEVICE_APPEARANCE=0
CONFIG_GPIO=y
		

Основной код. Здесь определяем пользовательские 128-битные UUID для сервиса и двух характеристик:

			#include <zephyr/kernel.h>
#include <zephyr/bluetooth/bluetooth.h>
#include <zephyr/bluetooth/gatt.h>
#include <zephyr/bluetooth/uuid.h>
#include <zephyr/drivers/gpio.h>

#define BT_UUID_LED_SERVICE_VAL \
    BT_UUID_128_ENCODE(0x00001234, 0x0000, 0x1000, 0x8000, 0x00805f9b34fb)
#define BT_UUID_LED_SERVICE BT_UUID_DECLARE_128(BT_UUID_LED_SERVICE_VAL)

#define BT_UUID_LED_CHAR_VAL \
    BT_UUID_128_ENCODE(0x00001235, 0x0000, 0x1000, 0x8000, 0x00805f9b34fb)
#define BT_UUID_LED_CHAR BT_UUID_DECLARE_128(BT_UUID_LED_CHAR_VAL)

#define BT_UUID_BUTTON_CHAR_VAL \
    BT_UUID_128_ENCODE(0x00001236, 0x0000, 0x1000, 0x8000, 0x00805f9b34fb)
#define BT_UUID_BUTTON_CHAR BT_UUID_DECLARE_128(BT_UUID_BUTTON_CHAR_VAL)

#define LED0_NODE DT_ALIAS(led0)
#define SW0_NODE  DT_ALIAS(sw0)

static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET(SW0_NODE, gpios);

static uint8_t led_state;
static uint8_t button_state;

static ssize_t read_led(struct bt_conn *conn,
                        const struct bt_gatt_attr *attr,
                        void *buf, uint16_t len, uint16_t offset)
{
    return bt_gatt_attr_read(conn, attr, buf, len, offset,
                             &led_state, sizeof(led_state));
}

static ssize_t write_led(struct bt_conn *conn,
                         const struct bt_gatt_attr *attr,
                         const void *buf, uint16_t len,
                         uint16_t offset, uint8_t flags)
{
    if (len != 1) {
        return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
    }
    led_state = *((const uint8_t *)buf);
    gpio_pin_set_dt(&led, led_state ? 1 : 0);
    printk("LED %s\n", led_state ? "ON" : "OFF");
    return len;
}

static ssize_t read_button(struct bt_conn *conn,
                           const struct bt_gatt_attr *attr,
                           void *buf, uint16_t len, uint16_t offset)
{
    button_state = gpio_pin_get_dt(&button);
    return bt_gatt_attr_read(conn, attr, buf, len, offset,
                             &button_state, sizeof(button_state));
}

BT_GATT_SERVICE_DEFINE(led_service,
    BT_GATT_PRIMARY_SERVICE(BT_UUID_LED_SERVICE),

    BT_GATT_CHARACTERISTIC(BT_UUID_LED_CHAR,
        BT_GATT_CHRC_READ | BT_GATT_CHRC_WRITE,
        BT_GATT_PERM_READ | BT_GATT_PERM_WRITE,
        read_led, write_led, NULL),

    BT_GATT_CHARACTERISTIC(BT_UUID_BUTTON_CHAR,
        BT_GATT_CHRC_READ,
        BT_GATT_PERM_READ,
        read_button, NULL, NULL),
);

static const struct bt_data ad[] = {
    BT_DATA_BYTES(BT_DATA_FLAGS, BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR),
    BT_DATA(BT_DATA_NAME_COMPLETE, CONFIG_BT_DEVICE_NAME,
            sizeof(CONFIG_BT_DEVICE_NAME) - 1),
};

static const struct bt_data sd[] = {
    BT_DATA_BYTES(BT_DATA_UUID128_ALL, BT_UUID_LED_SERVICE_VAL),
};

int main(void)
{
    int err;

    if (!gpio_is_ready_dt(&led) || !gpio_is_ready_dt(&button)) {
        printk("GPIO devices not ready\n");
        return 0;
    }

    gpio_pin_configure_dt(&led, GPIO_OUTPUT_INACTIVE);
    gpio_pin_configure_dt(&button, GPIO_INPUT);

    err = bt_enable(NULL);
    if (err) {
        printk("Bluetooth init failed (err %d)\n", err);
        return 0;
    }

    err = bt_le_adv_start(BT_LE_ADV_CONN, ad, ARRAY_SIZE(ad),
                          sd, ARRAY_SIZE(sd));
    if (err) {
        printk("Advertising failed to start (err %d)\n", err);
        return 0;
    }

    printk("Advertising as '%s'\n", CONFIG_BT_DEVICE_NAME);
    return 0;
}
		

Ключевые моменты: BT_GATT_SERVICE_DEFINE собирает GATT базу данных на этапе компиляции. Характеристика LED доступна для чтения и записи, а кнопка — только для чтения. UUID сервиса вынесен в scan response, чтобы не перегружать 31-байтовый advertising-пакет.

После прошивки подключаемся через nRF Connect, находим кастомный сервис (UUID начинается с 00001234), записываем 0x01 в LED-характеристику — светодиод загорается. Только что вы управляли железом со смартфона по Bluetooth.

Notifications: отправляем данные без запроса

Чтение и запись — это pull-модель. Но датчики обычно работают по push: термометр сам шлёт температуру, пульсомер — BPM. В BLE для этого есть notifications.

Чтобы добавить notification в характеристику, нужно:

  1. Указать свойство BT_GATT_CHRC_NOTIFY в характеристике.
  2. Добавить CCCD (Client Characteristic Configuration Descriptor) через BT_GATT_CCC — телефон пишет в него 0x0001, чтобы включить уведомления.
  3. Вызвать bt_gatt_notify при изменении данных.

Типовой паттерн — периодическая отправка через delayable work item:

			static void sensor_work_handler(struct k_work *work)
{
    sensor_value = read_actual_sensor();

    if (notifications_enabled) {
        bt_gatt_notify(NULL, &sensor_service.attrs[2]  /* index depends on service structure */,
                       &sensor_value, sizeof(sensor_value));
    }

    k_work_schedule(&sensor_work, K_SECONDS(1));
}

K_WORK_DELAYABLE_DEFINE(sensor_work, sensor_work_handler);
		

Work item выполняется в системном потоке, а не в контексте прерывания, поэтому вызовы Bluetooth API безопасны.

Полный sensor node в одном файле

Соберём всё вместе: датчик температуры (симулированный), который читается по запросу и пушится через notifications с настраиваемым интервалом. Добавим обработку соединений и LED-индикацию статуса.

			static int16_t temperature_value = 2250; /* 22.50 C, fixed point */
static uint16_t notify_interval_ms = 1000;
static bool temp_notifications_enabled;
static struct bt_conn *current_conn;

static int16_t simulate_temperature(void)
{
    static int16_t base = 2250;
    base += (k_uptime_get_32() % 11) - 5;
    if (base > 3500) base = 3500;
    if (base < 1000) base = 1000;
    return base;
}

static ssize_t read_temperature(struct bt_conn *conn,
                                const struct bt_gatt_attr *attr,
                                void *buf, uint16_t len, uint16_t offset)
{
    temperature_value = simulate_temperature();
    return bt_gatt_attr_read(conn, attr, buf, len, offset,
                             &temperature_value, sizeof(temperature_value));
}

static void temp_ccc_changed(const struct bt_gatt_attr *attr, uint16_t value)
{
    temp_notifications_enabled = (value == BT_GATT_CCC_NOTIFY);
    if (temp_notifications_enabled) {
        k_work_schedule(&sensor_work, K_MSEC(notify_interval_ms));
    } else {
        k_work_cancel_delayable(&sensor_work);
    }
}

static ssize_t write_interval(struct bt_conn *conn,
                              const struct bt_gatt_attr *attr,
                              const void *buf, uint16_t len,
                              uint16_t offset, uint8_t flags)
{
    if (len != sizeof(uint16_t)) {
        return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN);
    }
    uint16_t new_interval = *((const uint16_t *)buf);
    if (new_interval < 100 || new_interval > 60000) {
        return BT_GATT_ERR(BT_ATT_ERR_VALUE_NOT_ALLOWED);
    }
    notify_interval_ms = new_interval;
    if (temp_notifications_enabled) {
        k_work_cancel_delayable(&sensor_work);
        k_work_schedule(&sensor_work, K_MSEC(notify_interval_ms));
    }
    return len;
}
		

Здесь важны две детали. Во-первых, температура хранится в формате fixed-point (сотые доли градуса в int16_t) — это позволяет избежать дорогих операций с плавающей точкой на микроконтроллерах без FPU. Во-вторых, проверка в функции записи: если телефон шлёт некорректный интервал, стек возвращает ошибку BT_ATT_ERR_VALUE_NOT_ALLOWED.

При разрыве соединения обработчики отменяют work item, гасят LED и перезапускают advertising — устройство снова доступно для обнаружения.

Безопасность: pairing и шифрование

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

Методы pairing в BLE:

  • Just Works — шифрование без аутентификации. Защищает от пассивного прослушивания, но не от MITM.
  • Passkey Entry — пользователь вводит 6-значный код. Аутентификация есть.
  • Numeric Comparison — оба устройства показывают число, пользователь подтверждает совпадение.
  • Out of Band (OOB) — обмен ключами через внешний канал, например NFC.

Включаем SMP (Security Manager Protocol) и persistent storage для bonding:

			CONFIG_BT_SMP=y
CONFIG_BT_SETTINGS=y
CONFIG_FLASH=y
CONFIG_FLASH_MAP=y
CONFIG_NVS=y
CONFIG_SETTINGS=y
		

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

Чтобы требовать шифрования для чтения характеристики, меняем permission:

			BT_GATT_CHARACTERISTIC(BT_UUID_TEMP_CHAR,
    BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
    BT_GATT_PERM_READ_ENCRYPT,
    read_temperature, NULL, NULL),
		

BT_GATT_PERM_READ_ENCRYPT означает, что чтение возможно только по зашифрованному соединению. Если телефон пытается прочитать без pairing, стек автоматически инициирует процедуру сопряжения.

Роль central: сканируем и подключаемся

Все примеры выше — периферия (peripheral): устройство, которое рассылает advertising и ждёт подключения. Другая сторона — central: шлюз, хаб, сборщик данных. Для полной картины нужно понимать обе роли.

Конфигурация central:

			CONFIG_BT=y
CONFIG_BT_CENTRAL=y
CONFIG_BT_GATT_CLIENT=y
CONFIG_BT_SCAN=y
		

Запуск сканирования и подключение к первому найденному устройству с RSSI выше -70 dBm:

			static void device_found(const bt_addr_le_t *addr, int8_t rssi,
                          uint8_t type, struct net_buf_simple *ad)
{
    char addr_str[BT_ADDR_LE_STR_LEN];
    bt_addr_le_to_str(addr, addr_str, sizeof(addr_str));

    if (rssi < -70) return;

    printk("Found device: %s (RSSI %d)\n", addr_str, rssi);
    bt_le_scan_stop();

    int err = bt_conn_le_create(addr, BT_CONN_LE_CREATE_CONN,
                                BT_LE_CONN_PARAM_DEFAULT, &default_conn);
    if (err) {
        printk("Connection failed (err %d)\n", err);
        bt_le_scan_start(BT_LE_SCAN_ACTIVE, device_found);
    }
}
		

После подключения выполняется обнаружение сервисов через bt_gatt_discover, затем — чтение характеристик и подписка на notifications через bt_gatt_subscribe.

Оптимизация: MTU, PHY и энергопотребление

По умолчанию BLE ATT MTU — 23 байта. Минус 3 байта заголовка ATT, получаем 20 байт полезной нагрузки на операцию. Для передачи прошивки или больших буферов этого мало.

Включаем MTU 247 байт и Data Length Extension:

			CONFIG_BT_L2CAP_TX_MTU=247
CONFIG_BT_BUF_ACL_RX_SIZE=251
CONFIG_BT_BUF_ACL_TX_SIZE=251
CONFIG_BT_CTLR_DATA_LENGTH_MAX=251
		

Zephyr автоматически инициирует MTU exchange при соединении. С 247-байтным MTU, интервалом 7,5 мс и 2M PHY пропускная способность достигает ~800–1000 кбит/с на практике.

Выбор PHY — ещё один рычаг:

  • 1M PHY — дефолт, стандартная дальность и скорость.
  • 2M PHY (Bluetooth 5.0) — удваивает скорость, сокращает время работы радио и экономит энергию. Дальность чуть меньше.
  • Coded PHY — увеличивает дальность в 2–4 раза за счёт помехоустойчивого кодирования, но скорость падает до 125 кбит/с.

Энергосбережение — ключ к автономным устройствам. Главные приёмы:

  1. Увеличить advertising interval: 1000 мс потребляет в 5 раз меньше, чем 200 мс.
  2. Использовать peripheral latency: пропускать connection events, когда нечего передавать.
  3. Включить System OFF: ток nRF52840 падает с ~3 мА до ~1,5 мкА.
  4. Отключить неиспользуемые периферии в devicetree overlay — UART, SPI, I2C жрут ток даже в простое.

Цель для BLE-датчика — средний ток в единицы микроампер. При таком потреблении таблеточная батарейка CR2032 (225 мА·ч) проработает годы.

Что ещё умеет стек: Mesh, DFU и LE Audio

Помимо классической point-to-point связи, Zephyr поддерживает три продвинутые технологии:

Bluetooth Mesh

Bluetooth Mesh — многие-ко-многим сеть поверх BLE. Узлы пересылают сообщения друг другу, расширяя радиус действия за пределы одного соединения. Используется в умных домах и офисах: десятки лампочек, выключателей и датчиков общаются без единого центрального хаба.

DFU over BLE

DFU over BLE — обновление прошивки по воздуху. Интеграция с MCUboot даёт два слота: активный и резервный. Новая прошивка загружается через BLE SMP сервис, устройство перезагружается, MCUboot проверяет подпись и, в случае неудачи, автоматически откатывается к предыдущей версии.

LE Audio

LE Audio — новый аудиостандарт на базе BLE. Кодек LC3 даёт более высокое качество, чем классический SBC, при вдвое меньшем битрейте. Режим BIS (broadcast) позволяет одному источнику транслировать звук на неограниченное число приёмников — технология, лежащая в основе Auracast.

Отладка: что делать, когда не работает

BLE сложно отлаживать, потому что радио невидимо. Начинайте с логов:

			CONFIG_LOG=y
CONFIG_BT_DEBUG_LOG=y
CONFIG_BT_LOG_LEVEL_DBG=4
		

Это выдаст подробную трассировку каждой HCI-команды, advertising event и GATT-операции. Вывод идёт в UART — не оставляйте UART включённым в рабочей среде, он жрёт ток.

Интерактивный Zephyr shell позволяет вручную запускать bt advertise on, bt gatt discover и bt gatt read через последовательный порт. Это бесценно для быстрой проверки гипотез без перепрошивки.

Для анализа радиоэфира используйте nRF Sniffer для Bluetooth LE — прошивка для nRF52840 DK, которая вместе с Wireshark показывает каждый пакет в эфире: advertising PDU, connection events, pairing exchange. Это окончательный инструмент, когда подозреваете проблему на уровне протокола.

Часто задаваемые вопросы

Часто задаваемые вопросы
1
Нужен ли опыт в embedded, чтобы начать с Zephyr BLE?

Базовое знание C (указатели, структуры, обратные вызовы) обязательно. Опыт в Bluetooth не требуется — концепции GAP и GATT разбираются с нуля. Понадобятся плата nRF52840 DK (~40 USD) и смартфон с nRF Connect.

2
Почему Zephyr, а не Arduino или ESP-IDF?

Arduino упрощает старт, но скрывает детали BLE-стека и плохо масштабируется на сложные проекты. ESP-IDF хорош для ESP32, но привязывает к одному производителю. Zephyr даёт переносимость между 600+ платами, открытый код всего стека и сертификацию Bluetooth SIG — критично для коммерческих продуктов.

3
Какой реальный радиус действия BLE на Zephyr?

На 1M PHY в помещении — 10–30 метров. Coded PHY (Bluetooth 5.0) увеличивает дальность до 100+ метров за счёт помехоустойчивого кодирования, но снижает скорость до 125 кбит/с. Для Mesh-сетей радиус теоретически неограничен: каждый узел ретранслирует сообщение.

4
Можно ли использовать Zephyr в коммерческом продукте?

Да, лицензия Apache 2.0 разрешает коммерческое использование без открытия исходников вашего продукта. BLE-стек прошёл квалификацию Bluetooth SIG, что упрощает сертификацию конечного устройства.

5
Как уменьшить энергопотребление BLE-устройства?

Три главных рычага: увеличить интервал advertising или connection events, использовать peripheral latency для пропуска пустых слотов, и включать глубокий сон (System OFF) между активностями. Также отключайте в devicetree все периферии, которые не нужны в рабочей среде.

Выводы

Zephyr OS превращает разработку BLE-устройств из работы с чёрными ящиками в инженерную дисциплину. Полный открытый стек, сертификация Bluetooth SIG, поддержка 600+ плат и активное сообщество делают его одной из сильнейших платформ для IoT.

Мы прошли путь от простого beacon до полноценного sensor node с notifications, управлением соединениями и проверкой входных данных. Этих паттернов — advertising, GATT-обработчики, CCCD, подсчёт ссылок на соединение — достаточно, чтобы построить практически любое BLE-периферийное устройство.

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

Главный способ понять BLE — не читать спецификацию, а заставить мигать LED со смартфона. Когда это получится, остальное пойдёт быстро.
Nikheel Vishwas Savantembedded engineer, freeCodeCamp contributor

Источники:

How to Build Bluetooth Applications with Zephyr OS: A Handbook for Devs — freeCodeCamp.

Zephyr Bluetooth Stack Documentation — официальная документация.

Bluetooth SIG Specifications — спецификации протоколов и профилей.

Рекомендуем