РадиоКот >Статьи >

Теги статьи: Bluetooth

Разработка Bluetooth приложений на модулях фирмы Silicon Labs. Часть I.

Автор: Сергей Безруков (aka Ser60)
Опубликовано 08.01.2020
Создано при помощи КотоРед.

Общеизвестно, что технический прогресс в наше время развивается буквально семимильными шагами. В частности, это отчётливо видно по продукции, предназначенной для Интернета Вещей IoT. Например, из моих статей на эту тему, опубликованных здесь за последние 2 – 4 года многие уже устарели. В этой статье будет рассказано про работу с актуальными сегодня Bluetooth модулями фирмы Silicon Labs и разработке Bluetooth приложений в среде Simplicity Studio. Технология Bluetooth стала сегодня де-факто лидирующей для передачи небольших объёмов данных на короткие расстояния в пределах квартиры или дома и его окружения. Я имею в виду прежде всего версии 4.2 и 5.х протокола, хотя версия 5 замечательно подходит для передачи звука, а спецификация Bluetooth mesh успешно конкурирует с традиционными беспроводными технологиями концепции умного дома (например, Zigbee). Обо всём этом речь пойдёт как-нибудь в другой раз. Данная статья планируется состоять из трёх частей. В первой части мы приведём основные моменты разработки Bluetooth приложений сервера и клиента в среде Simplicity Studio и демонстрацию некоторых интегрированных в неё инструментов. Вторая часть будет посвящена вопросам безопасности Bluetooth приложений, а третья – разработке Bluetooth приложений под управлением RTOS.

Что касается элементной базы, оставим пока в стороне новейшие разработки модулей фирмы второго поколения (Series 2), собранных на основе ядра Cortex-M33 и обратимся к последним моделям предыдущего поколения на основе SoC серий MG12 и BG13 с привычными ядрами Cortex-M4F, поддерживающими версии протоколоа Bluetooth 4.х, 5.0, 5.1, а также Bluetooth mesh. Модули эти подходят для широкого спектра устройств, как с низким токопотреблением (в режиме сна на уровне 2 мкА), так и для устройств с повышеной мощностью передатчика до +20dBm и дальностью связи более километра на открытой местности. Потребление модулей при мощности 0 dBm и в режиме приёма составляет порядка 10мА, в то время как рекордные показатели на эти параметры у некоторых других модулей сегодня в 2 раза ниже. Однако, и это пока вполне приемлимо, а удобная и бесплатная интегрированная среда разработки, относительно невысокая цена модулей (среди сертифицированных некитайских), их малые размеры (13×15мм), и обильная документация выглядят очень привлекательно.

Система разработки Simplicity Studio [1] основана на платформе Eclipse, что сегодня можно назвать тенденцией среди подобного рода систем у многих других фирм. Стандартизация ПО способствует при необходимости быстрому переходу с изделий одних фирм на другие. Нашей первой целью будет создание достаточно простых приложений сервера и клиента. Разработку сервера будем проводить на Wireless Starter Kit Mainboard (BRD4001A) с установленной на ней плате BRD4306A с модулем BGM13P22. Основная плата имеет на борту помимо отладчика замечательную систему для измерения токопотребления.

Для упрощения разработки приложений фирма предлагает несколько примеров в составе Studio. Проще всего начать с примера SOC - Empty. При подсоединении платы к компьютеру Studio автоматически разпознает её и сформирует линк на проект в соответствии с подсоединённым модулем. Откроем этот проект и переименуем его в Server.

Приложение Сервера

Конфигурация Bluetooth профиля, сервисов, и характеристрик

В проект SOC - Empty уже заложен мимимальный функционал системы, включающий 3 Bluetooth сервиса. Среди них обязательный для всех Bluetooth устройств сервис Generic Access, требуемый Bluetooth SIG, сервис Device Information, а также проприетарный сервис Silicon Labs OTA фирмы для загрузки обновлений ПО по воздуху (об этом позже). При кликании мышкой на файл Server.isc (см. дерево проекта в левой части экрана), Bluetooth профиль проекта откроется в окошке графического конфигуратора.

