Оповещения BLE, руководство для начинающих Печать
Добавил(а) microsin   

Первое, что нужно сделать - загрузить код примера с GitHub [8]. Он основан на шаблоне примера из SDK V15.0.0, но из него вырезано то, что нам не понадобится. Тем не менее осталось довольно много кода, который используется для инициирования и использования процессов, работающих в фоновом режиме. Этот код в основном не касается материала в этом руководстве, и может быть оставлен как есть. Если Вам все равно интересно, что это такое, то вот несколько ссылок на документацию по некоторым модулям, которые используются в коде:

Peer Manager [3]. Этот модуль обрабатывает связанные (bonded) соединения, которые включают управление процедурами шифрования (encryption) и согласования параметров участников обмена (pairing). Поскольку это руководство главным образом сконцентрировано на системе оповещения (advertising), рассмотрение установки соединения не актуально. Тем не менее автор [1] решил оставить этот код, потому что он используется в других, более продвинутых примерах, и его наличие позволит ознакомиться с общей структурой приложений BLE.

Button Support Package (BSP) [4]. Этот модуль использeтся для поддержки светодиодов (LEDs) и кнопок (buttons). В этом примере Вы будете получать индикацию мерцанием LED, когда Ваша плата выполняет advertising. После истечения 180 секунд плата остановит оповещения и перейдет в сон. Затем Вы можете нажать на кнопку Button 1, это разбудит плату, и advertising возобновится. Все это обрабатывается модулем BSP.

Примечание: новые термины и сокращения см. в Словарике [12].

Чтобы выполнить задание в этом руководстве, понадобится следующее:

• nRF52 DK Development Kit [5].
• nRF52840 Dongle [6], или другая плата для разработки на основе чипа nRF52x.
• Среда разработки SEGGER Embedded Studo (SES) или Keil V5.xx.
• Установленное приложение nRF Connect. Его можно установить из Play Store Google для Android или из App store для iPhone.
• SDK V15.0.0 [7] **ВНИМАНИЕ! Это руководство написано для SDK V15.0.0.**. На GitHub можно найти примеры и для SDK 11, SDK V8.x.0 и SDK V9.0.0 [9], однако API-функции в них отличаются.
• Для nRF52 DK используется SoftDevice V6.0.0 (S132).
• Файлы рассматриваемого здесь примера кода можно скачать на github [8].

Могут быть использованы другие киты, отладочные платы и версии ПО, однако в этом списке приведено то, что использовалось автором [1].

Примечание: если испытываете трудности с самостоятельной закачкой перечисленных материалов, то воспользуйтесь ссылкой [13].

Чтобы скомпилировать пример, распакуйте папку nrf51-ble-tutorial-advertising из архива [8], и скопируйте её в каталог "папка_SDK\examples\ble_peripheral". После этого файл main.c должен находиться в папке "your_SDK_folder\examples\ble_peripheral\nrf52-ble-tutorial-advertising". Откройте файл проекта ble_app_template_pca10040_s132 в среде разработки SES и кликните на Build. Компиляция должна пройти успешно без каких-либо ошибок или предупреждений.

[Advertising]

Убедитесь, что Вы запрограммировали корректный бинарник SoftDevice в свой кит разработчика nRF5x DK, и успешно скомпилировали пример. Затем кликните на кнопку download в SES, и загрузите пример в памяти чипа. Запустите приложение nRF Connect и запустите обнаружение BLE-устройств. Должно появиться устройство с именем "HelloWorld" списке, как показано на рисунке ниже.

nRF5x advertising test fig01

Здесь мы видим следующее:

• Имя устройства, как оно было определено в модуле main.c. Найдите макрос #define DEVICE_NAME в коде, и поменяйте его строку на любое другое значение (но не делайте эту строку слишком длинной). Снова скомпилируйте и загрузите приложение, и увидите, что имя устройства поменялось.
• Bluetooth Device Address (адрес устройства BLE). Этот адрес похож на Media Access Control сети Ethernet (MAC), но он не обязательно будет уникальным.
• Received Signal Strength Indicator (RSSI). Это значение показывает уровень принимаемого радиосигнала. Попробуйте перемещать свой кит относительно смартфона, где запущена утилита nRF Connect, значение RSSI будет изменяться.

Advertising-пакет. Раскройте в nRF Connect дерево информации об устройстве HelloWorld, должно отобразится примерно следующее:

nRF5x advertising test fig02

В этом руководстве нас интересует тип адреса (Address type) и тип оповещения (Advertising type):

Address Type. Здесь тип адреса показан так, как это определено спецификацией Bluetooth Core (краткое описание типов адреса см. во врезке ниже). Тип адреса по умолчанию в нашем примере - "Random static address". Попробуйте перейти в gap_params_init() модуля main.c и добавить следующие 4 строки в конец этой фукции:

ble_gap_privacy_params_t prvt_conf;
memset(&prvt_conf, 0, sizeof(prvt_conf));
prvt_conf.privacy_mode = BLE_GAP_PRIVACY_MODE_DEVICE_PRIVACY;
prvt_conf.private_addr_type = BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLE;
prvt_conf.private_addr_cycle_s = 0;
err_code = sd_ble_gap_privacy_set(&prvt_conf);
APP_ERROR_CHECK(err_code);

Этот код установит тип адреса "Private Resolvable address" (частный распознаваемый адрес). Тип адреса (Address type) все так же будет показан как "Random", но попробуйте сбросить свою плату несколько раз. Вы должны увидеть, что после каждого сброса устройство будет видно в списке с другим адресом. Эта функциональность может быть полезна в определенных ситуациях, требующих более высокой безопасности. Снова удалите эти строки, чтобы тип адреса стал опять "Random static address".

Public address. Это глобальный, фиксированный адрес. Он должен быть зарегистрирован центром IEEE Registration Authority, и не будет меняться на протяжении всего времени жизни устройства.

Random Static address. Случайный статический адрес это просто случайное число, которое может либо генерироваться каждый раз заново при загрузке устройства, или оставаться постоянным в течение времени жизни устройства. Однако этот адрес не может быть изменен за время одного цикла подачи питания на устройство.

Private Resolvable address. Этот адрес генерируется из идентифицирующего ключа IRK (identity resolving key) и случайного числа, при этом адрес может часто меняться (даже в течение одного соединения), чтобы избежать отслеживания устройства неизвестным сканирующим оборудованием. Только те устройства, которые обладают ключом IRK, распределенным устройством с использованием private resolvable адреса, могут использовать этот адрес, позволяя им идентифицировать устройство.

Private Non-Resolvable address. Это не часто используемый вариант адреса, случайное число, которое может быть изменено в любое время.

Advertising Type. Чтобы не усложнять, существует два типа оповещения (advertising types): соединяемый (connectable) и не соединяемый (non-connectable). Non-connectable может быть полезен, если необходим обмен только в одну сторону, и нужно организовать широковещание только нескольких байт. Одним из вариантов использования может быть, например, информация о местоположении. С другой стороны, если нужно передавать больше данных на определенное устройство, то нужно connectable-устройство. Примерами могут служить датчик сердцебиения, подключенный к приложению на телефоне, или телефонная гарнитура Bluetooth.

[Данные оповещения]

Начинается самое интересное! Перейдите к функции advertising_init(). Поля данных оповещения (advertising data) содержащиеся в переменной advdata, это место, где Вы можете сконфигурировать свой первый пакет оповещения BLE (advertising packet). Пакет оповещения в этом примере уже содержит полное имя Вашего устройства. Он также содержит флаги, определяющие некоторые опции оповещения. Поле flag обязательное, и мы его здесь обсудим.

