BlueNRG-LP для передачи данных в диапазоне 2,4 ГГц без BLE

2 апреля

учёт ресурсовсветотехникапотребительская электроникаинтернет вещейSTMicroelectronicsстатьяинтегральные микросхемыбеспроводные технологиисредства разработки и материалыCortex-M0Bluetooth2400 МГцIoTинтернет вещейBLE 5.2

Александр Калачев (г. Барнаул)

С беспроводным контроллером BlueNRG-LP от STMicroelectronics, благодаря решениям радиотракта и низкоуровнему драйверу, у разработчиков есть возможность реализовать оптимальную для текущей задачи топологию и логику работы беспроводной сети, включая опции спящего режима и возможность обновления программного обеспечения «по воздуху».

BlueNRG-LP – серия микросхем BlueNRG-3xxx производства STMIcroelectronics, выпускаемых в разных корпусах и отличающихся размером памяти и количеством выводов. BlueNRG-LP является одной из лучших в своем классе по таким параметрам как потребление, бюджет радиоканала и избирательность. Поддержка режима повышенной дальности Long Range, встроенный балун и сверхмалое потребление в спящем режиме позволяют разрабатывать современные устройства интернета вещей, отличающиеся малыми размерами и экономичной ценой.

Главные характеристики BlueNRG-LP – это передача данных на скоростях до 2 Мбит/с, режим большой дальности Long Range (кодированный PHY), модернизированные широковещательные сообщения (Advertising extention), поддержка расширенной длины пакета. Кроме того, BlueNRG-LP обеспечивает расширенную аппаратную поддержку безопасной передачи данных за счет специальных аппаратных функций: генератора истинных случайных чисел (RNG), шифрования AES, ускорителя открытого ключа (PKA), блока вычисления CRC, 48-битного уникального идентификатора, защиты от чтения и записи Flash-памяти. BlueNRG-LP можно настроить для поддержки автономных приложений или приложений сетевого процессора [1, 2].

Помимо работы со стеком протоколов BLE, BlueNRG-LP допускает возможность работы в проприетарном режиме, позволяя разработчику наиболее оптимально реализовывать протоколы обмена и режимы радиотракта для решения конкретной прикладной задачи.

Необходимость в проприетарном (собственном) протоколе работы радиоканала может возникнуть при решении задач автоматизации производства (перехода на беспроводные каналы обмена при сохранении логики работы существующих), при разработке систем охранно-пожарной сигнализации, создании или модернизации распределенных систем учета потребления ресурсов.

Программный пакет STSW-BNRGLP-DK

Программный пакет STSW-BNRGLP-DK для платформы BlueNRG-LP kit (STEVAL-IDB011V1) предоставляет собой предварительно скомпилированную библиотеку Bluetooth LE с полным набором API и связанных с ними call-back-функций вызова событий для доступа к функциональности Bluetooth LE, предлагаемым устройством BlueNRG-LP. STSW-BNRGLP-DK доступен на сайте STMicroelectronics после несложной процедуры регистрации [3].

Пакет предоставляет набор демонстрационных приложений Bluetooth LE, касающихся некоторых типичных сценариев работы с малым энергопотреблением Bluetooth. Каждое демонстрационное приложение поставляется с полным набором заголовочных и исходных файлов. STSW-BNRGLP-DK SW содержит полный набор периферийных драйверов (заголовочных и исходных файлов), которые позволяют взаимодействовать с периферийными устройствами (6xPWM, 2xI2C, 2xSPI/I2S, SPI, USART, UART, PDM, 12-битный АЦП).

Программный пакет также включает в себя приложение BlueNRG-LP Navigator PC, которое обеспечивает интерактивный, простой и удобный интерфейс для выбора и запуска демонстрационных приложений ресурсов, доступных в пакете BlueNRG-LP DK SW, без необходимости дополнительного оборудования. Он также предоставляет 3D-вид всех доступных комплектов BlueNRG-LP и информацию о соответствующих компонентах HW.

Мастер параметров инициализации радио BlueNRG-LP, входящий в комплект поставки, позволяет определить значения, необходимые для правильной инициализации стека Bluetooth LE BlueNRG-LP на основе конкретного сценария пользовательского приложения. Файл заголовка конфигурации, сгенерированный из выбранных значений параметров, далее используется в папке конкретного пользовательского программного приложения.

Интересной опцией пакета STSW-BNRGLP-DK является низкоуровневый драйвер радиотракта BlueNRG-LP 2,4 ГГц для отправки и приема пакетов без использования Bluetooth. Представлены также примеры использования радиотракта на основе низкоуровневого драйвера, которые могут применяться в качестве опорных примеров для создания более сложных приложений, использующих радио BlueNRG-LP. Сами примеры, их описание, а также возможность загрузки в отладочную плату доступны через приложение BlueNRG-LP Navigator (рисунок 1а и 1б). Путь к примерам по умолчанию – «C:\Users\1111\ST\BlueNRG-LP DK 1.0.0\Projects\Peripheral_Examples\Examples_MIX\RADIO», где вместо «1111» будет имя пользователя под которым был установлен программный пакет STSW-BNRGLP-DK.

Рис. 1. BlueNRG-LP Navigator

Рис. 1. BlueNRG-LP Navigator