Стандартные Bluetooth SIG профили, поддерживаетмые конфигуратором, приведены в левой части его окна (синие иконки). Используя меню в правой части экрана добавим новый проприетарный сервис, назовём его Temp_Humi Service, и организуем в нём 3 характеристики для измеряемых величин. Характеристикам следует приписать ID, по которым к ним можно будет обращаться из программы, и разрешить их чтение как показано ниже для характеристики Temperature. Вообще, мы изменим лишь 3 файла в дефолтном проекте – Server_BGM.isc, app.c, и app.h – все они находятся в прилагаемом к статье архиве. По завершении конфигурирования нажимаем кнопку Generate для перевода картинки в код. Отмечу, что для последней характеристики я разрешил отсылку уведомлений (notification). В этом случае стек автоматически создаст для неё Client Characteristic Configuration Descriptor (CCCD) с дефолтным значением 0. Подробнее о работе с конфигуратором см. [2].

В файле main.c ничего менять не следует. Он содержит помимо конфигурации опций Bluetooth стека вызовы функций конфигурирования аппаратных ресурсов, а также функции appMain(), определённой в файле app.c и определяющей всю логику работы приложения. В начале этой функции мы добавили код конфигурации модуля I2C и выводов SoC, соединённых со светодиодами на плате (строки 39 – 58). Наш код зажжёт один из них по установке соединения с сервером и использует сенсор температуры и влажности Si7021, установленный на основной плате. Конфигурацию I2C и других модулей SоC можно производить непосредственно на уровне битов в регистрах периферии (Studio поддерживает стандарт CMSIS), а также используя библиотеку EMLIB [3]. По-моему весьма спорно что проще – узнать из Reference Manual на SoC какой бит установить в каком регистре для конфигурации чипа или вникнуть в использование библиотечных функций. В идеале следует чувствовать себя комфортно с обоими подходами, но оставлю это на усмотрение читателя. Сам я использую смесь подходов, в зависимости от того, что мне быстрее написать по памяти. Так, например, в строках 40 – 48 ниже производится вызов библиотечных функций, а строки 50 – 52 используют синтаксис непосредственной конфигурации регистров.

Однако, одним из неоспоримых преимуществ библиотеки EMLIB является наличие в ней драйверов коммуникационных интерфейсов таких как I2C, SPI, I2S и пр. Так, функции SI7021_startConversion() и SI7021_getTempHumi() для работы с сенсором Si7021 в конце файла app.c используют драйвер I2C библиотеки. Отмечу, что все имена констант в коде определены в многочисленных заголовочных файлах системы и включены в проект в установках Includes (см. дерево проекта).

После дополнительной конфигурации ресурсов и определения новых переменных приложение входит в основной цикл, состоящий из обработчиков событий стека Bluetooth. В начале этого цикла (строка 73) производится вызов блокировочной функции gecko_wait_event(). При наличии события происходит его обработка последующим кодом, а при отсутствии - вся система погружается в сон (при условии разрешения режима сна в строке 26 файла app.h) с автоматическим пробуждением по наступлению события. Это очень благоприятно сказывается на общем токопотреблении устройства (см. ниже). Первым событием по подаче питания или ресета является evt_system_boot, сигнализирующее приложению об окончании инициализации стека Bluetooth. Теперь можно сделать наше устройство видимым клиентам и начать посылку оповещений (advertisements). С деталями Bluetooth API функций можно ознакомиться в [4].

Конфигурация оповещений

По окончании инициализации стека Bluetooth мы выводим на экран TeraTerm MAC адрес сервера (строка 80) и отладочное сообщение (строка 83). MAC адрес будет использован позже в программе клиента для автоматического соединения с нашим сервером. Отмечу, что вывод отладочных сообщений на терминал будет производиться только если значение DEBUG_LEVEL, установленное в строке 26 файла app.h, отлично от 0. Здесь printLog фактически то-же самое, что и printf.

