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

Теги статьи:

Радиомодули для беспроводной передачи данных. Часть 5.

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

Введение

В предыдущей статье этого цикла [1] я рассказывал про разработку проектов, включающих беспроводную передачу данных с использованием библиотеки RAIL (Radio Abstraction Interface Layer) и обещал рассказать про следующую ступень этого процесса, именно про систему Connect. Эта система, так же, как и RAIL, интегрирована в Silicon Labs Simplicity Studio и является частью Flex SDK (Software Development Kit). Разработанные в системах RAIL/Connect проекты предназначены для портирования прежде всего на SoC (System on Chip) семейства EFR32FG фирмы, содержащие в одном корпусе МК и радио-трансивер.

Начнём с определения места системы Connect среди разнообразных сетевых протоколов, используемых сегодня. Если система RAIL предназначена скорее для упрощения разработки проприетарных протоколов, то система Connect, в основном, предназначена для использования в проекте пользователя прямо «из коробки». Во многих случаях, особенно в проектах, где передача данных между системами не является основной операцией, большинство функций стандартных сетевых протоколов, таких как Zigbee или Thread, остаются незадействованными и применять их нецелесообразно. Вместе с тем желательно иметь простой в использовании и надёжный способ двусторонней передачи сообщений между точками А и Б. Однако, даже такую, казалось бы, элементарную операцию, для надёжной передачи разработать совсем не просто. Вспоминая известную шутку, что на начальном этапе Интернета Вещей (IoT) буква S в аббревиатуре IoT отвечала за Security, сегодня требование безопасности является одним из определяющих при передаче данных. Легковесная система Connect не конкурирует с «тяжёлыми» сетевыми протоколами – она их дополняет, заполняя собой нишу работы с архитектурами PAN (Personal Area Network), показанными ниже.

Топология сети «звезда», показанная слева, используется, например, для отправки данных с нескольких удалённых сенсоров на центральный узел (с подтверждением приёма). В топологии сети на картинке в центре красные узлы сети используются как расширители радиуса связи между удалёнными сенсорами и центральным узлом. Они транслируют данные с сенсоров на центральный узел и также могут передавать свои собственные данные. Наконец, в сети справа все узлы находятся в общем радиусе связи, являются видимыми друг другу, и любые два узла могут обмениваться информацией напрямую. Во всех случаях любые два узла сети могут коммуницировать друг с другом, а стрелочки определяют фиксированные на момент построения сети потоки данных. В таких сетях не требуется реализовывать сложные алгоритмы динамической маршрутизации. В системе Connect имеется возможность определения узлов сети как свой/чужой, возможность добавления/удаления узлов, возможность восстановления сети при перебоях питания и прочие полезные функции. Наконец, уделяется особое внимание минимизации токопотребления периферийных узлов с возможностью их ухода в сон. При этом сообщения, приходящие к ним во время сна, накапливаются в других узлах и передаются первым по запросу при их пробуждении. Вопросы безопасности сети в стеке Connect основаны на алгоритме AES-128, который обеспечивает конфиденциальность передаваемых данных, их целостность, аутентификацию узлов сети, и защиту от несанкционированной повторной передачи данных (replay attack). Подробнее про функции Connect можно прочитать в [2]. Замечательно, что все перечисленные функции уже реализованы в системе, поставляемой как пре-компилированные модули, совершенно прозрачны для кода пользователя, и подключаются к проекту пользователя буквально одной-двумя строчками кода.

Структура пакетов и адресация узлов в сети Connect основана на стандарте IEEE 802.15.4. Поскольку Connect основана на RAIL, можно ограничиться лишь последней. Однако, использование высокоуровневых функций Connect позволяет добиться тех же целей несравненно проще, чем только с RAIL. В частности, в ней уже реализованы следующие функции прямо «из коробки»:
• Процедура добавления узлов к сети
• Подход CSMA/CA для минимизации коллизий и увеличения пропускной способности
• Безопасность (см. выше)
• Компоновка пакетов стандарта 802.15.4 – пользователю остаётся лишь позаботиться о самих передаваемых данных