Примеры представлены для сред разработки IAR Embedded Workbench for ARM и KEIL uVision 5 for ARM. Сам набор базовых примеров довольно полный (затронуты практически все основные аспекты работы радиотракта в проприетарном режиме), что позволяет вполне уверенно создавать на его базе собственные более сложные проекты:

  • Automatic Channel Management Demo – автоматическое переключение каналов при передаче. Бит INC_CHAN маски ActionTag используется для автоматического изменения канала. Устройство отправляет пакет из 16 байт полезной нагрузки, и после каждой передачи, начиная с канала 22, канал меняется. Переключение каналов задается функцией RADIO_SetChannel, в данном примере установлен инкремент канала, равный двум. В этом примере для планирования передачи используется механизм ActionPacket, предоставляемый драйвером BlueNRG_radio.
  • Beep demo. Устройство непрерывно посылает пакет по трем различным каналам: 37, 38 и 39. Пакет состоит из 16 байт полезной нагрузки, и после каждой передачи канал меняется. Следующая настройка канала выполняется не автоматически, как в примере AutomaticChMgm, а внутри функции callback conditionRoutine, используемой механизмом ActionPacket. Изменение канала осуществляется на передающей стороне в функции conditionRouting. Также можно включить функцию шифрования (конфигурация «Beep_Encryption»).
  • Beep multi state demo – пример реализации передачи данных, аналогичный Beep Demo, но работающий с несколькими состояниями. Определено несколько конечных автоматов с разными параметрами передачи. В процессе работы происходит последовательное переключение автоматов. Включить/отключить шифрование можно с помощью массива encryption_values[]: 1 – включить шифрование, а 0 – отключить. Используя пример Sniffer MultiState, можно получать сообщения, передаваемые устройством с примером BeepMultiState.
  • Serial port demo – пример организации беспроводной двунаправленной связи типа «точка-точка» (беспроводной удлинитель UART-интерфейса). Соответствует примеру BLE Chat для предыдущих поколений семейства BlueNRG. Для работы примера потребуются два устройства BlueNRG-LP с прошивкой Serial port demo, подключенных к последовательным терминалам на двух COM-портах. Передаваемое сообщение должно оканчиваться символом возврата каретки. Каждое устройство всегда запрограммировано на прием с определенным временным окном (CHAT_RECEIVE_TIMEOUT). Если пользователь записывает сообщение в последовательный терминал, а затем отправляет символ «возвратной каретки», передача произойдет с этим сообщением в качестве полезной нагрузки. Максимальная полезная нагрузка составляет 31 байт. Протокол обмена требует передачи пакета ACK (это пакет из 1 байта полезной нагрузки, определяемый как 0xAE и имеющий поле длины 0) для подтверждения приема. Полученное сообщение выводится на последовательный терминал. В случае потери подтверждения или его неполучения передатчик сделает три повторные попытки передачи сообщения. В этом примере слой hal_radio используется для планирования передач и приемов. Функция RXCallback, предоставляемая API HAL_RADIO_SendPacketWithAck, определяет поведение радио в соответствии с флагами IRQ. То же самое относится и к функции TXCallback, предоставляемой API HAL_RADIO_ReceivePacketWithAck. Проект также содержит конфигурацию, в которой включена функция шифрования.
  • Remote Control demo – базовый пример сценария дистанционного управления на BlueNRG_LP: нажатие кнопки «PUSH 1» на передающем устройстве приводит к переключению LED1 на приемном устройстве. Для примера необходимы два устройства с прошивкой Remote Control demo. При нажатии кнопки «PUSH 1» на одной плате вторая плата включит или выключит LED1, и наоборот. При получении пакета на второй плате переключается состояние светодиода LED1. В этом примере слой HAL_radio используется для планирования передач и приемов.
  • Sniffer – приложение-сниффер на определенном частотном канале. Приложение прослушивает заданный частотный канал выводит в терминал полученный пакет с определенным BLE_ADV_ACCESS_ADDRESS. Как только пакет получен, в терминале отображается RSSI, метка времени и весь кадр. Также можно включить функцию шифрования (Encryption configuration).
  • Sniffer multi state demo – приложение-сниффер частотного канала с возможностью задания последовательности и параметров прослушивания. В терминал выводится уровень сигнала принятого пакета, метка времени и сами данные.
  • TX/RX – примеры, демонстрирующие простой сценарий приема/передачи данных (TX/RX). Для работы примера потребуются два устройства: одно с конфигурацией TX, другое с конфигурацией RX. TX – сторона передатчика, RX, соответственно, приемника. Передатчик отправляет количество пакетов, равное MAX_NUM_PACKET. Протокол обмена требует передачи пакета ACK для подтверждения приема пакета. Если происходит IRQ_TIMEOUT, счетчик timeout_error_counter увеличивается, если происходит IRQ_CRC_ERR, увеличивается счетчик crc_error_counter. В последний байт пакета отправляется счетчик, который увеличивается с увеличением количества отправленных пакетов. Как только максимальное количество пакетов для отправки достигнуто, сводка связи распечатывается с информацией об ошибках тайм-аута, ошибках CRC и частоте ошибок пакетов (PER). Затем после небольшой задержки последовательность повторяется.

В примерах также представлена конфигурации приемника и передатчика для скорости 2 Мбит/с:

  • Star Network Master – пример центрального (ведущего) узла сети топологии «звезда». Центральный узел знает количество подчиненных устройств (NSLAVES) в сети и их адреса (SLAVE_DEVx), данные значения настраиваются заранее в соответствии с планируемым количеством поддерживаемых ведомых устройств. Периодически ведущее устройство запрашивает данные у подчиненных устройств и при их наличии ждет получения. Если данные получены, то ведущий отвечает пакетом ACK выбранному ведомому устройству. В последовательный порт выводится статистика работы сети (лог): количество отправленных запросов данных, количество полученных пакетов данных, количество неудовлетворенных запросов данных для каждого из ведомых устройств в сети.
  • Star Network Slave – пример ведомого устройства сети топологии «звезда». Ведомое устройство ожидает запроса данных от ведущего. Как только запрос получен, отправляется пакет данных и ожидается пакет подтверждения приема – ACK от ведущего. Если пакет ACK не получен, то попытка передачи данных повторяется. Ведомый может отправить до трех пакетов данных ведущему, если пакет ACK не получен. Проект позволяет задать новое ведомое устройство в сети, просто изменив значение BLE_NETWORK_ID на новый адрес.
  • Throughput TX/ Throughput RX – пример приложения для оценки пропускной способности радиоканала. Представлен в двух конфигурациях – одно- и двунаправленной передачи. В однонаправленной версии показывается время, необходимое для передачи пакета, от вызова команды на передачу, до срабатывания callback-функции на событие передачи. Время измеряется периферийным таймером (TIM): выдается время после передачи заданного числа пакетов — MAX_NUM_PACKET (по умолчанию = 1000). Однонаправленная версия не требует обязательного наличия приемного узла, оценивается только само время на передачу пакетов без учета времени на их доставку, пакеты подтверждения и так далее.