В строках 85 – 86 устанавливаем максимальную выходную мощность передатчика и его мощность в режиме посылки оповещений в единицах 0.1dBm. Выходная мощность 0dBm вполне достаточна для уверенной связи в пределах жилой комнаты и параметры передатчика в ДШ нормируются при такой мощности. Стек способен поддерживать до 8 соединений с разными клиентами. Соответственно, он может передавать несколько групп оповещений. В нашем случае передаётся лишь одна группа (согласно параметрy MAX_ADVERTIZERS, установленному в строке 61 файла main.c), соответственно первый параметр (0) в строках 85 – 90 это номер первой и единственной группы. В строке 87 устанавливаем период посылки оповещений в единицах 0.625мс. Таким образом, 1600 соответствует периоду в 1сек.

Теперь следует определиться какую информацию мы хотим передавать в оповещениях. Этот выбор можно переложить на стек или сделать по желанию пользователя. В первом случае помимо обязательных флагов будет передаваться полный или сокращённый список UUID сервисов, разрешённых к оповещению графическим конфигуратором профиля, и имя устройства. Так как мы программируем работу сервера в режиме совмещённом с версией Bluetooth 4, общая длина информационной части оповещений не должна превышать 31 байт. В нашем случае мы сами выбираем в структуре advData что именно передавать (строка 88). Инициализация этой структуры производится в функции setAdvScanData() в конце файла app.c. Для примера помимо обязательных флагов (первые 3 байта) я формирую в строках 231 - 235 Manufacturer Specific Data с именем производителя. Подчёркиваю, это сделано лишь в качестве примера, т.к. имя производителя уже имеется в сервисе Device Information (которое, однако, станет доступно клиенту только после установки соединения с сервером). Далее, в строке 89 мы определяем информацию, которая будет передаваться клиенту в качестве ответа (Scan Response) при активном сканировании. Таким образом, мы получаем возможность отправить клиенту дополнительные 31 байт информации об устройстве. Структура scanData также устанавливается в функции setAdvScanData() и в данном случае просто содержит полное имя устройства (Sergei’s Server). Наконец, сами оповещения начинают отправляться строкой 90, где первый параметр – это номер группы оповещений, второй указывает стеку о передаче информации сконфигурированной пользователем, а третий определяет наше устройство видимым всем клиентам с возможностью установки соединения с ним. Оповещения дефолтно передаются на всех трёх выделенных под это каналах 37, 38, и 39, и это можно изменить соответствующей API функцией.

Работу сервера в режиме оповещений можно проконтролировать любым сканером Bluetooth устройств. Для удобства мы воспользуемся донглом и системой CySmart фирмы Cypress, подробнее о работе с ними см. в [5].

Программа CySmart при подключении донгла сканирует оповещения от всех Bluetooth устройств в радиусе приёма и, в частности, показывает что именно передаёт наш сервер как Advertisement data ...

... так и Scan Response Data на вкладке в правой части окна.

Таким, образом, всё работает в соответствии с конфигурацией. При периоде оповещений 1 сек установка соединения клиента с сервером занимает, как правило, не более 2 сек, однако, токопотребление получается весьма малым, что видно из следующей осциллограммы.

Осциллограмма получена с помошью инструмента Energy Profiler, входящего в состав Simplicity Studio. Из неё следует, что пиковое токопотребление в режиме передачи составляет около 10мА (что полностью согласуется с ДШ) и среднее потребление за период оповещений составляет около 23.7мкА. Это позволит нашему серверу непрерывно работать в режиме оповещений в течении года при питании от CR2032. Шум на графике и периодические малые выбросы тока (4 за период), видимо, связаны с работой DC/DC конвертера, входящего в состав модуля. При его отключении (установкой параметра HAL_DCDC_BYPASS в строке 52 файла sl_module_bgm13p22f512ga.h) шум уменьшается, но среднее токопотребление получается несколько выше (34.5мкА), как показывает следующий график. Поэтому далее я использую сервер только с включённем DC/DC конвертером. Все пассивные элементы для конвертера входят в состав Bluetooth модуля.