Таким образом, RAIL следует применять лишь в случаях нехватки flash-памяти в МК для Connect в случае огромного проекта, или для нестандартной реализации какой-либо сетевой функции. Первая причина в большинстве случаев ограничением вряд ли может быть, поскольку стек Connect занимает всего порядка 75К/10К памяти МК типов Flash/RAM из 512К/64К имеющихся в типовых моделях семейств FG1x/FG2x, на которые эта система прежде всего ориентирована.

Учитывая, что в системе Simplicity Studio уже имеется несколько примеров проектов, иллюстрирующих различные функции Connect, и даже имеется руководство [3], настоящая статья не является их кратким переводом. Так, руководство [3] помимо введения в систему основано на старой её версии 2.х и на момент написания посвящено работе только с топологией сети на правой картинке выше (поэтому работа с такой сетью в настоящей статье не рассматривается). Разработка приложений в современной версии 3.х системы концептуально несколько отличается от версии 2.х. Примеры для версии 3.х системы, интегрированные в Simplicity Studio, основаны на CLI (Command Line Interface) интерактиве с пользователем через терминальную программу компьютера для построения сети и её конфигурирования. Я расскажу про автоматическое построение и обслуживание сети с топологией, показанной ниже. Наша сеть будет состоять из центрального узла (синий кружок), расширителя диапазона (красный кружок), и двух периферийных узлов.

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

Аппаратная часть

Разработку кода для центрального узла сети и расширителя радиуса произведём на новых отладочных платах EFR32FG23 Dev Kit (BRD2600A – две платы слева), а периферийных узлов сети на платах WSTK+BRD4257A, знакомых по предыдущей статье серии [1] (две платы справа). Я установил новые (маленькие) платы на деревянные основания, чтобы они не опрокидывались при вертикально установленных антеннах.

В комплект новых плат входят 2 антенны на диапазоны 868 и 915 Мгц, на них также имеется программатор/отладчик основного чипа семейства хG23 второго поколения с радио-модулем на борту. В свою очередь на платах WSTK имеется измеритель токопотребления. В таблице ниже показаны сравнительные характеристики нового семейства xG23 с популярными трансиверами класса EzRadio-PRO и радио-чипов первого поколения из семейства xG13. Подробнее про новые функции семейства xG23 и их особенности рассказано на вебинаре [4].

Отступая немного от главной цели статьи, следует обратить внимание на сниженное более чем в 2 раза токопотребление новой модели в режиме приёма и на новую функцию PSM (Preamble Sense Mode). В этом режиме радио может дольше находиться в режиме сна с минимальным работающим функционалом для приёма преамбулы и автоматическим переключением на приём пакета. Ниже показан пример работы системы, состоящей из приёмника на чипе EFR32FG23 (верхняя диаграмма) и передатчика на чипе EFR32FG14 (нижняя диаграмма). Верхняя диаграмма снята с помощью платы WSTK с установленной на ней радио-платой EFR32xG23B (BRD4210A) любезно предоставленной мне фирмой Silicon Labs, за что им отдельное спасибо.

Приёмник включается с периодом 1 секунда на время 1.7 мс, и при скорости канала 10 ksps его среднее токопотребление в режиме ожидания пакета оказалось около 13 мкА. Минимальная длительность активного состояния приёмника равна времени приёма 16 символов, что при скорости 10 ksps составляет 1.6 мс. Передатчик также включается только на момент передачи пакета и его среднее токопотребление в режиме сна примерно 2.13 мкА.

Начало работы с Connect