Для тестирования примера Throughput RX необходимы два устройства: одно с двунаправленной конфигурацией Throughput RX, второе с двунаправленной конфигурацией Throughput TX. Устройство RX получит пакет, а затем отправит обратно пакет ACK. Результаты тестирования будут выводиться с платы с конфигурацией Throughput TX. Настройки тестирования (количество байт полезной нагрузки или количество пакетов, используемых для измерения) проводятся также только на передающей стороне.

  • Sleep TX/Sleep RX – парный пример реализации обмена данными с опцией перехода в режим пониженного энергопотребления.
  • OTA_Client – клиентская часть приложения для обновления прошивки по радиоканалу (Over-The-Air Firmware Upgrade). OTA-cервер отправляет двоичный образ клиенту, который загружает его в свою Flash-память. Для передачи используется проприетарный протокол, реализованный на базе низкоуровневого драйвера радиотракта BlueNRG-LP. Для запуска примера необходимы два устройства: одно с конфигурацией клиента, другое с конфигурацией сервера. В конфигурации сервера нужно открыть последовательный терминал для передачи двоичного образа по стандартному протоколу YMODEM. Например, проект TxRx предоставляет OTA-конфигурации RX_Use_OTA_Reset Manager и TX_Use_OTA_Reset Manager, позволяющие создавать соответствующие образы для передачи на клиентское устройство OTA. В конфигурациях Tx Rx OTA кнопка «PUSH 2» позволяет активировать OTA_Client и получить новый образ с OTA-сервера.

Структура пакетов проприетарного режима работы радиотракта BlueNRG-LP

В проприетарном режиме работы BlueNRG-LP предусмотрен только один формат пакета, и он достаточно простой (рисунок 2), [4].

Рис. 2. Формат пакета BlueNRG-LP: а) без шифрования; б) – с шифрованием

Рис. 2. Формат пакета BlueNRG-LP: а) без шифрования; б) – с шифрованием

Пакет состоит из шести полей, из которых только четыре доступны пользователю:

  • preamble – преамбула. По умолчанию имеет длину 1 байт, но пользователь может определить, сколько раз повторять преамбулу, с помощью функции RADIO_SetPreambleRep();
  • networkId – адрес устройства, выраженный в 4 байтах. Приемное устройство принимает только те пакеты, чье поле networkId совпадает с полем в его собственном адресе. networkId должен соответствовать следующим правилам:
    • иметь не более 6 последовательных нулей или единиц;
    • не все 4 октета должны быть равны;
    • иметь не более 24 переходов;
    • иметь минимум 2 перехода в старших 6 битах.

Поле networkId доступно пользователю через функции API RADIO_SetTxAttributes() или API HAL_RADIO_SetNetworkID().

  • header – заголовок. Может принимать любые значения, а его длина составляет 1 байт. Может использоваться как байт данных, но к этому полю не применяется шифрование.
  • length – длина поля данных. Поле устанавливается при передаче пакета или же считывается из принятого пакета. Максимальное количество байт полезной нагрузки (с шифрованием или без него) – 255. Пользователь может установить значение этого порога (0…255) на аппаратном уровне через API RADIO_SetMaxRecievedLength().

Если шифрование включено, то от максимальной длины поля данных надо вычесть 4 байта — они зарезервированы для поля MIC, добавляемого в пакет.

Таким образом, допустимые длины поля данных составляют:

  • для каналов передачи данных – 255;
  • для каналов передачи данных с шифрованием – 251;
  • для рекламных каналов – 255;
  • для рекламных каналов с шифрованием – 251.

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

Если шифрование включено, то шифруется только поле данных. Другие поля, включая поле заголовка и поле длины, не шифруются.

  • CRC – контрольная сумма. Используется для идентификации поврежденных пакетов. Длина составляет 3 байта, генерируется и проверяется аппаратно, соответственно, во время передачи и приема. Пользователь может настроить начальное значение для расчета CRC, за исключением рекламных каналов, где начальное значение установлено равным 0x555555. Аппаратная функция CRC может быть отключена, при этом аппаратное обеспечение не будет добавлять CRC при передаче и не будет проверять его во время приема.

API-драйвера в проприетарном режиме

Драйвер низкого уровня радио состоит из четырех файлов [4]:

  • bluenrg_lp_ll_radio.h;
  • bluenrg_lp_ll_radio.c;
  • bluenrg_lp_hal_radio.h;
  • bluenrg_lp_hal_radio.c.

Интерфейс драйвера предоставляет набор API (файл bluenrg_lp_ll_radio.c), который позволяет реализовывать следующие функции:

  • инициализацию радио;
  • шифрование;
  • настройку физического уровня радиоканала (1 Мбит/с, 2 Мбит/с, S = 2, S = 8);
  • управление каналами связи;
  • настройку идентификатора сети, использования контрольных сумм CRC, уровня выходной мощности;
  • настройку максимальной длины принимаемых пакетов и времени ожидания приема;
  • тестовые команды (генерация тона).

Есть обстоятельство, несомненно, позитивное для разработчика – набор API-функций драйвера небольшой и интуитивно понятный. Список API, управляющих настройками радио в проприетарном режиме, выглядит следующим образом:

  • RADIO_Init();
  • RADIO_SetEncryptionCount();
  • RADIO_SetEncryptionAttributes();
  • RADIO_SetEncryptFlags();
  • RADIO_EncryptPlainData();
  • RADIO_Set_ChannelMap();
  • RADIO_SetChannel();
  • RADIO_SetTxAttributes();
  • RADIO_SetBackToBackTime();
  • RADIO_SetTxPower();
  • RADIO_SetReservedArea();
  • RADIO_MakeActionPacketPending();
  • RADIO_SetPhy();
  • RADIO_SetMaxRecievedLength();
  • RADIO_SetPreambleRep();
  • RADIO_SetDefaultPreambleLen();
  • RADIO_DisableCRC();
  • RADIO_StopActivity();
  • RADIO_StartTone();
  • RADIO_StopTone().

Большинство API-функций изменяет состояние конечного автомата, передаваемого им в качестве параметра. С другой стороны, некоторые параметры являются глобальными, то есть они действительны для всех конечных автоматов. Одним из них является тайм-аут приема, который устанавливается функцией ADIO_SetGlobalReceiveTimeout(), задающей значение длительности окна приема в микросекундах.