Установка соединения

По установке соединения с клиентом генерируется событие evt_le_connection_opened, при котором мы запускаем программный таймер для измерения параметров среды с периодом 1сек и включаем светодиод (строки 97 и 98). При этом TIMER_ID_SENSOR – это идентификатор нашего таймера, определённый в строке 20 кода, а последний параметр в этой API функции говорит системе, что таймер будет периодически вырабатывать события вплоть до его отключения.

Прекращение соединения

При разрыве соединения клиента и сервера генерируется событие evt_le_connection_closed, при котором мы останавливаем работу таймера и выключаем светодиод (строки 105 – 106).

Далее в коде проверяется заказал-ли пользователь производство обновления ПО (OTA update). Если нет, то возобновляем посылку оповещений дo следущего соединения с клиентом. Если да, то начинаем процесс загрузки обновления.

Обновление ПО по воздуху (OTA update)

Весь функционал для проведения обновления «по воздуху» уже заложен в стеке Bluetooth и в загрузчике, автоматически входящего в состав каждого приложения. Здесь мы просто произведём обновление без всяких предосторожностей в плане безопасности, т.к. этим вопросам будет посвящена вторая часть статьи. Отмечу, что первоначальное программирование МК кодами проекта и загрузчика должно быть произведено отладчиком и не по воздуху. Для этого подойдёт любой проект, можно даже использовать демонстрационный проект SOC-Empty без всяких изменений. Как только загрузчик будет размещён в модуле, все последующие обновления ПО можно будет производить по воздуху.

Для загрузки обновления используем смартфон (Android или iOS, не важно). Приведённая ниже процедура проверена на телефоне Samsung S8 и версии 9 ОС Android. После успешной компиляции нового кода следует сгенерировать файл в формате gbl (Gecko Boot Loader). Для этого кликнем мышкой на икону файла create_bl_files.bat в дереве проекта в левой части окна Studio. При этом в папке output_gbl (также в дереве проекта) появятся 3 файла, из которых нам нужен будет только файл application.gbl. Далее, загрузим в смартфон приложение Blue Gecko из Google Play. По завершении инсталляции в файловой системе смартфона появится папка SiliconLabs_BGApp/OTAFiles. Создадим новую директорию в этой папке (например, exampleOTAfolder) и загрузим туда файл application.gbl из компьютера. Это обязательная процедура, т.к. приложение Blue Gecko будет искать файл для загрузки только в поддиректориях папки OTAFiles.

Теперь запускаем приложение Blue Gecko на смартфоне, устанавливаем из него соединение с нашим сервером, открываем меню в правой верхней части окна и выбираем ОТА. В появившемся окне с красным фоном (левая картинка ниже) выбираем PARTIAL OTA (т.е. обновление только самого приложения без загрузчика), выбираем папку с файлом application.gbl и сам этот файл (средняя картинка). Как только кнопка ОТА внизу окна станет активной, кликаем на неё и дожидаемся конца загрузки ПО. Для файла приложения сервера объёмом около 150Кб это занимает порядка 15 сек. По окончании загрузки новое приложение автоматически запускается и возобновляет передачу оповещений.

Обновление базы данных сервера и ответ на запрос клиента

Последняя порция кода в приложении сервера служит для получения данных от сенсора Si7021 и загрузки их в базу данных сервера, чтобы они стали доступными клиентам. Запрос на измерение направляется каждую секунду по таймеру TIMER_ID_SENSOR, который генерирует событие evt_hardware_soft_timer. По его наступлении мы подаём в сенсор команду для проведения нового измерения (строка 141) и запускаем разовый программный таймер TIMER_ID_CONVERT на 50мс, которые необходимы сенсору для завершения измерений перед их чтением.