Логика разработки приложений в системе Connect очень похожа на таковую в приложениях Bluetooth. В обоих случаях имеются обширные стеки протоколов, время от времени генерирующие события, реакция на которые возложена на код пользователя в зависимости от целей приложения. Обработчики событий в системе Connect называются callbacks. Имеется специальный callback (emberAfTickCallback() в файле app_process.c проекта), вызываемый на каждой итерации основного цикла в main, куда можно поместить соответствующий код пользователя (код в mail.c лучше оставить без изменения). Все системные API следует вызывать только из callbacks, чтобы не создавать конфликты внутреннему планировщику системы, работающему в реальном времени. Имеется возможность буквально одним кликом в конфигураторе добавить Micriµm RTOS. Прежде чем начать сетевое приложение рассмотрим пару примеров, иллюстрирующих логику работы с системой.

Начнём с классического приложения мигания светодиодом. Создадим в Simplicity Studio проект из имеющегося в ней стартового проекта Flex (Connect) – SoC Empty, с которого следует начинать разработку каждого нового Connect проекта. В этом проекте уже заложен минимальный функционал системы, что заметно упрощает разработку. В частности, после инициализации стека протокола управление передаётся функции emberAfInitCallback(), вызываемой только один раз после ресета и расположенной конфигуратором в файле app_init.c проекта. Изначально эта функция пустая. Для мигания светодиодом нам следует создать новое событие, передать его планировщику, и написать его обработчик. Предполагая, что вывод МК, соединённый со светодиодом на плате (PB2) уже сконфигурирован на режим push-pull, получаем следующий код:

 

 

 

 

 

 

 

 

Новое событие, в котором изменяется состояние светодиода, создаётся первой строчкой в emberAfInitCallback() API, а в следующей строке мы передаём его планировщику Connect. Последний, как только будет возможность, вызовет функцию blink_handler(), вторая строчка которой определяет период мигания. Важно отметить, что обработчики событий рассматриваются системой Connect как процессы RTOS, которые вызываются планировщиком в режиме Round Robin и работают непревентивно, т. е., от начала до конца без прерывания другими процессами. Как только событие будет активировано API функцией emberEventControlSetActive(), его обработчик будет вызываться снова и снова до тех пор, пока оно не будет деактивировано API функцией emberEventControlSetInactive(). Проиллюстрируем это вторым примером учебного проекта, где состояние светодиода изменяется каждый раз по нажатию кнопки BTN0 на плате. Здесь опять предполагается, что выводы корпуса под кнопку (PB1) и светодиод (PB2) сконфигурированы соответствующим образом.

 

 

 

 

 

 

 

 

 

 

 

 

 

Первые 2 строчки в функции emberAfInitCallback() конфигурируют вывод корпуса под кнопку на аппаратное прерывание по падающему уровню и определяют его обработчик. Отметим, что аппаратное прерывание асинхронно по отношению к Connect, поэтому в его обработчике следует проинформировать стек Connect о наступлении прерывания путём вызова API функции emberEventControlSetActive(). В свою очередь стек Connect вызовет свой обработчик этого события button_handler() когда придёт время. В данном простейшем случае ничего плохого не случится если изменить состояние светодиода непосредственно из buttonEvent() без информирования об этом стека Connect. Однако, в общем случае, особенно если в обработчике асинхронного прерывания вызываются Connect API, для гарантии правильного функционирования системы рекомендуется информировать стек о каждом внешнем событии. В данном примере наличие первой строчки в функции button_handler() существенно для одноразовой смены состояния светодиода. Без неё его состояние будет изменяться непрерывно с большой скоростью. Полные файлы проектов находятся в приложении.

Программирование центрального узла сети (проект Connect_Sink)

Вернёмся теперь к реализации нашей PAN. Центральный узел сети, помимо всего прочего, занимается конфигурацией всей сети, отсылкой биконов новым узлам, присвоением сетевых адресов новым узлам, и вообще построением сети с нуля. Создадим проект на основе начального проекта Flex (Connect) – SoC Empty. Для этого в конфигураторе проекта следует подключить компоненты, показанные ниже:

Подключение первой компоненты (AES) опционально, если не требуется security (плохая идея). Компонента MAC Queue (также опциональная) обеспечивает установку передаваемых/принимаемых сообщений в очередь на случай, если они будут генерироваться/приниматься быстрее, чем передаваться/обрабатываться. Компонента Parent Control обязательна для центрального узла. Она, в частности, контролирует приём новых узлов в PAN и поддерживает их список. Начало кода в файле app_init.c задаёт ключ шифрования для AES-128 и определяет параметры PAN. Несмотря на имеющуюся процедуру обмена ключами, изначально этот ключ, так же, как и PAN_ID должны быть одинаковыми для всех узлов сети. Сообщения, передаваемые другим узлам сети, будут передаваться при дефолтной мощности передатчика +4 dBm с опциями, установленными в переменной tx_options. Во всех узлах сети следует сконфигурировать одинаковые параметры радиотракта как рассказано в предыдущей статье серии [1] (здесь мы используем дефолтные параметры). Все параметры сети (в случае успешной её конфигурации), включая список адресов всех узлов сети, автоматически сохраняются стеком в энергонезависимой области памяти МК, которая не изменяется при загрузке в чип новой версии программы.

 

 

 

 

 

Поместим следующий код в уже знакомую нам API функцию emberAfInitCallback(). При старте системы следует вызвать API функцию emberNetworkInit(). Если центральный узел не создал PAN ранее, или мы хотим создать новую PAN нажатием кнопки BTN0 на плате во время ресета, следует стереть старые данных всех узлов прежней сети и вызвать API функцию emberFormNetwork(). В противном случае запрещаем присоединение новых узлов к центру обнулением переменной join_period (подробнее об этом ниже). Здесь и далее в коде для простоты и прозрачности опущен вывод статуса и другой служебной информации на терминал компьютера. Полный исходный код проекта, включающий все отладочные сообщения, находится в приложении.

 

 

 

 

 

 

 

 

 

При успешном создании новой PAN статус стека Connect изменится на EMBER_NETWORK_UP и сгенерируется соответствующее событие. В callback этого события следует разрешить другим узлам присоединение к нашей PAN вызовом API функции emberPermitJoining(). Параметр этой функции определяет интервал времени в секундах на присоединение новых узлов к сети. Значение 0 параметра запрещает присоединение, а максимальное его значение 255 разрешает неограниченное по времени присоединение. Стек Connect может контролировать присоединение новых узлов по определённому шаблону в области данных. Хотя с нашем проекте эта опция не используется, для стирания шаблона, и тем самым разрешения присоединения любого узла (с таким-же значением PAN_ID, номера канала, и AES_key) вызываем API функцию emberClearSelectiveJoinPayload().

 

 

 

 

 

 

 

 

 На этом этапе PAN ждёт присоединения к ней новых узлов. Как только это произойдёт, стек генерирует событие и вызовет его callback, представленный ниже. В нашем приложении мы планируем добавить расширитель диапазона к сети, к которому новые узлы сети также будут иметь возможность присоединиться. Поскольку при экспериментах, когда все узлы сети находятся вблизи друг друга на рабочем столе, при введении расширителя присоединение нового узла будет возможно как к центральному узлу, так и к расширителю. Для проверки работы расширителя мы хотим заставить новый узел присоединиться именно к нему, а не к центру. Для этого после присоединения расширителя к центру мы запрещаем присоединение к центру других узлов. Предполагается, что на этом этапе построения сети все её узлы помимо расширителя обесточены, так что расширитель будет единственным дочерним узлом центра.

 

 

 

Целью нашего приложения является просто приём данных от нескольких узлов и выдачи принятых данных в окно терминальной программы на компьютере. После приёма сообщения, в соответствии с установками нашей PAN, передающему узлу будет послано подтверждение. Здесь важно отметить, что подтверждение центра будет послано только расширителю, а не узлу-отправителю. Кстати, узел-отправитель получит подтверждение от расширителя. Такие подтверждения посылаются стеком автоматически без участия пользователя в соответствии с параметрами сети выше. Если желательна отсылка подтверждения из центра узлу-отправителю, это должно быть реализовано отдельно в коде пользователя. При приёме пакета стеком Connect вызывается следующий callback, где мы просто распечатываем содержимое пакета для контроля.

 

 

 

 

 

 