Найдите в файлах проекта определение структуры ble_advdata_t для данных оповещения, для чего сделайте правый клик мышью на ble_advdata_t, и выберите в контекстном меню Go To Definition Of 'ble_advdata_t' (таким способом можно найти определения всех функций и переменных). Когда Вы найдете это определение, то увидите, что это список опций и структур данных с некоторыми очень краткими пояснениями. Существует не так много вариантов, однако важно знать, что advertising packet может состоять не более чем из 31 байта, так что необходимо выбрать свои данные с осторожностью. Этой теме посвящено интересное обсуждение [10].

Давайте поиграемся с именем устройства - предположим, что оно должно быть длинное. Найдите еще раз определение DEVICE_NAME, и поменяйте строку на "ThisIsAReallyLongName". Скомпилируйте и загрузите код, и убедитесь, что новое имя отображается в списке устройств. Это имя занимает слишком много места в advertising-пакете, под который отведено только 31 байт. Предположим, что хотим сохранить наше длинное имя, но при этом хотим отправлять в оповещении только несколько его букв. Тогда функция advertising_init() должна выглядеть примерно так:

static void advertising_init(void)
{
   ret_code_t err_code;
   ble_advertising_init_t init; // Структура, содержащая advertising-параметры
   
   // Сборка структуры данных оповещения, чтобы передать её
   // в вызов @ref ble_advertising_init.
   memset(&init, 0, sizeof(init));
 
   init.advdata.name_type               = BLE_ADVDATA_FULL_NAME;
   init.advdata.include_appearance      = true;
   init.advdata.flags                   = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
   init.advdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
   init.advdata.uuids_complete.p_uuids  = m_adv_uuids;
 
   init.config.ble_adv_fast_enabled  = true;
   init.config.ble_adv_fast_interval = APP_ADV_INTERVAL;
   init.config.ble_adv_fast_timeout  = APP_ADV_DURATION;
 
   init.evt_handler = on_adv_evt;
 
   err_code = ble_advertising_init(&m_advertising, &init);
   APP_ERROR_CHECK(err_code);
 
   ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);
}

Поменяйте тип имени оповещения (advertising name type):

init.advdata.name_type = BLE_ADVDATA_SHORT_NAME; // Использование укороченного имени

Сразу ниже этой строки добавьте еще одну:

init.advdata.short_name_len = 6; // Оповещать только 6 букв имени

Снова выполните компиляцию и загрузку. В списке "Discovered devices" Ваше устройство должно теперь выглядеть с именем "ThisIs", это первые 6 букв полного имени. Однако если теперь подключиться к устройству выбором "Select device", "Connect", и затем кликнуть "Discover services", то будет видно полное имя устройства. В нашем случае с помощью короткого имени мы освободили 15 в advertising-пакете.

Manufacturer Specific Data. Одно из наиболее интересных полей структуры данных оповещения - поле p_manuf_specific_data (специфические данные производителя). Сюда Вы можете поместить любые данные, которые захотите. Скопируйте код функции advertising_init(), показанный ниже:

static void advertising_init(void)
{
   ret_code_t err_code;
   ble_advertising_init_t init; // Структура, содержащая advertising-параметры
 
   // Сборка структуры данных оповещения, чтобы передать её
   // в вызов @ref ble_advertising_init.
   memset(&init, 0, sizeof(init));
 
   ble_advdata_manuf_data_t      manuf_data; // Переменная для хранения данных производителя
   uint8_t data[]                = "SomeData!"; // Наши данные для оповещения
   manuf_data.company_identifier =  0x0059;  // ID компании Nordic
   manuf_data.data.p_data        = data;
   manuf_data.data.size          = sizeof(data);
   init.advdata.p_manuf_specific_data = &manuf_data;
 
   init.advdata.name_type = BLE_ADVDATA_SHORT_NAME; // Используется укороченное имя
   init.advdata.short_name_len = 6; // Оповещать только первыми 6 буквами имени
   init.advdata.flags                   = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
   init.advdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
   init.advdata.uuids_complete.p_uuids  = m_adv_uuids;
 
   init.config.ble_adv_fast_enabled  = true;
   init.config.ble_adv_fast_interval = APP_ADV_INTERVAL;
   init.config.ble_adv_fast_timeout  = APP_ADV_DURATION;
 
   init.evt_handler = on_adv_evt;
 
   err_code = ble_advertising_init(&m_advertising, &init);
   APP_ERROR_CHECK(err_code);
 
   ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);
}