Ключевой структурой данных, используемой явно или неявно проприетарным драйвером, является связанный список элементов типа ActionPacket. Сами элементы ActionPacket – это структуры данных (в терминах языка Си), которые, в сочетании с описанными выше API, полностью определяют операции радиотракта на передачу или прием. Данная структура также включает в себя ряд указателей на callback-функции, которые позволяют пользователю определить цепочку действий при организации собственных протоколов.

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

  • StateMachineNo указывает номер состояния конечного автомата для данного действия (0…7);
  • ActionTag – конфигурация текущего действия. Подробная информация о флагах в ActionTag будет ниже;
  • MaxReceiveLength устанавливает максимальное количество принимаемых байт и принимает значение 0…255.
  • WakeupTime содержит время пробуждения в микросекундах, если оно относительное. Если оно абсолютное, то выражается в единицах системного времени (STU);
  • *next_true – указатель на следующий элемент ActionPacket, если condRoutine() возвращает True;
  • *next_false – указатель на следующий элемент ActionPacket, если condRoutine() возвращает False;
  • (*condRoutine) (ActionPacket*) – указатель на пользовательскую функцию обратного вызова (callback-функцию), необходимую для принятия решения о следующем действии в связанном списке элементов ActionPacket (можно рассматривать как список запланированных действий/протокол обмена). Вызов функции критичен по времени выполнения и должен закончиться в течение 45 мкс;
  • (*dataRoutine) (ActionPacket*, ActionPacket*) – callback-функция для работы с данными;
  • *data – указатель на массив с данными для отправки (заголовок, длина и поле данных) при передаче. При приеме – указатель на массив, в который копируются полученные данные (максимальный размер массива должен быть не более MaxReceiveLength);
  • timestamp_receive – поле, содержащее метку времени получения пакета. Предназначено для использования в callback-процедурах dataRoutine(). Имеет смысл только в режиме приема. Метка времени приводится в единицах STU. Один STU равен 625/256 мкс;
  • status – регистр статуса с информацией о результатах выполнения операции;
  • rssi – уровень сигнала принятого пакета (RSSI, только для режима приема).

ActionTag – это битовая маска, используемая для включения различных функций радио, используемых ActionPacket (биты поля состояния в пакете действий совпадают с регистром состояния BLE-контроллера):

  • 7 – TIMESTAMP_POSITION. Этот бит устанавливает, где находится временная метка – вначале пакета или в его конце:
    • 0 – конец пакета, когда демодулятор получает последний бит принятого пакета или когда последний переданный бит был смещен из блока передачи;
    • 1 – начало пакета, когда демодулятор обнаруживает преамбулу + адрес доступа. Только Rx.
  • 6 – INC_CHAN. Данный бит активирует автоматическое увеличение канала. API RADIO_SetChannel() устанавливает значение приращения:
    • 0 – нет приращения;
    • 1 – автоматическое увеличение.
  • 5 RELATIVE определяет, рассматривается ли поле времени пробуждения ActionPacket как абсолютное время или как относительное:
    • 0 – абсолютное;
    • 1 – относительное.
  • 4 – WHITENING_DISABLE. Этот бит определяет, отключено ли отбеливание:
    • 0 – отбеливание включено;
    • 1 – отбеливание отключено.
  • 3 RESERVED – зарезервирован.
  • 2 TIMER_WAKE UP. Бит определяет, действие (RX или TX) будет выполняться на основе времени от завершения операции до начала следующей (back-to-back time) или на основе времени пробуждения. В первом случае этот бит игнорируется, так как он будет выполняться всегда, в зависимости от времени пробуждения:
    • 0 – на основе back-to-back (по умолчанию 150 мкс);
    • 1 – на основе времени пробуждения.
  • 1 – TXRX. Данный бит определяет, текущая операция является операцией приема (RX) или передачи (TX):
    • 1 – передача (TX);
    • 0 – прием (RX).
  • 0 PLL_TRIG. Этот бит активирует радиочастотную калибровку PLL:
    • 0 – калибровка радиочастот отключена;
    • 1 – калибровка радиочастот включена.

Пользователь должен установить данный бит при начале работы.

Связанный список структур ActionPacket можно рассматривать как описание конечного автомата, реализующего пользовательский проприетарный протокол обмена данными и/или работы сети.

Примеры кода приложений с использованием драйвера проприетарного режима

Существуют два способа написания приложения: первый основан на слое HAL, состоящем в основном из четырех API, а второй – на использовании структуры данных ActionPacket [4].

Способ 1. Уровень HAL

Самый простой способ – использовать набор API, предоставленных в драйвере HAL radio (файл bluenrg_lp_hal_radio.c), который позволяет настроить радио для выполнения таких действий, как:

  • отправить пакет;
  • отправить пакет и затем дождаться приема пакета (ACK);
  • ожидать прием пакета;
  • ожидать прием пакета, и если пакет получен, то отправить пакет-подтверждение (ACK).

В подходе на уровне HAP-API пользователю не нужно использовать ActionPacket для настройки операций радио, но требуется настроить указатель на пользовательские callback-функции, который обрабатывает различную информацию, в соответствии с выполненным действием:

  • передача – TX: статус прерывания – IRQ;
  • прием – RX: статус IRQ, RSSI, временная метка и полученные данные.

Пользовательские callback-функции вызываются в режиме прерывания, в частности – BLE_TX_RX_IRQHandler(), который имеет максимальный приоритет.

Второй параметр каждой API – это относительное время в микросекундах, которое показывает, когда может быть вызвана следующая функция, начиная с момента вызова текущей. Эта задержка должна быть достаточно большой, так как в противном случае нет возможности запрограммировать радиотаймер, и возвращается код ошибки.

Пользователь может выбрать желаемое время, не принимая во внимание время, которое радио использует для его настройки. Затем задержка, которая передается API, представляет собой момент передачи первого бита или запуска окна приема.

Пример 1. Передача данных с применением функций слоя HAL

Рассмотрим пример, в котором радио запрограммировано на периодическую отправку пакета с промежутком времени между двумя последовательными пакетами в 10 мс. Каждый пакет содержит 3 байта данных.

Примечание. За инициализацией радио следует инициализация модуля таймера.

Структура Action Packed не используется непосредственно в этом примере, но она так или иначе используется через API уровня HAL.