Чтение данных производится в строке 146 с последующим их выводом на экран терминала и записью данных температуры и влажности в соответствующие характеристики сервиса Temp_Humi Service. Строки 148 – 149 кода используют ID этих характеристик, определённые в графическом конфигураторе профиля, как было рассказано выше. Отправка значений харакреристик клиенту по его запросу производится стеком протокола автоматически и никакого дополнительного кода не требует. Уведомления (строка 50) будут отправляться клиенту автоматически только после их разрешения в дескрипторе CCCD (подробнее об этом ниже). Вот как выглядит окно терминала при установке соединения с клиентом.

Обратим внимание на MAC адрес сервера во второй строчке выше. Он будет использован приложением клиента для автоматического соединения с нашим сервером. А пока можно протестировать работу сервера, например, с помощью того-же донгла и системы CySmart. Для этого установим соединение с сервером и запишем значение 1 в дескриптор CCCD, разрешив отсылку уведомлений о новых значениях параметров среды. Приём уведомлений донглом регистрируются в логе в нижней части окна. Значение 17:27 (hex) соответствует 23°C и 39% влажности.

Среднее токопотребления в режиме соединения с клиентом получилось около 60 мкА, о чём свидетельствует следующий график:

Приложение Клиента

Для создания приложения клиента также начнём с примера SOC-Empty и добавим код только в файл app.c. В приведённом архиве содержится код для демо-платы Thunderboard Sense 2 на основе EFR32MG12, показанной ниже. Приложение клиента также управляется событиями стека и поддерживает OTA update, который производится в полной аналогии с сервером и поэтому здесь не рассматривается.

Сканирование и обнаружение устройств

Первым делом после инициализации стека Bluetooth нам следует позаботиться о начале сканирования устройств и обнаружении нашего сервера. Это реализовано в строках 57 – 62 файла app.c в прилагаемом архиве.

В строке 58 мы устанавливаем период сканирования в единицах 0.625мс (160 = 10мс) и параметры радио. Так как наш сервер сконфигурирован на оповещения в формате Bluetooth 4, первый параметр должен быть le_gap_phy_1m. В следующей строке мы требуем организации активного сканирования, чтобы увидеть имя нашего сервера, передаваемое как Scan Response. Далее, в строке 60 устанавливаем границы периода обмена данными с сервером в единицах 1.25мс. Для наших целей минимальный период в 80×1.25 = 100мс более чем приемлимый. Четвёртый параметр в этой API (=1000) - это время ожидания ответа от сервера до разрыва соединения (в единицах 10мс). Наконец, само сканирование начинается подачей команды в строке 61. Может показаться странным использование различных единиц измерения интервалов (0.625мс, 1.25мс, 10мс), но это нормативы Bluetooth SIG, т.к. все временные интервалы должны быть кратными этим значениям.

При приёме оповещения от Bluetooth устройства или получении ответа на запрос Scan Request активного сканирования генерируется событие evt_le_gap_scan_response и MAC адрес обнаруженного устройства становится доступным из структуры в строке 109. В следующей строке полученный адрес сравнивается с адресом сервера и в случае совпадения MAC адрес выводится в окно терминала вместе со значением packet_type. Анализируя это значение в программе можно отличить приём оповещения или ответa на запрос активного сканирования. В последнем случае этот параметр равен 4 и мы выводим в терминал имя сервера. Напомню, что запрос Scan Request передаётся стеком только после приёма оповещения, поэтому адрес нашего сервера появится в окне терминала 2 раза. Это сделано специально для демонстрации опций обнаружения устройств. При получении ответа на запрос активного сканирования от нашего сервера процесс сканирования завершается (строка 117) и генерируется запрос на соединение с сервером (строка 122).

В данной программе сканирование устройств продолжается вплоть до обнаружения сервера с нужным MAC адресом. Отмечу, что порядок байтов его адреса в строке 38 должен быть от младшего к старшему (little endian).

Соединение с сервером

