Создание кастомного профиля для передачи данных
Материал носит ознакомительный характер. Работа с электроникой и микроконтроллерами требует соблюдения техники безопасности; автор не несет ответственности за возможные повреждения оборудования или иной ущерб, возникший в ходе обучения.
Мы разобрались с ролями устройств и UUID. Теперь пора наделить nRF52840 «голосом». В Arduino мы привыкли отправлять текст через Serial.print(). В профессиональной разработке на nRF Connect SDK (NCS) мы создаем структуру, которую смартфон поймет мгновенно.
Что такое Custom Profile?
Custom Profile — это ваш собственный протокол обмена данными. Стандартные профили (например, для пульсометра) жестко ограничены. В кастомном профиле вы сами решаете, что передавать: заряд батареи, ускорение по осям или состояние кнопки.
В основе профиля лежит таблица GATT. Представьте её как таблицу в Excel, где каждая строка — это характеристика. У каждой строки есть адрес (Handle), права доступа и значение.
Анатомия 128-битного UUID
Для стандартных сервисов Bluetooth SIG использует короткие 16-битные ID. Для своих проектов используйте 128-битные UUID — это гарантирует, что ваше устройство не перепутают ни с каким другим в мире.
- Плохо: использовать случайные числа вроде
0x1234(возможен конфликт со стандартами). - Хорошо: сгенерировать полноценный идентификатор.
Пример в коде:
// Уникальный ID сервиса
#define BT_UUID_MY_SERVICE_VAL \
BT_UUID_128_ENCODE(0x12345678, 0x1234, 0x5678, 0x1234, 0x56789abcdef0)
static struct bt_uuid_128 my_service_uuid = BT_UUID_INIT_128(BT_UUID_MY_SERVICE_VAL);
Объявление сервиса через макрос
В NCS не нужно вручную распределять байты в памяти. Макрос BT_GATT_SERVICE_DEFINE автоматически регистрирует сервис в системе:
BT_GATT_SERVICE_DEFINE(my_service,
BT_GATT_PRIMARY_SERVICE(&my_service_uuid),
BT_GATT_CHARACTERISTIC(&my_char_uuid.uuid,
BT_GATT_CHRC_READ | BT_GATT_CHRC_NOTIFY,
BT_GATT_PERM_READ,
read_callback, NULL, &my_value),
BT_GATT_CCC(ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),
);
Важно: Макрос
BT_GATT_CCCсоздает дескриптор конфигурации. Без него смартфон не сможет «подписаться» на ваши обновления.
Notification vs Indication: как передавать данные
Когда данные на датчике изменились, их нужно отправить клиенту (смартфону). Есть два способа:
- Notification — устройство отправляет данные без подтверждения. Это быстро и экономит заряд. Если пакет потеряется — не страшно, придет следующий.
- Indication — аналог заказного письма. Смартфон обязан подтвердить получение каждого пакета. Это надежно, но медленно и энергозатратно.
Для большинства задач прототипирования выбирайте Notification.
Ограничение MTU
MTU (Maximum Transmission Unit) — максимальный размер полезных данных в одном пакете. По умолчанию в Bluetooth это всего 23 байта.
Если отправить строку длиннее MTU, данные обрежутся. Современные смартфоны умеют договариваться об увеличении лимита, но всегда учитывайте это ограничение при проектировании структуры данных.
Обработка записи данных
Чтобы смартфон мог управлять устройством (например, зажечь LED), создайте характеристику с правами WRITE. Для обработки данных понадобится Callback-функция.
Как это работает:
- Стек Bluetooth получает данные.
- Находит нужный Handle в таблице.
- Вызывает вашу функцию и передает ей указатель на данные.
Это и есть событийная модель: мы не опрашиваем порт в бесконечном loop(), а реагируем на прерывание от стека.
Совет: Не делайте тяжелых вычислений внутри Callback-функции. Скопируйте данные в переменную и обработайте их позже в основном потоке.
Профиль описан, код готов. В следующей теме мы превратим ваш смартфон в мощный инструмент отладки и проведем первое полноценное тестирование соединения.