uint8_t send_packet_flag = 1;
uint8_t packet[5];
int main(void)
{
HAL_VTIMER_InitType VTIMER_InitStruct = {HS_STARTUP_TIME, INITIAL_CALIBRATION,
CALIBRATION_INTERVAL};

/* Инициализация системы */
if (SystemInit(SYSCLK_64M, BLE_SYSCLK_32M) != SUCCESS) {
/* Действия в случае ошибки инициализации системы тактирования */
    while(1);
}
/* Инициализация радиотракта */
RADIO_Init();
/* Инициализация таймера */
HAL_VTIMER_Init(&VTIMER_InitStruct);
/* Задание сетевого идентификатора - Network ID */
HAL_RADIO_SetNetworkID(0x88DF88DF);
/* Установка выходной мощности (в данном случае – максимальный уровень) */
RADIO_SetTxPower(MAX_OUTPUT_RF_POWER);
/* Формирование пакета */
packet[0] = 1; /* Поле Header */
packet[1] = 3; /* Поле Length */
packet[2] = 2; /* Три байта данных */
packet[3] = 3;
packet[4] = 4;

while(1) {
HAL_VTIMER_Tick();
/* Если передача завершена, планируется следующее действие */
if(send_packet_flag == 1) {
send_packet_flag = 0;
/* запланировать операцию с параметрами: channel, wakeupTime, relative time, dataCallback */
HAL_RADIO_SendPacket( 22, 10000, 1, packet, TxCallback );
}
}
return 0;
}
void BLE_TX_RX_IRQHandler(void)
{
RADIO_IRQHandler();
}

Сама callback-функция для передачи:


uint8_t TxCallback(ActionPacket* p, ActionPacket* next)
{
/* Проверка завершения предыдущей передачи */
if( p ->status & BLUE_STATUSREG_PREVTRANSMIT) != 0) {
/* Установка флага о выполнении операции*/
send_packet_flag = 1;
}
return TRUE;
}

Пример 2. Прием данных (RX) со слоем HAL

Приведем пример, где радио запрограммировано периодически переходить в состояние RX. Задержка между каждой операцией RX составляет 9 мс. Это гарантирует, что после первого удачного приема RX-устройство всегда просыпается за 1 мс до того как TX-устройство начнет посылать пакет (так называемый guard time).

Примечание. Максимальная длина полезной нагрузки установлена в 255 байт.

Тайм-аут RX составляет 20 мс. Это значение достаточно велико, чтобы гарантировать получение хотя бы одного пакета.

Структура Action Packed не используется непосредственно в этом примере, но она используется через API уровня HAL.


uint8_t rx_flag = 1;
uint8_t packet[MAX_PACKET_LENGTH];
int main(void)
{
HAL_VTIMER_InitType VTIMER_InitStruct = {HS_STARTUP_TIME, INITIAL_CALIBRATION,
CALIBRATION_INTERVAL};
/* Инициализация системы */
if (SystemInit(SYSCLK_64M, BLE_SYSCLK_32M) != SUCCESS) {
/* Действия в случае ошибки инициализации системы тактирования */
while(1);
}
/* Инициализация радиотракта */
RADIO_Init();
/* Инициализация таймера */
HAL_VTIMER_Init(&VTIMER_InitStruct);
/* Установка сетевого идентификатора - Network ID */
HAL_RADIO_SetNetworkID(0x88DF88DF);
while(1) {
HAL_VTIMER_Tick();
/* Если операция выполнена, планируется следующая */
if(rx_flag == 1) {
rx_flag = 0;
/* Планировка следующей операции с параметрами: channel, wakeupTime,
relative time, RX timeout, timestamp, dataCallback */
HAL_RADIO_ReceivePacket ( 22, 9000, packet, 20000, 255, RxCallback );
}
}
return 0;
}
void BLE_TX_RX_IRQHandler(void)
{
RADIO_IRQHandler();
}

Задание самой callback-функции на прием:


uint8_t RxCallback(ActionPacket* p, ActionPacket* next)
{
/* Проверка завершения предыдущей операции */
if( (p->status & BLUE_STATUSREG_PREVTRANSMIT) == 0) {
/* Проверка CRC */
if((p->status & BLUE_INTERRUPT1REG_RCVOK) != 0) {
/* Извлечение информации из пакета */
// p->data contains the data received: header field | length field | data field
// p->rssi
// p->timestamp_receive
}
/* Проверка истечения тайм-аута на прием */
else if( (p->status & BLUE_INTERRUPT1REG_RCVTIMEOUT) != 0) {
}
/* Проверка наличия ошибки CRC */
else if ((p->status & BLUE_INTERRUPT1REG_RCVCRCERR) != 0) {
}
}
/* Установка программного флага для следующей операции */
rx_flag = 1;
return TRUE;
}

Способ 2. ActionPacket

Наиболее гибким способом разработки приложения на основе проприетарного протокола является объявление ряда структур ActionPacket, в соответствии с действиями, которые должны быть предприняты в ходе радиообмена. Программист заполняет эти структуры описанием выполняемых операций. Для каждого элемента ActionPacket должна быть вызвана процедура инициализации RADIO_SetReservedArea().

Элементы ActionPacket связываются между собой в список (как минимум благодаря элементам структуры *next_true, *next_false это возможно сделать с ветвлениями и внутренними циклами). В итоге получается своеобразное описание конечного автомата (графа автомата), реализующего задачу обмена данными или описывающего работу сети из нескольких узлов (работу данного сетевого узла).

Чтобы начать выполнение ActionPacket, необходимо вызвать RADIO_MakeActionPacketPending ().

После этого приложение может:

  • убедиться, что существует возможность вызова последующих процедур из списка структур ActionPacket и выбирается начальная структура;
  • повторно активировать радио вызовом RADIO_MakeActionPacketPending().

Все дальнейшие действия выполняются в режиме обработки прерываний, как и при подходе уровня HAL, но в этом случае пользователь обрабатывает две функции обратного вызова:

  • Порядок состояний condRoutine(). condRoutine() предоставляет результат выполнения текущего ActionPacket и возвращает TRUE или FALSE. В зависимости от этого, следующий ActionPacket, связанный с текущим, выбирается из двух возможных вариантов:
    • next_true ActionPacket1;
    • next_false ActionPacket2.

Цель этого механизма – запланировать следующую операцию, в зависимости от результатов выполненной.

Например, типичная ситуация при приеме пакета:

  • запланировать next_true ActionPacket, если пакет получен успешно (ActionPacket1);
  • запланировать next_false ActionPacket, если пакет получен с ошибкой (ActionPacket2).
  • Процедура обработки данных dataRoutine(). dataRoutine() предоставляет информацию в виде полученных или переданных данных, RSSI, временных меток и прочего. Кроме того, он предназначен для формирования передаваемых данных для следующего пакета на основе последних полученных данных.