При соединении с сервером генерируется событие evt_le_connection_opened, такое-же как и в программе сервера. В нём мы включаем светодиод как индикатор соединения, и далее нам предстоит сделать выбор как получить данные из сервера. Для этого имеется несколько опиций. Можно, например, начать длинную процедуру получения адресов (handles) всех сервисов сервера и их характеристик и далее выделить из них те, что содержат нужные данные (например, температуру). В данной программе для простоты мы используем адреса нужных характеристик, полученные с помошью CySmart. Их также можно найти в файле gatt_db.h проекта сервера. Нашей целью является периодическое получение клиентом данных температуры и влажности, измеряемых сервером, и отображение их в окне терминала. Напомню, что сервер периодически обновляет значения параметров среды в своей базе данных. Для их чтения можно послать в сервер соответствующую команду или просто разрешить уведомления (notifications) посылки этих данных в дескрипторе CCCD характеристики Temp_Humi notification. Реализация второго подхода содержится в строке 68 ниже. Разрешённные в CCCD, уведомления посылаются сервером в строке 150 его кода с периодом производства измерений (1 сек).

Для реализации первого подхода следует закомментировать строку 68 и раскомментировать строку 69. При этом активируется программный таймер TIMER_ID_READ для периодических запросов данных из сервера. В данном случае его период также равен 1сек, но он может быть и иным и не синхронизирован с периодом производства измерений на стороне сервера. Таймер (если активирован) генерирует периодические события evt_hardware_soft_timer, в котором мы посылаем запрос на чтение характеристики с адресом 0х001А (температура), определённой в строке 21.

По приёму уведомления или ответа на запрос чтения характеристики стеком генерируется событие evt_gatt_characteristic_value, и адрес этой характеристики доступен из структуры в строке 136.

Если получено значение характеристики температуры (строка 137), то производится выдача её на терминал и посылка запроса характеристики влажности (строка 139). Если получены данные влажности (строка 142) или уведомление характеристики Temp_Humi notification (строка146), то просто выдаём соответствующие значения в терминал. Вот так выглядят сообщения клиента в окне терминала:

Работу клиента и сервера можно проследить с помощью сниффера Bluetooth. Для этого можно воспользоваться донглом и системой Wireshark, как рассказано в другой моей статье [5]. Однако, можно поступить и иначе, задействовав инструмент Network Analyzer, входящий в состав Simplicity Studio. Замечательно, что для этого при использовании демо-плат фирмы не требуется никаких дополнительных донглов или прочего оборудования. Демо-плата, на которой реализован наш клиент, может одновременно работать в качестве сниффера (картинку можно более детально рассмотреть, открыв её отдельно).

Зелёная осциллограмма в верхней части окна анализатора символизирует передачу пакетов между устройствами. Сами устройства отображаются точками под осциллограммой, где из меню можно заказать индикацию MAC адресов устройств. Зелёные стрелки показывают направление передачи данных пакета, а расшифровка пакетов приводится в средней части окна. Детали передаваемого пакета показываются в правой верней части окна, а состав пакета в виде массива байтов показан внизу справа. Так, например, для выделенного серым фоном пакета, следует, что принято значение температуры 0х15 = 21°С. Подробнее о работе с анализатором можно прочитать в интегрированной документации на Simplicity Studio.

Литература

1. QSG139: Bluetooth® Development with Simplicity Studio
2. UG365: GATT Configurator User's Guide
3. BGM13 Gecko Module Software Documentation
4. Bluetooth Software API Reference Manual
5. Реалиазция стандартного GATT-BLE профиля на RSoC фирмы Cypress

 

 

 


Файлы:
Архив ZIP


Все вопросы в Форум.




Эти статьи вам тоже могут пригодиться:

О работе с BLE модулем BGM111 фирмы Silicon Labs

О работе с Bluetooth модулями AMS001/002 фирмы Zentri

Прием и передача данных по bluetooth (HC-05)

Разработка Bluetooth приложений на модулях фирмы Silicon Labs. Часть III.

Bluetooth по-китайски: теория и практика

Радиоуправление моделью через Bluetooth LE

Универсальный генератор с Bluetooth из готовых модулей

Разработка Bluetooth приложений на модулях фирмы Silicon Labs. Часть 2.

О работе с Bluetooth модулями семейства BL65х фирмы Laird