В одном из комментариев меня упрекнули — дескать «всё суета, а ты вот попробуй светодиодом поморгать через bluetooth». Ну что — упрёк справедлив и вызов принят. Начинаю серию статей по программированию под zephyr. В данной статье будет код для bluetooth устройства с одним светодиодом, которым можно управлять.
Я слегка напряжён — каждый раз когда громко заявляю про серию статей возникают обстоятельства непреодолимой силы, по которым каждый раз эта серия заканчивается на первой же статье. Но тем-не менее попробую.
Создаём минимальное bluetooth устройство
Итак простейший код для включения bluetooth является просто вызовом функции bt_enable
в которую передаётся функция которая вызовется после завершения инициализации bluetooth стека. В этой функции мы вызываем функцию bt_le_adv_start
в которую передаём данные для адвертайзинга и для сканирования.
Вот такого кода вполне достаточно для создания BLE устройства. Я сознательно удалил все проверки на ошибки и вызовы отладочных функций, чтобы не загромождать код и чтобы было более понятно. На самом деле конечно проверка кодов ошибок возвращаемых функциями нужна.
1 |
|
Собственно запустив вот такой нехитрый код на устройстве мы получаем вполне рабочее bluetooth устройство
1 | $ bluetoothctl |
Минутка теории
Вообще когда начинаешь разбираться с работой bluetooth стека документация выглядит откровенно пугающей. Куча терминов — все эти HCI, GATT, Advertizing и пр. по крайней мере мне так точно просто выносили мозг и погружали в депрессию. Однако разобравшись как следует в теме всё оказалось не так пугающе.
Итак к нашему великому счастью все нюансы соединения за нас обрабатывает zephyr и это прекрасно. Поэтому останавливаться на этом больше не буду. Перейдём к более насущной теме — обмену данными.
Итак для обмена данными в bluetooth стеке есть такое понятие как GATT — некая таблица аттрибутов, которую мы можем читать и если разрешат то и писать. На самом деле это прекрасно — никакого бардака и всё чётенько. Читаем и пишем только то что можно и только в заранее отведённые места.
Ну и соответственно чтобы как-то организовать взаимодействие и обмен данными с нашим устройством мы должны в нём создать такую таблицу и добавить в неё некий аттрибут в который мы и будем писать. А уже устройство должно следить за значением этого аттрибута и как-то реагировать — в нашем случае поджигать или гасить светодиод.
Переходим от теории к практике
Создаём GATT таблицу
Собственно даже по умолчанию у любого устройства есть GATT таблица (возможно это zephyr добавляет — я это не рыл). Как минимум в ней есть имя и appearance (что-то вроде некоевого типа устройства). Аттрибуты GATT таблицы группируются в сервисы — вот тут (https://www.bluetooth.com/specifications/gatt/services) можно посмотреть список стандартных сервисов и аттрибутов. Сервисы в свою очередь группируются в профили.
Соответственно я создам сервис с одним аттрибутом. Ну и все элементы GATT имеют свой идентификатор UUID. Часть из них зарезервирована, а остальные можно использовать по своему усмотрению. Для создания UUID-ов в Zephyr есть набор макросов. В частности для нашего случая создадим следующие:
1 | static struct bt_uuid_128 led_service_uuid = BT_UUID_INIT_128( |
Далее в примере выше мы определяли массив ad
для адвертайзера. В него нужно добавить UUID сервиса.
1 | static const struct bt_data ad[] = { |
Ну и в хук, который вызывается после инициализации bluetooth стека добавим инициализацию GATT.
1 | static void bt_ready(int err) |
А передаём мы в функцию bt_gatt_service_register
как раз ссылку на нашу таблицу, на создании которой далее остановлюсь подробнее.
Итак создание таблицы аттрибутов (GATT) выглядит так:
1 | static struct bt_gatt_attr led_attrs[] = { |
Как видно из таблицы мы просто вызываем ряд макросов, в которые первым параметром передаём UUID, а затем набор параметров. Для сервиса у нас никаких параметров нет, у аттрибута (CHARACTERISTIC) только задаём права доступа, включая запись конечно., а вот в DESCRIPTOR кроме всего прочего передаём адреса функций для чтения и для записи этого аттрибута, ну и последним параметром адрес переменной, в которой будет храниться значение аттрибута. Её надо заранее объявить, впрочем как и функции.
Сами функции большого интереса не представляют посмотреть их можно в полной версии кода программы.
Ну собственно и всё — теперь осталось засунуть в функцию записи аттрибута проверку его значения и установку пина светодиода в соответствующее значение. Установка пина и поджигание светодиода я думаю тоже не ахти как интересно, поэтому останавливаться на нём не буду.
Проверка
В общем заливаю программу в устройство. Для проверки можно использовать несколько способов, но я почему-то выбрал вариант со смартфона. Просто захотелось по быстрому проверить. Для каких-то боле менее автоматических тестов лучше конечно написать некий код на том-же python-е.
Использовал я нордиковскую программу «nRF Connect». Итак смотрим как выглядит наше устройство:
Если нажать кнопку «Connect» то происходит подключение и мы можем прочитать список сервисов и аттрибутов:
Виден наш сервис и аттрибут. Видны они как Unknown т. к. являются нестандартными. Так-же видно значение аттрибута и кнопочки для чтения и записи.
Ну и отправляя значение отличное от нуля (обязательно вводить 2 знака, даже если у нас незначащий нуль) мы видим как на плате загорается светодиод. Соответственно вводя нулевое значение мы гасим светодиод.
Итоги
В общем я конечно рад, что всё получилось. Оговорюсь ещё раз — код, приведённый выше это всего-лишь пример — там отсутствуют проверки на ошибки ну и организован он с целью сделать его как можно более наглядным, а не удобным надёжным и производительным.
Вообще так размышляя на тему стоило бы управление светодиодом и работу с bluetooth внести в разные процессы, а взаимодействие сделать в виде обмена сообщениями. Тем более, что пишем-то мы под RTOS, которая как раз под такие вещи и заточена. Но в данном случае и таааак сойдёт.
Так-же осталась нераскрыта тема авторизации для доступа к записи атрибута, которая делается довольно просто.
Хоте записать видео, но что-то как-то пока ума не приложу как в один кадр засунуть экран, устройство и смартфон.
Полный код программы
Ниже привожу полный код программы:
Источники
- https://www.safaribooksonline.com/library/view/getting-started-with/9781491900550/ch04.html
- https://www.bluetooth.com/specifications/assigned-numbers/generic-access-profile
- https://www.silabs.com/community/wireless/bluetooth/knowledge-base.entry.html/10.02.2017/bluetooth_advertisin-hGsf
- https://stackoverflow.com/questions/10243769/what-range-of-bluetooth-uuids-can-be-used-for-vendor-defined-profiles
- https://devzone.nordicsemi.com/b/blog/posts/nrf-blinky-compatible-firmware-using-zephyr-blueto? CommentSortBy=CreatedDate& CommentSortOrder=Ascending
Комментарии