Наличие нескольких callback-функций дает возможность программисту избежать возникновения «узких» мест при обработке событий радиообмена – выполняется основная задача, а обработка приема/передачи пакетов происходит по мере возникновения соответствующих событий. Кроме этого, фреймворк заставляет программиста разбивать код на более мелкие подпрограммы, что приводит к более структурированному коду, который несколько проще в отладке, поддержке и документировании.

Наиболее критичные по времени операции выполняются непосредственно в condRoutine(). Обработка должна завершиться до истечения времени таймаута – до вызова следующей операции радиоконтроллер начнет свою работу (рисунок 3).

Рис. 3. Тайминги во время callback-вызовов

Рис. 3. Тайминги во время callback-вызовов

Максимальное время выполнения процедуры условия составляет примерно столько же, сколько и продолжительность «back-to-back», за вычетом времени, которое радио тратит на радиочастотную настройку. На данный момент можно считать, что радио в сценарии «back-to-back» нуждается в настройке 70 мкс. Затем, если время back-to-back составляет 150 мкс, у condRoutine() есть около 80 мкс, чтобы установить указатель на следующее действие.

Пример 1. Организация передачи данных с применением структур ActionPacket

Запрограммируем радио на периодическую отправку пакета с промежутком времени между двумя последовательными пакетами в 10 мс. Каждый пакет содержит 3 байта данных.


int8_t packet[5];
ActionPacket txAction;
int main(void)
{
HAL_VTIMER_InitType VTIMER_InitStruct =
{HS_STARTUP_TIME, INITIAL_CALIBRATION,
CALIBRATION_INTERVAL};
/* Инициализация системы */
if (SystemInit(SYSCLK_64M, BLE_SYSCLK_32M)!=SUCCESS) {
/* Действия в случае ошибки инициализации тактирования */
while(1);
}
/* Инициализация радиотракта */
RADIO_Init();
/* Инициализация таймера */
HAL_VTIMER_Init(&VTIMER_InitStruct);
/* Установка начального канала (22) и инкремента каналов (0) */
RADIO_SetChannel(STATE_MACHINE_0, 22, 0);
/* Установка сетевого идентификатора Network ID и начального значения CRC */
RADIO_SetTxAttributes(STATE_MACHINE_0,0x88DF88DF, 0x555555);
/* Установка передачи на максимальный уровень */
RADIO_SetTxPower(MAX_OUTPUT_RF_POWER);
/* Пакет данных для передачи */
packet[0] = 1; /* заголовок - Header */
packet[1] = 3; /* Длина поля данных - Length */
packet[2] = 2; /* Данные */
packet[3] = 3;
packet[4] = 4;
/* Формируем ActionPacket для отправки пакета с запланированным следующим действием
   в виде уже определенного элемента */

txAction.StateMachineNo = STATE_MACHINE_0;
/* Передача с активацией по относительному времени с разрешением калибровки PLL */
txAction.ActionTag = RELATIVE | TIMER_WAKEUP | TXRX |
PLL_TRIG;
/* Пауза 10 мс перед операцией */
txAction.WakeupTime = 10000;
/* Указатель на данные для передачи */
txAction.data = packet;
/* Указатель на следующий элемент ActionPacket в списке - txAction */
txAction.next_true = &txAction;
/* В случае неуспешного завершения предыдущей операции ничего не делаем */
txAction.next_false = NULL_0;
/* Задаем Condition routine для выбора следующего ActionPacket */
txAction.condRoutine = conditionRoutine;
/* Обработчик данных, вызываемый после conditionRoutine */
txAction.dataRoutine = dataRoutine;
/* Записываем подготовленную структуру ActionPacket */
RADIO_SetReservedArea(&txAction);
/* Выполняем ActionPacket */
RADIO_MakeActionPacketPending(&txAction);
while(1) {
HAL_VTIMER_Tick();
}
return 0;
}
void BLE_TX_RX_IRQHandler(void)
{
RADIO_IRQHandler();
}

Callback-функция проверки условия запускает выполнение следующего запланированного пакета действий (tx_Action). Callback-функция обработчика данных в этом случае не используется, но должна быть определена:


uint8_t conditionRoutine(ActionPacket* p)
{
/* Check if the TX action is ended */
if( p ->status & BLUE_STATUSREG_PREVTRANSMIT) != 0) {
}
/* The TRUE schedules the next_true action: txAction */
return TRUE;
}
uint8_t dataRoutine(ActionPacket* p, ActionPacket* next)
{
return TRUE;
}

Пример 2. Прием данных с применением структур ActionPacket

Рассмотрим случай, когда радио запрограммировано периодически переходить в состояние приема. Задержка между каждой операцией RX составляет 9 мс. Это гарантирует, что после первого успешного приема RX-устройство всегда просыпается за 1 мс до того как TX-устройство начнет посылать пакет (guard time). Тайм-аут RX составляет 20 мс. Это значение достаточно велико, чтобы гарантировать получение хотя бы одного пакета.


ActionPacket rxAction;
int main(void)
{
HAL_VTIMER_InitType VTIMER_InitStruct =
{HS_STARTUP_TIME, INITIAL_CALIBRATION,
CALIBRATION_INTERVAL};
/* Инициализация системы */
if (SystemInit(SYSCLK_64M,BLE_SYSCLK_32M)!= SUCCESS) {
/* Действия в случае ошибки инициализации тактирования */
while(1);
}
/* Инициализация таймера */
RADIO_Init();
/* Инициализация таймера */
HAL_VTIMER_Init(&VTIMER_InitStruct);
/* Установка начального канала (22) и инкремента каналов (0) */
RADIO_SetChannel(STATE_MACHINE_0, 22, 0);
/* Установка сетевого идентификатора Network ID и начального значения CRC */
/* Тай-маут на прием 20 мс */
RADIO_SetGlobalReceiveTimeout(20000);
rxAction. MaxReceiveLength = 255;
RADIO_SetTxAttributes(STATE_MACHINE_0, 0x88DF88DF, 0x555555);
/* Формируем ActionPacket для принятия пакета с запланированным следующим действием
   в виде уже определенного элемента */

rxAction.StateMachineNo = STATE_MACHINE_0;
/* Действие на прием по Wakeup Timer в режиме относительного времени, разрешаем
   калибровку PLL calibration */
rxAction.ActionTag = RELATIVE | TIMER_WAKEUP | PLL_TRIG;
/* 9 мс перед началом операции */
rxAction.WakeupTime = 9000;
/* Указатель на массив для приема данных */
rxAction.data = packet;
/* Указатель на следующий ActionPacket: rxAction */
rxAction.next_true = &rxAction;
/* В случае ошибки приема действий не планируется */
rxAction.next_false = NULL_0;
/* задание callback-функции проверки условия для выбора следующего ActionPacket */
rxAction.condRoutine = conditionRoutine;
/* Задание функции-обработчика принятых данных, Data routine, вызывается после
   conditionRoutine */
rxAction.dataRoutine = dataRoutine;
/* Записываются созданные ActionPacket */
RADIO_SetReservedArea(&rxAction);
/* Выполняется ActionPacket */
RADIO_MakeActionPacketPending(&rxAction);
while(1) {
HAL_VTIMER_Tick();
}
return 0;
}
void BLE_TX_RX_IRQHandler(void)
{
RADIO_IRQHandler();
}