Программирование расширителя диапазона (проект Connect_Extender)

Этот проект также создан на основе Flex (Connect) – SoC Empty. Его инициализация в функции emberAfInitCallback() почти идентична центральному узлу. Отличие состоит в том, что при создании новой сети для подключения расширителя к PAN, вместо API функции emberFormNetwork() следует использовать функцию emberJoinNetwork(), первый параметр которой определяет тип узла. Однако, если вызвать эту функцию из emberAfInitCallback(), то работать не будет. Решением является вызов этой функции из отдельного события. В нашем случае для управления этим событием в коде служит переменная join_control и обработчик join_handler() в файле app_process.c проекта. Создание этого события и передача его планировщику происходит уже известными нам API по учебным проектам выше (emberAfAllocateEvent() и emberEventControlSetActive()).

 

 

 

 

 

 

 

 

 

 

Как и в проекте центрального узла, при нажатой кнопке BTN0 при ресете стираются все данные предыдущей сети. Дополнительно, если ресет МК произведён при нажатой кнопке BTN1 на плате, то этим запрещаются все присоединения узлов к расширителю, которые в противном случае разрешены на неограниченное время.

 

 

 

 

 

 

 

 

 

Контролировать присоединения узлов к расширителю следует только после успешного присоединения его самого к PAN. В этом случае статус стека Connect изменится на EMBER_NETWORK_UP и вызовется следующий callback, такой же, как в предыдущем проекте. В случае ошибки присоединения, попытку следует повторить. При этом статус стека будет EMBER_NETWORK_DOWN или одним из показанных ниже, и попытка присоединения будет повторяться вплоть до достижения успеха неограниченно с периодом JOIN_RETRY_INTERVAL (у нас 1 сек). Кстати, из-за этих попыток становится понятным почему функцию emberJoinNetwork() нельзя вызвать из emberAfInitCallback().

 

 

 

 

 

 

 

 

 

 

После успешного присоединения к центру расширитель готов к работе. Собственно, сама пересылка через него сообщений обеспечивается кодом стека, скрытым от пользователя. На экранах терминалов центрального узла и расширителя при работе появятся следующие сообщения (повторяю, что для прозрачности изложения выдача этих сообщений удалена из фрагментов кода выше, но присутствует в полном коде проекта в приложении). В соответствии со стандартом IEEE 802.15.4, при обмене данными внутри PAN вместо уникальных 64-битных ID узлов, которые заложены в каждый радио-чип на фабрике, используются их внутренние 16-битные адреса в сети. При этом центральный узел (Sink) получает адрес 0х0000 и далее он назначает адреса остальным узлам по мере их присоединения. Не удивительно, что наш расширитель получил адрес 0х0001. Минимальное значение адресов устанавливается конфигуратором проекта.

 

Важно отметить, что к расширителю диапазона нельзя будет подключить другой расширитель. Таким образом, радиус графа сети Connect относительно центрального узла не превосходит 2.

Программирование периферийного узла сети (проект Connect_Node)

В плане подключения к сети программа периферийного узла практически идентична таковой для расширителя. Единственное отличие в функции join_handler() – это тип узла, передаваемый в качестве параметра функции emberJoinNetwork() и определяемый значением SLEEP. От него в значительной мере зависит токопотребление узла в режиме неактивности (см. график ниже).

 

 

 

 

 

 

 

 

Если присоединение прошло успешно и статус стека изменился на EMBER_NETWORK_UP, то можно начинать периодическую отсылку данных центральному узлу. Это реализовано в функции report_handler(), вызов которой контролируется переменной report_control. Однако, поскольку дефолтно в проекте присутствует приём данных с USART, для которого требуется тактовая частота системы, погрузить её в глубокий сон (EM2) на период неактивности не удастся и возможен лишь «неглубокий» режим сна EM1. Для успешного погружения в режим EM2 можно, например, добавить пару команд компоненте Power_Manager как показано ниже.

 

 

 

 

 

 

 

 

 