Скомпилируйте, загрузите код в устройство и проверьте, как оно появляется в nRF Connect. Вы можете увидеть, что появилось новое поле в списке "Advertising Data", которое называется "ManufacturerSpecificData". Оно содержит выглядящую зашифрованной строку из шестнадцатеричных чисел. Первые 2 числа это идентификатор компании (company ID, в нашем случае 0x0059), самый младший значащий байт идет первым. Company ID это уникальное 16-битное значение, назначаемое участникам Bluetooth SIG. Не позволяется выполнять широковещание ID компании, которая не является зарегистрированной Bluetooth SIG. Список ID зарегистрированных участников Bluetooth SIG можно посмотреть по ссылке [11]. Как описывается внизу этой страницы, значение 0xFFFF "может использоваться для внутренних тестов и тестов совместимости перед тем, как будет назначен официальный Company ID. Значение 0xFFFF не должно использоваться в конечной выпускаемой продукции". Когда Вы будете готовы поставлять свои собственные устройства, необходимо вступить в членство Bluetooth SIG, чтобы получить свой уникальный ID. Членство "Adopter level" бесплатное, но Вы должны быть связаны с действительной компанией. Следующие 10 байт в поле manufacturer specific data это актуальные данные производителя. Если Вы сравните их с кодировкой ASCII, то распознаете строку "SomeData!", на этот раз байты идут по порядку. Последние 2 нуля в конце представляют символ завершения строки (termination character).

Задание 1: попробуйте добавить и заполнить поле "p_tx_power_level" в переменной advdata значением по Вашему выбору, и посмотрите, что изменится в nRF Connect. Это поле означает передаваемую мощность Вашего устройства. Это значение может позволить, например, другим устройствам грубо оценить дистанцию до Вашего устройства. 

Примечание: имейте в виду, что следует использовать один из допустимых уровней мощности, упомянутый спецификацией для чипа nRF52832. Также важно знать, что это не изменяет реальную мощность передачи, это просто информация, передаваемая в оповещении.

Задание 2: установите поле include_appearance переменной advdata в значение true, и посмотрите, что произойдет в nRF Connect. Затем найдите sd_ble_gap_appearance_set(BLE_APPERANCE_) в файле main.c, и поменяйте BLE_APPERANCE_ на BLE_APPEARANCE_HID_MOUSE, и посмотрите, что получится. Функция appearance может быть полезна для разработчиков приложений мобильных телефонов. Если у Вас смартфон (или наладонник) с поддержкой BLE, то теперь Ваша плата должна отображаться с именем "ThisIs" и иконкой компьютерной мыши, если Вы осуществляете поиск устройств BLE.

Совет: advertisement-пакет уже становится опасно большим, так что Вы возможно захотите его сократить за счет размера данных производителя (manufacturer) или имени устройства.

[Сканирование данных ответа]

Но что если Вам на самом деле, реально нужно посылать оповещение длиной больше 31 байта? Для этого есть решение, и оно называется сканирование данных ответа (scan response data). Это опциональная вторичная (secondary) полезная нагрузка оповещения (advertising payload). Она позволяет сканирующим устройствам, которые детектируют оповещающие о себе устройства, запросить второй пакет оповещения. Это дает возможность отправить 2 фрейма оповещения с общей полезной нагрузкой 62 байта.

Задание 3: выполните в своем коде следующее:

1. Декларируйте новую переменную типа ble_advdata_manuf_data_t с именем manuf_data_response.
2. Заполните manuf_data_response данными так же, как это мы делали в первом advertising-пакете.
3. Заполните init.srdata.name_type типом имени BLE_ADVDATA_NO_NAME.
4. Заполните init.srdata.p_manuf_specific_data новыми специфическими данными производителя.
5. Скомпилируйте, загрузите и посмотрите, что получится.

Теперь Ваше устройство должно выглядеть в nRF Connect так:

nRF5x advertising test fig03

Функция advertising_init() должна выглядеть примерно так:

static void advertising_init(void)
{
   ret_code_t             err_code;
   ble_advertising_init_t init;
   memset(&init, 0, sizeof(init));
 
// Установка данных производителя (manufacturing data):
   ble_advdata_manuf_data_t manuf_data; // Переменная для хранения данных производителя
   uint8_t data[]                = "SomeData!";   // Ваши данные для оповещения
   manuf_data.company_identifier =  0x0059;       // ID компании Nordic
   manuf_data.data.p_data        = data;
   manuf_data.data.size          = sizeof(data);
   init.advdata.p_manuf_specific_data = &manuf_data;
 
// Сборка структуры данных оповещения для передачи в @ble_advertising_init:
   init.advdata.name_type = BLE_ADVDATA_SHORT_NAME; // Использовать укороченное имя
   init.advdata.short_name_len = 6; // Передавать только первые 6 букв имени
   init.advdata.include_appearance      = true;
   init.advdata.flags                   = BLE_GAP_ADV_FLAGS_LE_ONLY_GENERAL_DISC_MODE;
   init.advdata.uuids_complete.uuid_cnt = sizeof(m_adv_uuids) / sizeof(m_adv_uuids[0]);
   init.advdata.uuids_complete.p_uuids  = m_adv_uuids;
   int8_t tx_power                   = -4;  // Установка уровня мощности
   init.advdata.p_tx_power_level = &tx_power;
 
// Подготовка scan response с пакетом специфических данных производителя:
   ble_advdata_manuf_data_t  manuf_data_response;
   uint8_t                     data_response[] = "Many_bytes_of_data";
   manuf_data_response.company_identifier  = 0x0059;
   manuf_data_response.data.p_data         = data_response;
   manuf_data_response.data.size           = sizeof(data_response);
   init.srdata.name_type = BLE_ADVDATA_NO_NAME;
   init.srdata.p_manuf_specific_data = &manuf_data_response;
 
   init.config.ble_adv_fast_enabled  = true;
   init.config.ble_adv_fast_interval = APP_ADV_INTERVAL;
   init.config.ble_adv_fast_timeout  = APP_ADV_DURATION;
 
   init.evt_handler = on_adv_evt;
 
   err_code = ble_advertising_init(&m_advertising, &init);
   APP_ERROR_CHECK(err_code);
 
   ble_advertising_conn_cfg_tag_set(&m_advertising, APP_BLE_CONN_CFG_TAG);
}

[Ссылки]

1. Bluetooth low energy Advertising, a beginner's tutorial site:nordicsemi.com.
2. Bluetooth Smart and the Nordic's Softdevices - Part 1 GAP Advertising site:nordicsemi.com.
3nRF5 SDK Peer Manager.
4. BSP BLE Button Module site:nordicsemi.com.
5. nRF52 DK site:nordicsemi.com.
6. nRF52840 Dongle site:nordicsemi.com.
7. nRF5 SDK.
8. nrf51-ble-tutorial-advertising-master.zip.
9. nrf51-ble-tutorial-advertising-SDK11.zip.
10. setting advdata.p_tx_power_level site:devzone.nordicsemi.com.
11. Company identifiers are unique numbers assigned by the Bluetooth SIG site:bluetooth.com.
12Bluetooth: аббревиатуры и термины.
13. 210716BLE-Advertising-tutorial.zip - пример кода, утилиты, документация.