Callback-функция проверки условия запускает выполнение следующего запланированного пакета действий (rx_Action). Callback-функция обработчика данных в этом случае не используется, но должна быть определена:


uint8_t conditionRoutine(ActionPacket* p)
{
/* Проверяем окончание приема */
if( (p->status & BLUE_STATUSREG_PREVTRANSMIT) == 0) {
/* Проверяем наличие пакета с корректной контрольной суммой */
if((p->status & BLUE_STATUSREG_RCVOK) != 0) {
}
/* Проверка тайм-аута на прием */
else if( (p->status & BLUE_INTERRUPT1REG_RCVTIMEOUT) != 0) {
}
/* Проверка ошибки CRC */
else if ((p->status & BLUE_INTERRUPT1REG_RCVCRCERR) != 0) {
}
}
/* Запускаем следующую операцию на прием */
return TRUE;
}

// Обработчик данных
uint8_t dataRoutine(ActionPacket* p, ActionPacket* next)
{
/* Проверяем окончание приема */
if( (p->status & BLUE_STATUSREG_PREVTRANSMIT) == 0) {
Проверяем наличие пакета с корректной контрольной суммой 
if((p->status & BLUE_STATUSREG_RCVOK) != 0) {
/* Извлекаем информацию из пакета */
// p->data содержит поля header field | length field | data field
// p->rssi
// p->timestamp_receive
}
/* Проверка истечения таймаута */
else if( (p->status & BLUE_INTERRUPT1REG_RCVTIMEOUT) != 0) {
}
/* Проверка ошибки контрольной суммы */
else if ((p->status & BLUE_INTERRUPT1REG_RCVCRCERR) != 0) {
}
}
return TRUE;
}

Режим обновления прошивки «по воздуху» для проприетарного режима

Помимо возможностей передачи данных по радиоканалу, в драйвере BlueNRG-LP предусмотрена возможность обновления прошивки «по воздуху» (Over-the-Air, OTA).

Для данной процедуры необходимы два узла: сервер и клиент.

Первый узел отвечает за беспроводную отправку двоичного образа на клиентский узел. Клиент выбирает, какое приложение запускать: клиентское приложение OTA, связывающееся с узлом сервера, чтобы получить двоичный образ и загрузить его во Flash-память, или приложение, загруженное ранее (с помощью OTA или другим способом).

Серверное приложение OTA отвечает за отправку «по воздуху» двоичного образа на клиентский узел. Сам сервер получает образ через UART-порт по протоколу YMODEM.

Программное обеспечение серверной части, как и клиентской, реализовано в виде конечного автомата. Конечный автомат сервера реализуется с помощью двух подпрограмм конечного автомата, это:

  • конечный автомат протокола YMODEM;
  • конечный автомат протокола OTA.

Конечный автомат OTA, серверная часть

На рисунке 4 представлен конечный автомат, реализующий узел ОТА-сервера, включая оба протокола — OTA и YMODEM.

Четыре состояния отвечают за YMODEM:

  • SIZE – это первое сообщение YMODEM, в котором пользователь предоставляет размер образа;
  • LOAD – основное состояние, в котором принимается и хранится в оперативной памяти до 1 кбайт данных нового образа (новой прошивки);
  • WAIT – переходное состояние, в котором проверяется, получен образ прошивки или нет;
  • CLOSE – состояние, ответственное за закрытие связи YMODEM после получения всего образа.

Рис. 4. Серверная часть конечного автомата протокола OTA

Рис. 4. Серверная часть конечного автомата протокола OTA

Любая проблема связи через UART YMODEM приводит к завершению работы приложения и его перезапуску.

За реализацию протокола ОТА отвечают шесть состояний:

  • CONNECTION. Как только размер файла двоичного образа получен (состояние YMODEM SIZE), запускается новая последовательность обновления встроенного ПО, поэтому сервер начинает периодически отправлять пакеты подключения, чтобы показать свою доступность для подключения. Если получен ответ ACK на пакет подключения, то соединение с клиентом считается установленным.
  • SIZE. В этом состоянии сервер отправляет клиенту пакет размера, показывающий размер образа прошивки, который он может отправить.
  • START – это состояние, в котором клиент говорит серверу начать обновление прошивки по воздуху. Клиент может отправить стартовый или не стартовый пакет, в зависимости от размера двоичного образа, полученного в состоянии SIZE. Если сервер получает стартовый пакет, то запускается обновление прошивки OTA. В противном случае сервер переходит в состояние CONNECTION в поисках следующего соединения.
  • DATAREQ. Сервер получает номер пакета для отправки (порядковый номер) от клиента.
  • SENDATA. Сервер отправляет клиенту пакет, запрошенный через порядковый номер. Все пакеты данных имеют одинаковую длину, но последний из них может быть меньше. Если запрос данных все еще не получен, сервер переходит в состояние загрузки YMODEM и получает следующую часть двоичного образа.
  • COMPLITE. Когда клиент подтвердил последний пакет данных, это значит, что весь двоичный образ был передан и операция OTA завершена.

В случае ошибок при передаче данных во время операций OTA предусмотрено определенное количество повторных попыток передачи пакетов. Если повторные попытки исчерпаны, то процесс обновления прерывается, и приложение запускается заново.

Клиентское приложение OTA

Клиентское приложение OTA – это приложение, которое при запуске принимает решение, перейти к пользовательскому приложению или активировать клиентское приложение обновления прошивки OTA.