Альтернативно, вместо этих команд можно изменить значение соответствующего дефайна с 1 на 0 в строке 78 файла sl_iostream_usart_vcom_config.h. В любом случае приём данных с USART во время сна при этом будет невозможен, но нам это и не нужно. Кстати, если это нужно, следует вместо компоненты IO_Stream_USART использовать компоненту IO_Stream_LEUART на скорости не более 9600 бод.

 

Каждый раз, когда стек Connect готов отправить всю систему в глубокий сон он запрашивает на это разрешение у приложения путём вызова следующего callback. Если возвращаемое значение этой функции будет false, то система погрузится лишь в режим неглубокого сна EM1.

 

 

 

 

Разница в среднем токопотреблении системы при погружении её в режимы сна EM1 и EM2 существенна. В первом случае она будет порядка 3 мА, а во втором - пара десятков микроампер, как следует из приведённой осциллограммы. Она снята с реального устройства при периоде отсылки сообщений 5 сек, мощности передатчика +4 dBm, и скорости передачи данных 100 kbps. Частые пики на графике возникают из-за работы встроенного DC-DC конвертера. Отмечу, что описанный выше режим PSM в модели приёмника EFR32FG14 не предусмотрен. Он имеется только в моделях ERF32 второго поколения.

Но вернёмся к программной части. Передача данных реализована в функции report_handler(). Все данные предназначенные для передачи следует поместить в буфер с последующей передачей его в API функцию emberMessageSend(). В нашем случае данные представляют собой массив из 8 байт, генерируемых случайным образом. Период отправки данных определяется значением REPORT_PERIOD.

 

 

 

 

 

 

По завершении передачи и получении подтверждения от принимающего узла (если они затребованы) или истечения таймаута ожидания подтверждения стеком Connect вызывается следующий callback. В нашем случае он не оставит следа на терминале. Однако, если выключить расширитель, то подтверждение приёма от него не будет получено и будет показано сообщение об ошибке.

 

 

 

 

 

Соответственно, при приёме узлом сообщения стек Connect вызывает следующий callback, где мы просто выдаём принятое сообщение на терминал. Как мы уже знаем, этот callback, так же, как и предыдущий, имеется и в программе центрального узла для контроля принятых пакетов.

 

 

 

 

 

 

 

Таким образом, при работе системы на терминалах периферийного (слева) и центрального (справа) узлов будут показаны передаваемые и принимаемые данные. Последний столбец на левой картинке — это статус переданного пакета (0х00 если передача прошла успешно и получено подтверждение). Как мы уже знаем, адрес центрального узла PAN равен 0х0000, а рассматриваемый периферийный узел получил номер 0х0002. Как видно, получены те же данные, что и переданы. Аналогично получим сообщения и от второго периферийного узла с PAN адресом 0х0003.

 

В нашем приложении центральный узел не отсылает данные (спящим) периферийным узлам. Если это требуется, то расширителю следует накапливать данные, поступающие соединённому с ним периферийному узлу во время его неактивности, а периферийному – запрашивать данные при пробуждении. Это можно организовать включением в проект компоненты Poll, находящейся в категории Connect конфигуратора ресурсов. Альтернативно, имеется возможность организации почтовых ящиков на обоих сторонах канала передачи данных. Это реализуется компонентами категории Mailbox. Однако, использование перечисленных возможностей, также, как и описание ряда других функций системы, выходит за рамки этой ознакомительной статьи.

Литература

1. Радиомодули для беспроводной передачи данных. Часть 4.
2. UG103.12: Silicon Labs Connect Fundamentals
3. Connect Tutorial Series
4. Proprietary Sub-GHz Leaping RF Performance and Improving Low Power Performance with FG23
5. Connect Stack v3.x documentation

 


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


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