Клиентское приложение обновления прошивки, как уже упоминалось выше, также реализуется через конечный автомат, который управляет протоколом OTA и загружает во Flash-память узла полученный двоичный образ.

Размер памяти клиентского приложения OTA не превышает 8 кбайт Flash-памяти.

На рисунке 5 представлен конечный автомат для протокола OTA, реализованного для клиентского узла OTA.

Рис. 5. Клиентская часть конечного автомата протокола OTA

Рис. 5. Клиентская часть конечного автомата протокола OTA

Существует 8 состояний ОТА:

  • CONNECTION – начальное состояние для клиента, при котором он ждет поступления пакета подключения от сервера. Если пакет подключения получен, то ответ ACK отправляется обратно. Это действие устанавливает соединение с сервером.
  • SIZE. В этом состоянии клиент получает пакет размера от сервера.
  • START – состояние, в котором клиент отправляет стартовый пакет на сервер. Это приводит к запуску обновления прошивки OTA. Стартовый пакет отправляется только в том случае, если размер двоичного образа соответствует размеру Flash-памяти пользователя.
  • NOTSTART. Размер двоичного изображения не соответствует пользовательской Flash-памяти клиента (он слишком велик), поэтому обновление прошивки OTA не может начаться.
  • DATAREQ. Клиент отправляет на сервер количество пакетов, которое ему необходимо. Это вычисляется с учетом размера образа прошивки и максимального количества байт (изначально заданного как для клиента, так и для сервера) в одном пакете, которые может отправить сервер.
  • SENDATA. Клиент получает запрошенный пакет данных.
  • FLASHDATA. В этом состоянии данные из пакета данных хранятся внутри буфера, и после того как размер буфер больше или равен размеру страницы, весь буфер фактически записывается в пользовательскую Flash-память. Эта операция выполняется также после получения последнего блока образа.
  • COMPLITE. После загрузки всего двоичного изображения во Flash-память пользователя операция OTA завершается.

Типичный сценарий операции обновления прошивки «по воздуху»

Задействуются два устройства:

  • сервер, на котором запущено приложение RADIO_OTA_ResetManager, конфигурация OTA_Server_Ymodem;
  • клиент, выполняющий приложение RADIO_OTA_ResetManager, конфигурация OTA_Client.

Прошивка приложения находится в папке DK \Projects\Peripheral_Examples\Examples_MIX\RADIO \RADIO_OTA_ResetManager.

Шаги, которые необходимо выполнить для обновления прошивки OTA:

  1. Включите питание как плат BlueNRG-LP, так и соответствующих им приложений OTA_Server_Ymodem и OTA_Client. Плата с прошивкой OTA_Server_Ymodem должна быть подключена с помощью USB-кабеля к ПК.
  2. Откройте COM-порт платы с конфигурацией OTA_Server_Ymodem с помощью программы последовательного терминала (TeraTerm, Putty, Hyperterminal или аналогичной).
  3. Выберите режим передачи файла со стандартом YMODEM.
  4. Выберите двоичный образ для загрузки (файл.bin).
  5. После завершения передачи YMODEM также завершается обновление прошивки OTA клиентской платы.

Внутри выпущенного DK можно найти пример приложения, также содержащего две конфигурации, зарезервированные для функциональности клиента OTA. Пример TxRx демонстрирует связь «точка-точка» с помощью низкоуровневого драйвера радио. Конфигурации таковы:

  • TX_Use_OTA_ResetManager;
  • RX_Use_OTA_ResetManager.

Кнопка «PUSH2» STEVAL-IDB0011V1 используется для запуска функциональности клиента OTA, чтобы обновить соответствующий образ.

Пример находится в папке \Projects\Peripheral_Examples\Examples_MIX\RADIO\RADIO_TxRx.

Чтобы интегрировать функциональность клиента OTA в существующее приложение, пользователь должен выполнить следующие действия:

  1. Зарезервируйте первые пять страниц (10 кбайт) Flash-памяти для клиентского приложения. Сделать это можно при помощи специальной переменной ”MEMORY_FLASH_APP_OFFSET»:

MEMORY_FLASH_APP_OFFSET = 0x2800

Пример приложения Tx Rx, конфигурации TX_Use_OTA_Reset Manager или RX_Use_OTA_Reset Manager можно использовать в качестве опорного.

  1. Включите файлы “radio_ota.c” и “radio_ota.h” в проект приложения. Эти файлы находятся в папке \Library\BLE_Application\OTA. Файлы содержат API OTA_Jump_To_Reset_Manager (), используемый для установки переменной оперативной памяти с именем ota_sw_activation и последующего сброса системы.
  2. Определите триггер, который будет использоваться для перехода от пользовательского к клиентскому приложению OTA. Этот триггер используется для вызова функции OTA_Jump_To_Reset_Manager().

Заключение

Работа BlueNRG-LP в проприетарном режиме организуется достаточно просто, но в то же время гибко. Сама структура и программная модель драйвера позволяют легко перекладывать абстрактные модели протоколов работы радио (просто для передачи или работы в сетевых режимах) на программный код. При этом также сохраняется возможность организации обновления прошивки «по воздуху», что весьма практично при поддержке и сопровождении проектов.

Литература

  1. Олег Пушкарев. Запускаем новый BLE 5.2-чип BlueNRG-LP от STMicroelectronics.
  2. BlueNRG-LP – новый чип BLE 5.2 с режимом Long Range
  3. BlueNRG-LP DK SW package
  4. The BlueNRG-LP 2.4 GHz radio proprietary driver
•••

Наши информационные каналы

О компании ST Microelectronics

Компания STMicroelectronics является №1 производителем электроники в Европе. Компоненты ST широко представлены в окружающих нас потребительских товарах – от iPhone до автомобилей разных марок. Лидеры индустриального рынка выбирают компоненты ST за их надежность и выдающиеся технические параметры. В компании ST работает 48 000 сотрудников в 35 странах. Производственные мощности расположены в 12 странах мира. Более 11 тысяч сотрудников заняты исследованиями и разработками – инновационное лидерство ...читать далее

Товары
Наименование
BLUENRG-345MC (ST)
BLUENRG-355AC (ST)
BLUENRG-345AC (ST)
BLUENRG-355MC (ST)
BLUENRG-345MT (ST)
BLUENRG-355VC (ST)
BLUENRG-355MT (ST)
STEVAL-IDB011V1 (ST)