Bluetooth Low Energy: подробный гайд для начинающих. Соединения и сервисы
Это третья часть перевода книги Мохаммада Афане “Intro to Bluetooth Low Energy”. Сегодня мы подробнее рассмотрим процесс подключения устройств и поговорим о сервисах.
Благодаря сервисам происходит обмен как стандартными данными (уровень заряда батареи через Battery Service, текущее время устройства через Current Time Service и т.д.), так и кастомными, при помощи сервисов, созданных разработчиком устройства для удовлетворения специфических нужд. Например, для Atmotube Pro мы сделали два сервиса, в которые сгруппировали несколько характеристик для синхронизации истории, передачи данных о концентрации пыли и летучих органических соединений.
Эта часть получилась очень большой, извините. Я старался сократить ее, или разбить на несколько, однако это нарушило бы целостность восприятия, пожалуй, самой важной темы книги. В следующей части мы поговорим о нововведениях Bluetooth 5 и рассмотрим методы обеспечения безопасности соединения и пользовательских данных в BLE.
4. Соединения
Для того, чтобы два BLE устройства установили соединение, необходимо выполнить следующие шаги:
- Периферийное устройство должно начать процесс адвертайзинга и не останавливать его до момента установки соединения.
- Центральное устройство должно сканировать радиоэфир в поисках пакетов адвертайзинга.
- Если центральное устройство прослушивает канал адвертайзинга в момент, когда периферийное устройство посылает по нему данные, то оно обнаруживает периферийное устройство.
- Затем центральное устройство посылает пакет CONNECT_IND(также известный какзапрос на соединение).
- Периферийное устройство всегда прослушивает текущий канал адвертайзинга после отправки пакета. Это позволяет ему получить запрос на соединение от центрального устройства, что запускает процесс установки соединения между двумя устройствами.
После того как будут выполнены эти шаги, соединение будет считаться созданным, но еще не установленным. Соединение будет считаться установленным, после того как устройство получит пакет данных от своего партнера по соединению. После установления соединения центральное устройство возьмет на себя роль ведущего, а периферийное, в свою очередь, ведомого. Ведущее устройство будет отвечать за управление соединением, контроль параметров соединения и контроль временных интервалов между различными событиями.
4.1 События подключения
Во время событий подключения ведущий и ведомый отправляют друг другу данные до тех пор, пока не останется данных, требующих передачи. Есть несколько фактов о соединениях, которые очень важно знать:
- События подключения происходят периодически до тех пор, пока соединение не будет закрыто или потеряно.
- Событие подключения состоит как минимум из одного пакета посланного ведущим устройством.
- Ведомое устройство всегда отвечает отправкой пакета, если оно получает пакет от ведущего.
- Если ведущий не получает ответный пакет от ведомого, он закрывает подключение — он возобновит посылку пакетов во время следующего события подключения.
- Подключение может быть закрыто как ведущим, так и ведомым.
- События подключения разнесены во времени на период интервала соединения.
4.2 Параметры соединения
Параметры, определяющие соединение:
- Интервал соединения Интервал соединения может принимать любое из значений между 7.5 мс и 4.0 секундами с шагом в 1.25 мс. Он задается центральным устройством в пакете запроса соединения. Центральное устройство может принять во внимание Предпочитаемые Параметры Соединения Периферийного Устройства ( PPCP ). Центральное устройство вправе принять их, модифицировать или отклонить.
- Задержка ведомого (Slave Latency) Параметр задержки ведомого позволяет периферийному устройству оставлять без ответа определенное количество событий соединения и не слушать центральное устройство во время этих событий без инвалидации соединения. Это позволяет периферийному устройству переходить в режим энергосбережения на более длительный срок. Задержка ведомого отображает количество событий соединения, которое периферийное устройство может пропустить. Например, если установлена задержка три, то периферийное устройство может пропустить три последовательных события соединения, но затем оно должно будет выйти из режима энергосбережения, прослушать центральное устройство и ответить на следующее событие соединения.
- Таймаут наблюдения (Supervision Timeout) Таймаут наблюдения используется для определения потери соединения. Он определяется как максимальное время между двумя полученными пакетами данных, прежде чем соединение считается потерянным. Его значение может задаваться в диапазоне между 100 мс и 32 секундами с шагом 10 мс. Другое условие выглядит следующим образом: Таймаут наблюдения > (1 + задержка ведомого) * интервал соединения * 2 Существует исключение, для которого таймаут наблюдения не применяется — в момент, когда соединение создано, но еще не установлено. В этом случае ведущее устройство примет решение о потере соединения, если не получит пакета от ведомого в течении 6 интервалов соединения.
- Расширение длины данных ( DLE ) Это опция, которая позволяет пакетам данных содержать большее количество полезной нагрузки (до 251 байта, в случае, когда эта настройка выключена, полезная нагрузка составляет 27 байт). Эта возможность введена в версии 4.2 спецификации Bluetooth.
- Максимальная единица передачи ( MTU ) Понятие максимальной единицы передачи используется в компьютерных сетях и определяет максимальный размер данных, которые можно передать по определенному протоколу. Максимальная единица передачи атрибута (в спецификации определена как ATT_MTU) это наибольший размер полезной нагрузки атрибута, который можно передать между клиентом и сервером. Эффективный размер ATT_MTU определяется наименьшим значением максимальных ATT_MTU поддерживаемых ведущим и ведомым. Например, если ведущее устройство поддерживает ATT_MTU 100 байт и ведомое устройство сообщает, что оно поддерживает ATT_MTU 150 байт, то ведущий решает, что для этого соединения будет использоваться ATT_MTU 100 байт. Примечание: Для достижения максимальной пропускной способности убедитесь, что включено расширение длины передаваемых данных (в случае, если вы используете Bluetooth 4.2 или новее). Это поможет снизить количество избыточно передаваемых служебных данных, таких как заголовки пакетов, за счет уменьшения числа пакетов.
4.3 Переключение между каналами
Как мы упоминали ранее в начале этой главы, существуют 37 радиоканалов, используемых для передачи пакетов данных в течении соединения. В то же время, не все 37 каналов необходимы для соединения. Используемые каналы определяются картой каналов, которая включена в пакет запроса соединения, отправляемый центральным устройством периферийному для установки соединения. Для каждого события соединения пакеты данных будут переданы по другому каналу в карте каналов.
Последовательность каналов, используемая для каждого события соединения определяется картой каналов и шагом прыжка (hop increment). Шаг прыжка, также как и карта каналов, включен в пакет запроса соединения. Комбинация карты каналов и шага прыжка определяет, какой канал будет использован для каждого интервала соединения.
Существует два алгоритма выбора каналов в BLE. Рассмотрение подробностей их работы лежит вне поля зрения этой книги. Для того, чтобы узнать больше об этих алгоритмах и принципах их работы, обратитесь к спецификации Bluetooth (version 5.0 | Vol 6, Part B, Section 4.5.8.2).
4.4 Белый список и фильтрация устройств
BLE поддерживает фильтрацию устройств для процедур, связанных с состояниями адвертайзинга, сканирования и стадией инициации (при установлении соединения).
Белый список – это список адресов и типов адресов определенных устройств. Он используется для определения того, в каких устройствах заинтересовано конкретное устройство. Запись для анонимного типа адреса устройства позволяет сопоставить все широковещательные пакеты, отправленные без адреса.
Фильтрация устройств происходит на канальном уровне контроллера (нижний уровень протокола Bluetooth), что позволяет сохранить время и не производить лишнюю работу на уровне хоста (на верхних уровнях протокола). Однако именно хост отвечает на конфигурацию белого списка.
Ниже приведен список различных правил фильтрации для каждого из состояний:
- Правило фильтрации для состоянии адвертайзинга (периферийное устройство) Эта политика фильтрации определяет, как периферийное устройство будет обрабатывать запросы сканирования и запросы на подключение. Она может быть сконфигурирована следующим образом:
- Обрабатываются запросы сканирования и запросы на подключение только от устройств из белого списка.
- Обрабатываются запросы от всех устройств (фильтрация не используется).
- Обрабатываются запросы сканирования только от устройств из белого списка, обрабатываются запросы на подключение от всех устройств.
- Обрабатываются запросы на подключение только от устройств из белого списка, обрабатываются запросы сканирования от всех устройств.
- Правило фильтрации для состояния сканирования (центральное устройство) Это правило фильтрации определяет, как сканирующее устройство будет обрабатывать пакеты адвертайзинга. Может быть сконфигурировано следующим образом:
- Обрабатываются пакеты адвертайзинга от всех устройств (белый список не используется).
- Обрабатываются пакеты адвертайзинга только от устройств, включенных в белый список.
- Правило фильтрации для состояния инициации (центральное устройство) Это правило определяет, как устройство, инициирующее соединение, будет обрабатывать пакеты адвертайзинга. Может быть сконфигурировано следующим образом:
- Обрабатывать пакеты адвертайзинга и инициировать соединение только с устройствами, включенными в белый список.
- Обрабатывать пакеты адвертайзинга и инициировать соединение только с устройствами, определенными хостом.
Обратите внимание, что это не опция для обработки пакетов и подключения к подключаемому периферийному устройству, которого нет в белом списке.
5. Сервисы и характеристики
Прежде чем начать рассказ о сервисах и характеристиках, мы должны рассмотреть два очень важных понятия: Общий профиль атрибутов ( GATT ) и Протокол атрибутов ( ATT ).
Для того, чтобы понять, что из себя представляет общий профиль атрибутов, необходимо сперва разобраться, что из себя представляет его нижний слой — протокол атрибутов. GATT вступает в игру только после того, как будет установлено соединение между двумя устройствами.
если вы считаете GAP , GATT и ATT набором слишком похожих акронимов… не проклинайте меня… Я просто рассказчик! Тем не менее важно разделять их!
5.1 Протокол атрибутов (ATT)
АТТ определяет, в каком виде сервер представит свои данные клиенту и как эти данные будут структурированы. Существует две роли, связанные с АТТ:
- Сервер: Это устройство, которое предоставляет данные, которые оно содержит, или которыми управляет и, в некоторых случаях, другие аспекты поведения сервера, которые могут контролировать другие устройства. Это устройство, которое получает входящие команды от связанного устройства и отправляет ответы, уведомления и индикации. Например, беспроводной термометр будет вести себя как сервер, когда он будет предоставлять температуру окружающей среды, единицу измерений, свой уровень заряда и, возможно, временные интервалы, с которыми термометр производит измерения и сохраняет их результаты. Он также может уведомлять клиент (объясним позже) об изменениях температуры для того, чтобы клиент не опрашивал его непрерывно в ожидании готовых к отправке данных.
- Клиент: Это устройство, которое взаимодействует с сервером с целью считать предоставляемые сервером данные и/или для того, чтобы контролировать его поведение. Это устройство, которое посылает команды и запросы и получает входящие уведомления и индикации. В предыдущем примере, мобильный телефон, который подключался к беспроводному термометру и считывал его показания температуры, действовал в роли клиента. Данные, предоставляемые сервером, сгруппированы в атрибуты. Атрибут это общий термин для любых типов данных, предоставляемых сервером, он определяет структуру этих данных. Например, сервисы и характеристики (будут описаны позднее) являются атрибутами. Ниже состав атрибута:
- Тип атрибута (универсальный уникальный идентификатор, UUID) Это 16-битное (в случае стандартных атрибутов Bluetooth SIG) или 128-битное число (в случае атрибутов, определенных разработчиком устройства, vendor-specific UUID). Например, UUID для одобренного консорциумом атрибута значения температуры 0x2A1C. Одобренные консорциумом типы атрибутов имеют один общий (за исключением 16 бит) специальный 128-битный UUID: 0000XXXX-0000-1000-8000-00805F9B34FB 16-битный UUID будет подставлен вместо символов ХХХХ в базовом UUID. Собственный UUID может быть любым 128-битным числом, не совпадающим ни с одним из одобренных Bluetooth-SIG базовых UUID. Например, разработчик может создать свой собственный UUID для показаний температуры, такой как: F5A1287E-227D-4C9E-AD2C-11D0FD6ED640 Одно из преимуществ использования стандартных UUID состоит в уменьшении размера пакета, так как UUID может быть передан в виде 16-битного числа, вместо передачи полного 128-битного числа.
- Дескриптор атрибута Это 16-битное число, которое сервер присваивает каждому из своих атрибутов. Это число используется клиентом как ссылка на конкретный атрибут, и сервер гарантирует, что эта ссылка будет уникальной для атрибута, которому она присвоена, в течении всего времени существования соединения между устройствами. Дескриптор может иметь любое значение в диапазоне 0x0001-0xFFFF, значение 0х0000 зарезервировано.
- Права атрибута Права определяют, может ли атрибут быть прочитан или записан, может ли он посылать уведомления или индикации, и какие уровни доступа требуются для каждой из этих операций. Эти права не определяются протоколом атрибутов (АТТ) и не могут быть прочитаны через него, они определяются на верхнем уровне (GATT или уровне приложения).
5.2 Общий профиль атрибутов (GATT)
Теперь, когда мы рассмотрели концепцию атрибутов, познакомимся с другими тремя важными понятиями в BLE, с которыми вам придется постоянно сталкиваться.
Эти понятия введены для того, чтобы обеспечить возможность иерархического представления данных, предоставляемых сервером. Сервисы и характеристики это атрибуты, служащие для определенной цели. Характеристики это низкоуровневые атрибуты в базе данных атрибутов. Сервисы служат для группировки характеристик по функциональному признаку. Профили немного отличаются и не обнаруживаются на сервере — мы расскажем о них позже в этой главе.
GATT определяет формат сервисов и их характеристик, а также процедуры, используемые для взаимодействия с этими атрибутами, такие как обнаружение сервисов, чтение и запись характеристик, уведомления и индикации.
GATT выполняет ту же роль, что и протокол атрибутов (АТТ). Роли устанавливаются не для устройств, они определяются во время транзакций (такие как запрос ⟷ ответ, индикация ⟷ подтверждение, уведомление). Таким образом, устройство может действовать как сервер, предоставляющий данные клиентам, и в то же время быть в роли клиента, получая данные, подготовленные другими серверами одновременно.
5.3 Сервисы и характеристики
5.3.1 Сервисы
Сервисы это группа из одного или большего числа атрибутов, некоторые из которых являются характеристиками. Он предназначен для группировки связанных атрибутов, удовлетворяющих специфической функциональности сервера. Например, одобренный SIG сервис батареи содержит одну характеристику под названием “уровень заряда”.
Сервисы также содержат другие атрибуты, которые помогают структурировать данные сервиса, такие как объявления сервера, объявления характеристик и другие.
Вот что из себя представляют сервисы:
На рисунке мы видим различные атрибуты, составляющие сервис:
- Один или несколько включенных сервисов
- Одна или несколько характеристик
- Свойства характеристики
- Ее значение
- Дескрипторы характеристики
Включение сервисов позволяет сервису ссылаться на другие сервисы как на включенные в него. Существует два типа сервисов:
- Первичный сервис: предоставляет основную функцию устройства или одну из них.
- Вторичный сервис: предоставляет дополнительную функциональность устройства и включен в как минимум один первичный сервис на устройстве (вторичные сервисы очень редко применяются, и еще реже возникает необходимость в них – они не будут более подробно рассматриваться в этой книге).
5.3.2 Характеристики
Характеристика всегда является частью сервиса и предоставляет часть тех данных, которые сервер хочет предоставить клиенту. Например, характеристика уровня заряда батареи показывает оставшийся уровень заряда батареи в устройстве, который может быть прочитан клиентом. Характеристика содержит набор атрибутов, которые облегчают работу с содержащимися в характеристике данными:
- Свойства: представлены набором бит, определяющих то, каким образом значение характеристики может использоваться. Пример свойств: чтение, запись, запись без ответа, уведомление, индикация.
- Дескрипторы: используются для хранения информации, связанной со значением характеристики. Примеры использования: расширенные свойства, пользовательское описание, поля, используемые для подписки на уведомления и индикации, поля, описывающие представление характеристики, такие как формат или единица измерения.
Понимание этих концепций важно, однако вы, как разработчик приложения, будете в основном взаимодействовать с API, предоставленным операционной системой или SDK чипсета, которые абстрагируют вас от многих этих понятий.
Например, вы можете иметь API для включения уведомлений для некоторых характеристик, который вы просто вызываете и вам нет необходимости знать, что результатом этого вызова будет запись значения 0x0001 в дескриптор конфигурации характеристик клиента ( CCCD ) на сервере.
Важно помнить, что, хотя нет никаких запретов или ограничений на количество характеристик, содержащихся в сервисе, сервисы предназначены для группировки связанных характеристик, которые определяют конкретные функции в устройстве.
Например, технически возможно создать сервис под названием сервис влажности, который будет включать в себя характеристику влажности и характеристику температуры. Но гораздо более логичным решением будет создать два отдельных сервиса для каждой несвязанной функции (отображение текущей температуры и отображение текущей влажности).
Стоит отметить, что Bluetooth SIG приняла довольно-таки большое количество сервисов и характеристик, которые удовлетворяют большей части распространенных сценариев использования. Для этих одобренных сервисов существуют спецификации, помогающие разработчикам реализовать их вместе с обеспечением соответствия и взаимодействия с каждым сервисом.
Если устройство заявляет о совместимости с сервисом, оно должно полностью соответствовать спецификации сервиса, опубликованной Bluetooth SIG. Это важно, если вы хотите разработать устройство, которое будет гарантированно подключаться к устройствам, изготовленными другим производителем. Сервисы Bluetooth, принятые SIG, делают соединение «предварительно согласованным» между устройствами различных производителей.
Вы можете найти список одобренных сервисов здесь, и их спецификации здесь. Одобренные характеристики находятся по этому адресу.
5.4 Профили
Профили по своему определению гораздо шире сервисов. Они занимаются определением поведения как клиента, так и сервиса, когда дело касается сервисов, характеристик и даже соединений и требований безопасности. Сервисы и их спецификации, с другой стороны, касаются реализации этих сервисов и характеристик только на стороне сервера.
Также как и в случае с сервисами, существуют одобренные SIG профили с опубликованными спецификациями к ним. В спецификации профиля вы можете найти следующее:
- Определение ролей и взаимоотношений между GATT сервера и клиента.
- Требуемые сервисы.
- Требования сервисов.
- Как используются требуемые сервисы и характеристики.
- Детали требований к процессу установления соединения, включая параметры адвертайзинга и соединения.
- Соображения безопасности.
Ниже в качестве примера приведена диаграмма, взятая из спецификации на профиль кровяного давления. Она показывает отношения между ролями (сервер и клиент), сервисами и характеристиками внутри профиля.
Роли представлены в виде желтых прямоугольников, сервисы – в виде оранжевых прямоугольников. Вы можете найти список профилей, одобренных SIG, здесь.
5.5 Пример GATT
Давайте посмотрим на пример использования GATT. В этом примере мы будем использовать файл GATT.xml, используемый в Silicon Labs Bluetooth Low Energy development framework (BGLib).
В этом XML-файле вы можете заметить следующее:
- Определены два сервиса:
- Сервис общего профиля доступа (GAP) с UUID: 0x1800 (одобрен SIG).
- Сервис замены кабеля с UUID: 0bd51666-e7cb-469b-8e4d-2742f1ba77cc (Собственный сервис, определенный разработчиком приложения или производителем чипсета. Обычно так называют реализацию UART средствами Bluetooth. Например, у Nordic Semi эту роль выполняет Nordic UART Service, а у Dialog Semiconductor — Dialog Debug Service).
- Имя с UUID 0x2A00 и значением: Bluegiga CR Demo.
- Окружение с UUID 0x2A01 и значением 0x4142.
Описание значений окружения приведены здесь.
Примечание: обычно создание и включение этого сервиса является задачей вендора чипсета и, обычно, разработчику предоставляется API для простой установки значений имени и окружения.
- Характеристика данных имеет UUID: e7add780-b042-4876-aae1-112855353cc1
- Включены свойства, разрешающие запись в характеристику и индикацию.
5.6 Операции с атрибутами
Существует шесть различных типов операций, которые могут производиться над атрибутами:
- Команды: посылаются клиентом серверу и не требуют ответов.
- Запросы: посылаются клиентом серверу и требуют ответов. Существуют два типа запросов:
- Запросы на поиск информации
- Запросы на чтение
5.6.1 Управление потоком и последовательность операций с атрибутами
Запросы последовательны по своей природе и требуют ответа от сервера перед отправкой нового запроса. Индикации имеют такое же требование: новая индикация не может быть отправлена до тех пор, пока не получено подтверждение о приеме предыдущей индикации.
Запросы и индикации являются взаимоисключающими с точки зрения требований к последовательности. То есть индикация может быть отправлена сервером до того как он ответит на запрос, который получил ранее.
Команды и уведомления отличаются, и не требуют управления потоком сообщений — они могут быть посланы в любое время. По этой причине, а также потому что сервер или клиент могут быть не в состоянии обработать эти пакеты, они считаются ненадежными. Запросы и индикации должны использоваться в случаях, когда надежность стоит во главе угла.
5.6.2 Чтение атрибутов
Чтение – это запрос по своей природе, так как оно требует ответа. Существует несколько типов запросов на чтение, но наиболее важны следующие два:
- Запрос на чтение: простой запрос, ссылающийся на атрибут, который будет прочитан его дескриптором.
- Запрос на чтение части данных: похож на запрос на чтение, но содержит смещение, указывающее, с какого участка чтение должно начаться, возвращая часть значения. Этот тип чтения используется для чтения только части значения характеристики.
5.6.3 Запись атрибутов
Запись может быть командой или запросом. Ниже представлены наиболее популярные типы записи:
- Запрос на запись: как следует из названия, требует ответа от сервера, подтверждающего успешное получение данных.
- Команда на запись: не требует ответа от сервера.
- Упорядоченная запись (атомарное поведение операции): классифицируется как запрос и требует подтверждения от сервера. Они используются в случаях, когда требуется записать большое количество данных, не умещающееся в одно сообщение. Вместо того, чтобы записывать части сообщения и давать возможность кому-то прочитать некорректное неполное значение, применяются два типа запросов на запись, для того, чтобы иметь уверенность в безопасном выполнении операции:
- Один или несколько запросов на подготовку к записи: каждый включает в себя смещение, с которым посылаемое значение должно быть записано в значение атрибута. Посылаемые значения также часто называют подготовленными значениями, и они хранятся в буфере на стороне сервера, еще не записанные в атрибут. Эта операция требует ответа со стороны сервера.
- Один запрос на запись: используется для запроса сервера о выполнении или отмены записи принятых значений в атрибут. Требует ответа от сервера для того, чтобы клиент был уверен в том, что атрибут содержит целое и верное значение, посланное серверу.
5.6.4 Запрос на обмен MTU
Сервер и клиент согласуют общее значение MTU, использующееся для передачи данных в обоих направлениях. Запрос инициируется клиентом и может быть послан только один раз во время жизни соединения (согласно спецификации Bluetooth, version 5.0, Vol 3, Part F, Section 3.4.2.1).
Сервер отвечает особым пакетом, содержащим поддерживаемый им размер ATT_MTU. Итоговое значение будет выбрано равным меньшему из переданных между сервером и клиентом ATT_MTU.
Важно помнить, что различные версии BLE имеют разные поддерживаемые максимальные значения ATT_MTU.
5.7 Создание своего GATT
5.7.1 Общие рекомендации
Несмотря на то, что GATT это удивительно гибкий фреймворк, существуют несколько общих рекомендаций, которым нужно следовать при создании сервисов и характеристик для них:
- Убедитесь, что внедрены обязательные сервисы и их характеристики:
- Сервис общего профиля доступа (GAP)
- Заданы характеристики имени и окружения.
- Вы уменьшаете размер передаваемых пакетов, содержащих UUID сервисов и характеристик (включая пакеты адвертайзинга) ускоряете процедуру обнаружения и другие процедуры за счет использования 16-битных UUID вместо 128-битных.
- Производители модулей и чипсетов обычно предоставляют примеры реализации этих сервисов в своих примерах и SDK — уменьшая нужное вам время на разработку.
- Большее число сторонних устройств и приложений сможет взаимодействовать с вашим, тем самым расширяя число возможных пользовательских сценариев.
В следующей главе мы рассмотрим практический пример, показывающий как разработать GATT для системы домашней автоматизации.
6. Урок создания GATT
Разработка GATT для вашего BLE устройства может быть очень сложной задачей. Для того, чтобы упростить ее, давайте пройдем полный урок создания GATT для простой системы домашней автоматизации.
Система домашней автоматизации это абстрактное нечто, но она поможет вам лучше понять шаги создания GATT для реального приложения, а не для какой-то обобщенной системы. Ниже показана схема с элементами системы “умного дома” и связями между ними.
Система состоит из множества устройств. Некоторые из них куплены в магазине, и мы не можем как-то изменить их прошивку, остальные подлежат модификации в соответствии с нашими нуждами.
6.1 Общее описание системы
Давайте опишем основные пользовательские сценарии для системы:
- Владелец дома может использовать пульт дистанционного управления для включения и выключения светильника.
- Владелец дома может наблюдать за изменениями показаний температуры и влажности на сенсоре окружающей среды.
- Владелец дома получает уведомления об уровне заряда батареи всех элементов системы.
6.2 Элементы системы
Теперь давайте рассмотрим составляющие нашей системы.
- Шлюз Шлюз будет выполнять роль центрального BLE устройства во время обмена данными со всеми устройствами кроме смартфона, где он будет выполнять роль периферийного устройства. Мы можем контролировать это устройство и спроектируем GATT для него. Команды для управления светильником будут поступать с пульта управления на шлюз, и с него на сам светильник.
- Пульт управления
Пульт управления это устройство, которое выполняет только периферийную роль, для него мы также спроектируем GATT. - Сенсор окружающей среды
Это стандартное устройство, GATT которого мы не можем изменить, так что область нашего интереса будет ограничена чтением показаний с него (данные о текущей температуре и влажности). - Светильник Это еще одно стандартное устройство, которое мы не можем каким-либо образом изменить.
- Смартфон Очередное стандартное устройство, мы будем использовать его для контроля системы.
6.3 Разработка GATT
Теперь мы пройдем пошагово весь процесс создания GATT, удовлетворяющего нашим требованиям.
6.3.1 Шаг 1: Документирование различных пользовательских сценариев
Несмотря на то, что GATT обычно больше сфокусирован на периферийной роли (так как обычно в роли сервера, предоставляющего данные, выступает периферийное устройство), центральное устройство также может выполнять роль сервера в особых случаях. Также, так как мы разрабатываем устройство, выполняющее обе роли (центральную и периферийную), это поможет нам понять, что должно произойти с каждой стороны, так как это повлияет на некоторые аспекты разработки системы и GATT.
Шлюз выполняет роль как центрального, так и периферийного устройства. Каждая из этих ролей используется для обеспечения связи с различными устройствами, входящими в состав системы. Основная задача шлюза как центрального устройства — считывать показания с множества периферийных устройств. Затем он, действуя в роли периферийного устройства, предоставляет собранные и обработанные данные другому центральному устройству (смартфону), который может передать эти данные на облачный сервер.
Рассмотрим пользовательские сценарии с точки зрения шлюза, для центральной и периферийной роли.
Периферийная роль
- Пульт управления уведомляет шлюз, когда кнопки на нем нажимаются, для включения и выключения светильника.
- Данные должны передаваться на облачный сервер через шлюз. Они представлены центральному устройству (например смартфону, имеющему доступ в интернет) в виде GATT-сервера в периферийной роли. Ниже перечень этих данных:
- Показания температуры датчика окружающей среды
- Показания влажности датчика окружающей среды
- Статус светильника (включен или выключен)
- Уровни заряда батареи пульта, светильника и датчика окружающей среды.
Центральная роль
Шлюз должен считывать некоторые данные, представленные устройствами, составляющими систему, и получать уведомления о других данных с этих устройств.
6.3.1.2 Пульт управления
Пульт управления выполняет одну функцию: управляет светильником. Он выполняет исключительно периферийную роль и должен предоставлять следующую информацию:
- При нажатии кнопки “ВКЛ”: уведомить шлюз о том, что была нажата эта кнопка.
- При нажатии кнопки “ВЫКЛ”: также уведомить шлюз о нажатии этой кнопки.
- Уровень заряда батареи: шлюз должен иметь возможность читать данные о заряде батареи пульта и получать уведомления о его изменении.
6.3.2 Шаг 2: Настройка сервисов, характеристик и прав доступа
На этом шаге мы сгруппируем характеристики в группы (сервисы), объединяющие характеристики со схожим функционалом и зададим разрешения для каждой из них.
У нас есть один GATT-сервер для шлюза в периферийной роли. Посмотрев еще раз на данные, которые нам нужно передавать, мы можем назначить им характеристики и сгруппировать их в следующие сервисы:
- Сервис датчика окружающей среды:
- Характеристика показаний температуры датчика окружающей среды: “Температура” Права: Чтение, уведомление.
- Текущее состояние светильника “Статус” Права: Чтение, уведомление.
Сервис пульта дистанционного управления:
- Характеристика остаточного заряда аккумулятора: “Уровень заряда” Права: Чтение, уведомление.
Помимо этих сервисов необходимо реализовать обязательный (согласно спецификации Bluetooth) сервис:
- GAP сервис:
- Характеристика “имя”: имя устройства. Права: чтение.
- Характеристика местоположения: описание места, где размещено устройство. Права: чтение.
6.3.2.2 Пульт управления
У нас есть один GATT-сервер для пульта управления. Мы можем назначить ему следующие сервисы и характеристики:
- GAP сервис (обязательный):
- Характеристика “имя”: имя устройства. Права: чтение.
- Характеристика местоположения: описание места, где размещено устройство. Права: чтение.
- Характеристика остаточного заряда аккумулятора: “Уровень заряда” Права: Чтение, уведомление.
- Характеристика кнопки “ВКЛ” Права: уведомление.
- Характеристика кнопки “ВЫКЛ” Права: уведомление.
6.3.3. Шаг 3: Использование стандартных сервисов и характеристик
Сервисы датчика окружающей среды, светильника и пульта являются нашими собственными, так как не существует ни одного стандартного сервиса, который мы бы могли использовать для них. Однако у нас есть три устройства с аккумуляторами, заряд которых нам необходимо отображать и мы можем использовать стандартную характеристику уровня заряда. Мы будем использовать ее для каждого устройства в его сервисе в GATT шлюза. Также мы будем использовать обязательный GAP сервис.
6.3.3.2. Пульт управления
Для пульта управления мы можем использовать стандартный сервис уровня заряда батареи и обязательный GAP сервис.
6.3.4. Шаг 4: Присвоение UUID нашим сервисам и характеристикам
Для каждого нестандартного сервиса и характеристики мы можем использовать онлайн-инструмент для генерации UUID, например этот.
Распространенной практикой является выбор базового UUID для сервиса и увеличение значения третьего и четвертого старшего байта UUID для каждой последующей характеристики.
Для примера, возьмем следующий UUID для какого-либо сервиса:
00000001-1000-2000-3000-111122223333
И затем заменим выделенные байты на
0000000[N]-1000-2000-3000-111122223333, где N>1 — порядковый номер характеристики.
Единственное условие, которое накладывает ограничение на выбор UUID для наших сервисов и характеристик это условие несовпадения нашего UUID с базовым UUID Bluetooth SIG: XXXXXXXX-0000-1000-8000-00805F9B34FB.
Следование вышепредставленной методике выбора UUID немного упрощает понимание связей между сервисами и их характеристиками.
На таблицах ниже представлены наши сервисы, характеристики и их UUID для шлюза и пульта управления.
6.3.5. Шаг 5: Реализация сервисов и характеристик с использованием API SDK производителя платформы
Каждая платформа, встраиваемая или мобильная, имеет собственный интерфейс прикладного программирования (API, application programming interface) для реализации сервисов и характеристик. Задача читателя – самостоятельно реализовать их для выбранного им устройства или приложения.
- bluetooth low energy
- ble
- Беспроводные технологии
- Стандарты связи
- Производство и разработка электроники
- Интернет вещей
- Электроника для начинающих
Android Bluetooth Low Energy (BLE) — готовим правильно, часть #2 (connecting/disconnecting)
В предыдущей статье мы подробно рассмотрели сканирование устройств. Эта статья — о подключении, отключении и обнаружении сервисов (discovering services).
Подключение к устройству
После удачного сканирования, вы должны подключиться к устройству, вызывая метод connectGatt() . В результате мы получаем объект – BluetoothGatt , который будет использоваться для всех GATT операций, такие как чтение и запись характеристик. Однако будьте внимательны, есть две версии метода connectGatt() . Поздние версии Android имеют еще несколько вариантов, но нам нужна совместимость с Android-6 поэтому мы рассматриваем только эти две:
BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback) BluetoothGatt connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback, int transport)
Внутренняя реализация первой версии – это фактически вызов второй версии с аргументом transport = TRANSPORT_AUTO . Для подключения BLE устройств такой вариант не подходит. TRANSPORT_AUTO используется для устройств с поддержкой и BLE и классического Bluetooth протоколов. Это значит, что Android будет сам выбирать протокол подключения. Этот момент практически нигде не описан и может привести к непредсказуемым результатам, много людей сталкивались с такой проблемой. Вот почему вы должны использовать вторую версию connectGatt() с transport = TRANSPORT_LE :
BluetoothGatt gatt = device.connectGatt(context, false, bluetoothGattCallback, TRANSPORT_LE);
Первый аргумент – context приложения. Второй аргумент – флаг autoconnect , говорит подключаться немедленно ( false ) или нет ( true ). При немедленном подключении ( false ) Android будет пытаться соединиться в течение 30 секунд (на большинстве смартфонов), по истечении этого времени придет статус соединения status_code = 133 . Это не официальная ошибка для таймаута соединения. В исходниках Android код фигурирует как GATT_ERROR . К сожалению, эта ошибка появляется и в других случаях. Имейте ввиду, с autoconnect = false Android делает соединение только с одним устройством в одно и то же время (это значит если у вас несколько устройств — подключайте их последовательно, а не паралелльно). Третий аргумент – функция обратного вызова BluetoothGattCallback (callback) для конкретного устройства. Этот колбек используется для всех связанных с устройством операциях, такие как чтение и запись. Мы рассмотрим это более детально в следующей статье.
Autoconnect = true
Если вы установите autoconnect = true , Android будет подключаться самостоятельно к устройству всякий раз, когда оно будет обнаружено. Внутри это работает так: Bluetooth стек сканирует сохраненные устройства и когда увидит одно из них – подключается к нему. Это довольно удобно, если вы хотите подключиться к конкретному устройству, когда оно становится доступным. Фактически, это предпочтительный способ для переподключения. Вы просто создаете BluetoothDevice объект и вызываете connectGatt с autoconnect = true .
BluetoothDevice device = bluetoothAdapter.getRemoteDevice("12:34:56:AA:BB:CC"); BluetoothGatt gatt = device.connectGatt(context, true, bluetoothGattCallback, TRANSPORT_LE);
Обратите внимание, этот подход работает только, если устройство есть в Bluetooth кеше или устройство было уже сопряжено (bonding). Посмотрите мою предыдущую статью, где подробно объясняется работа с Bluetooth кешем. При перезагрузке смартфона или выключении/включении Bluetooth (а также Airplane режима) – кеш очистится, это надо проверять перед подключением с autoconnect = true , что действительно раздражает.
Autoconnect работает только с закешированными и сопряженными (bonded) устройствами!
Для того, чтобы узнать, закешировано устройство или нет, можно использовать небольшой трюк. После создания объекта BluetoothDevice , вызовите у него getType , если результат – TYPE_UNKNOWN , значит устройство не закешировано. В этом случае, необходимо просканировать устройство с этим мак-адресом (используя не агрессивный метод сканирования) и после этого можно использовать автоподключение снова.
Android-6 и ниже имеет известный баг, в котором возникает гонка состояний и автоматическое подключение становится обычным ( autoconnect = false ). К счастью, умные ребята из Polidea нашли решение для этого. Настоятельно рекомендуется использовать его, если думаете использовать автоподключение.
- работает достаточно хорошо на современных версиях Android (прим. переводчика — от Android-8 и выше).
- возможность подключаться к нескольким устройствам одновременно;
- работает медленнее, если сравнивать сканирование в агрессивном режиме + подключение с autoconnect = false . Потому что Android в этом случае сканирует в режиме SCAN_MODE_LOW_POWER , экономя энергию.
Изменения статуса подключения
После вызова connectGatt() , Bluetooth стек присылает результат в колбек onConnectionStateChange , он вызывается при любом изменении соединения.
Работа с этим колбеком – достаточно нетривиальная вещь. Большинство простых примеров из сети выглядит так (не обольщайтесь):
public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState) < if (newState == BluetoothProfile.STATE_CONNECTED) < gatt.discoverServices(); >else < gatt.close(); >>
Этот код обрабатывает только аргумент newState и полностью игнорирует status . В многих случаях это работает и кажется безошибочным. Действительно, после подключения, следующее что нужно сделать – это вызвать discoverServices() . А в случае отключения — необходимо сделать вызов close() , чтобы Android освободил все связанные ресурсы в стеке Bluetooth. Эти два момента очень важные для стабильной работы BLE под Android, давайте их обсудим прямо сейчас!
При вызове connectGatt() , Bluetooth стек регистрирует внутри себя интерфейс для нового клиента ( client interface: clientIf ).
Возможно вы заметили такие логи в LogCat:
D/BluetoothGatt: connect() - device: B0:49:5F:01:20:XX, auto: false D/BluetoothGatt: registerApp() D/BluetoothGatt: registerApp() — UUID=0e47c0cf-ef13–4afb-9f54–8cf3e9e808d5 D/BluetoothGatt: onClientRegistered() — status=0 clientIf=6
Здесь видно, что клиент 6 был зарегистрирован после вызова connectGatt() . Максимальное количество клиентов (подключения) у Android равно 30 (константа GATT_MAX_APPS в исходниках), при достижении которого – Android не будет подключаться к устройствам вообще и вы будете получать постоянно ошибку подключения. Достаточно странно, но сразу после загрузки Android уже имеет 5 или 6 таких подключенных клиентов, предполагаю, что Android использует их для внутренних нужд. Таким образом, если вы не вызываете метод close() , то счетчик клиентов увеличивается каждый раз при вызове connectGatt() . Когда вы вызываете close() , Bluetooth стек удаляет ваш колбек, счетчик клиентов уменьшается на единицу и освобождает ресурсы клиента.
D/BluetoothGatt: close() D/BluetoothGatt: unregisterApp() — mClientIf=6
Важно всегда вызывать close() после отключения! А сейчас обсудим основные случаи дисконнекта устройств.
Состояние подключения (newState)
Переменная newState содержит новое состояние подключения и может иметь 4 значения:
- STATE_CONNECTED
- STATE_DISCONNECTED
- STATE_CONNECTING
- STATE_DISCONNECTING
Значения говорят сами за себя. Хотя состояния STATE_CONNECTING , STATE_DISCONNECTING есть в документации, на практике я их не встречал. Так что, в принципе, можно не обрабатывать их, но для уверенности, я предлагаю их явно учитывать (прим. переводчика — и это лучше, чем не обрабатывать их), вызывая close() только в том случае если устройство действительно отключено.
Статус подключения (status)
В примере выше, переменная статуса status полностью игнорировалась, но в действительности обрабатывать ее важно. Эта переменная, по сути, является кодом ошибки. Вы можете получить GATT_SUCCESS в результате как подключения, так и контролируемого отключения. Таким образом, мы можем по-разному обрабатывать контролируемое или внезапное отключение устройства. Если вы получили значение отличное от GATT_SUCCESS , значит «что-то пошло не так» и в status будет указана причина. К сожалению, объект BluetoothGatt дает очень мало кодов ошибок, все они описаны здесь. Чаще всего вы будете встречаться с кодом 133 ( GATT_ERROR ). Который не имеет точного описания, и просто говорит – «произошла какая-то ошибка». Не очень информативно, подробнее об GATT_ERROR позже.
Теперь мы знаем, что обозначают переменные newState и status , давайте улучшим наш колбек onConnectionStateChange :
public void onConnectionStateChange(final BluetoothGatt gatt, final int status, final int newState) < if(status == GATT_SUCCESS) < if (newState == BluetoothProfile.STATE_CONNECTED) < // Мы подключились, можно запускать обнаружение сервисов gatt.discoverServices(); >else if (newState == BluetoothProfile.STATE_DISCONNECTED) < // Мы успешно отключились (контролируемое отключение) gatt.close(); >else < // мы или подключаемся или отключаемся, просто игнорируем эти статусы >> else < // Произошла ошибка. разбираемся, что случилось! . gatt.close(); >
Это не последний вариант, мы еще улучшим колбек в этой статье. В любом случае, теперь у нас есть обработка ошибок и успешных операций.
Состояние bonding (bondState)
Последний параметр, который необходимо учитывать в колбеке onConnectionStateChange – это bondState , состояние сопряжения (bonding) с устройством. Мы получаем этот параметр так:
int bondstate = device.getBondState();
Состояние bonding может иметь одно из трех значений BOND_NONE , BOND_BONDING or BOND_BONDED . Каждое из них влияет на то, как обрабатывать подключение.
- BOND_NONE , нет проблем, можно вызывать discoverServices() ;
- BOND_BONDING , устройство в процессе сопряжения, нельзя вызывать discoverServices() , так как Bluetooth стек в работе и запуск discoverServices() может прервать сопряжение и вызвать ошибку соединения. discoverServices() вызываем только после того, как пройдет сопряжение (bonding);
- BOND_BONDED , для Android-8 и выше, можно запускать discoverServices() без задержки. Для версий 7 и ниже может потребоваться задержка перед вызовом. Если ваше устройство имеет Service Changed Characteristic, то Bluetooth стек в этот момент еще обрабатывает их и запуск discoverServices() без задержки может вызвать ошибку соединения. Добавьте 1000-1500мс задержки, конкретное значение зависит от количества характеристик на устройстве. Используйте задержку всегда, если вы не знаете сколько Service Changed Characteristic имеет устройство.
Теперь мы можем учитывать состояние bondState вместе с status и newState :
if (status == GATT_SUCCESS) < if (newState == BluetoothProfile.STATE_CONNECTED) < int bondstate = device.getBondState(); // Обрабатываем bondState if(bondstate == BOND_NONE || bondstate == BOND_BONDED) < // Подключились к устройству, вызываем discoverServices с задержкой int delayWhenBonded = 0; if (Build.VERSION.SDK_INT final int delay = bondstate == BOND_BONDED ? delayWhenBonded : 0; discoverServicesRunnable = new Runnable() < @Override public void run() < Log.d(TAG, String.format(Locale.ENGLISH, "discovering services of '%s' with delay of %d ms", getName(), delay)); boolean result = gatt.discoverServices(); if (!result) < Log.e(TAG, "discoverServices failed to start"); >discoverServicesRunnable = null; > >; bleHandler.postDelayed(discoverServicesRunnable, delay); > else if (bondstate == BOND_BONDING) < // Bonding в процессе, ждем когда закончится Log.i(TAG, "waiting for bonding to complete"); >.
Обработка ошибок
После того как мы разобрались с успешными операциями, давайте взглянем на ошибки. Есть ряд ситуаций, которые на самом деле «нормальные», но выдают себя за ошибки.
- Устройство отключилось намеренно. Например, все данные были переданы и больше ему нечего делать. Вы получите статус — 19 ( GATT_CONN_TERMINATE_PEER_USER );
- Истекло время ожидания соединения и устройство отключилось само. В этом случае придет статус — 8 ( GATT_CONN_TIMEOUT );
- Низкоуровневая ошибка соединения, которая привела к отключению. Обычно это статус — 133 ( GATT_ERROR ) или более конкретный код, если повезет;
- Bluetooth стек не смог подключится ни разу. Здесь также получим статус — 133 ( GATT_ERROR );
- Соединение было потеряно в процессе bonding или discoverServices . Необходимо выяснить причину и возможно повторить попытку подключения.
Первые два случая абсолютно нормальные явления и все что нужно сделать — это вызывать close() и подчистить ссылки на объект BluetoothGatt , если необходимо. В остальных случаях, либо ваш код, либо устройство, что-то делает не так. Вы возможно захотите уведомить UI или другие части приложения о проблеме, повторить подключение или еще каким-то образом отреагировать на ситуацию. Взгляните как я сделал это в моей библиотеке.
Статус 133 при подключении (connecting)
Статус — 133 часто встречается при попытках подключиться к устройству, особенно во время разработки. Этот статус может иметь множество причин, некоторые из них можно контролировать:
- Убедитесь, что вы всегда вызываете close() при отключении. Если этого не сделать, в следующий раз при подключении вы точно получите status=133 ;
- Всегда используйте TRANSPORT_LE в вызове connectGatt() ;
- Перезагрузите смартфон. Возможно Bluetooth стек выбрал лимит по клиентским подключениям или есть внутренняя проблема. (Прим. переводчика: я сначала выключал/включал Bluetooth, потом Airplane режим и если не помогало — перезагружал);
- Проверьте что устройство посылает advertising пакеты. Вызов connectGatt() с autoconnect = false имеет таймаут 30 секунд, после чего присылает ошибку status=133 ;
- Замените/зарядите батарею на устройстве. Обычно устройства работают нестабильно при низком заряде;
Если вы попробовали все способы выше и все еще получаете статус 133, необходимо просто повторить подключение! Это одна из Android ошибок, которую мне так и не удалось понять или решить. Иногда вы получаете 133 при подключении к устройству, но если вызывать close() и переподключиться, то все работает без проблем! Есть подозрение, что проблема в кеше Android и вызов close() сбрасывает его состояние для конкретного устройства. Если кто-нибудь поймет, как решить эту проблему – дайте мне знать!
Отключение по запросу (disconnect)
Для отключения устройства вам необходимо сделать шаги:
- вызвать disconnect() ;
- подождать обновления статуса в onConnectionStateChange ;
- вызвать close() ;
- освободить связанные с объектом gatt ресурсы;
Команда disconnect() фактически разрывает соединение с устройством и обновляет внутреннее состояние Bluetooth стека. Затем вызывается колбек onConnectionStateChange с новым состоянием «disconnected».
Вызов close() удаляет ваш BluetoothGattCallback и освобождает клиента в Bluetooth стеке.
Наконец, удаление BluetoothGatt освободит все связанные с подключением ресурсы.
Отключение «неправильно»
В примерах из сети можно увидеть, разные примеры отключения, например:
- вызвать disconnect()
- сразу вызвать close()
Это будет работать более-менее. Да устройство отключится, но вы никогда не получите вызов колбека с состоянием «disconnected». Дело в том, что disconnect() операция асинхронная (не блокирует поток и имеет свое время выполнения), а close() немедленно удаляет коллбек! Получается, когда Android будет готов вызвать колбек, его уже не будет.
Иногда в примерах не вызывают disconnect() , а только close() . Это приведет к отключению устройства, но это неправильный способ, поскольку disconnect() отключает активное соединение и отменяет ожидающее автоматическое подключение (вызов с autoconnect = true ). Поэтому, если вы вызываете только close() , любое ожидающее автоподключение может привести к новому подключению.
Отмена попытки подключения
Если вы хотите отменить подключение после connectGatt() , вам нужно вызвать disconnect() . Так как в этому моменту вы еще не подключены, колбек onConnectionStateChange не сработает! Просто подождите некоторое время после disconnect() и после этого вызывайте close() (прим. переводчика: обычно это 50-100мс).
При удачной отмене вы увидите примерно такое в логах:
D/BluetoothGatt: cancelOpen() — device: CF:A9:BA:D9:62:9E
Скорее всего, вы никогда не отмените соединение, для параметра autoconnect = false . Часто это делается для подключений с autoconnect = true . Например, когда приложение на переднем плане – вы подключаетесь к вашим устройствам и отключаетесь от них, если приложение переходит в фон.
Прим. переводчика: но это не значит что для autoconnect = false не надо проводить такую отмену!
Обнаружение сервисов (discovering services)
Как только вы подключились к устройству, необходимо запустить обнаружение его сервисов вызовом discoverServices() . Bluetooth стек запустит серию низкоуровневых команд для получения сервисов, характеристик и дескрипторов. Это занимает обычно около одной секунды в зависимости от того сколько таких служб, характеристик, дескрипторов имеет ваше устройство. В результате будет вызыван колбек onServicesDiscovered.
Первым делом проверим, есть ли какие ошибки после обнаружения сервисов:
// Проверяем есть ли ошибки? Если да - отключаемся if (status == GATT_INTERNAL_ERROR)
Если есть ошибки (обычно это GATT_INTERNAL_ERROR со значением 129), делаем отключение устройства, что-то исправить здесь невозможно (нет специальных технических способов для этого). Вы просто отключаете устройство и повторно пробуете подключиться.
Если все прошло удачно, вы получите список сервисов:
final List services = gatt.getServices(); Log.i(TAG, String.format(Locale.ENGLISH,"discovered %d services for '%s'", services.size(), getName())); // Работа со списком сервисов (если требуется) .
Кеширование сервисов.
Bluetooth стек кеширует найденные на устройстве сервисы, характеристики и дескрипторы. Первое подключение вызывает реальное обнаружение сервисов, все последующие – возвращаются кешированные версии. Это соответствует стандарту Bluetooth. Обычно это нормально и сокращает время соединения с устройством. Однако в некоторых случаях, может потребоваться очистить кеш, чтобы снова обнаружить их с устройства при следующем соединении. Типичный сценарий: обновление прошивки, в которой изменяется набор сервисов, характеристик, дескрипторов. Есть скрытый метод очистки кеша и добраться до него нам поможет механизм рефлексии Java:
private boolean clearServicesCache() < boolean result = false; try < Method refreshMethod = bluetoothGatt.getClass().getMethod("refresh"); if(refreshMethod != null) < result = (boolean) refreshMethod.invoke(bluetoothGatt); >> catch (Exception e) < Log.e(TAG, "ERROR: Could not invoke refresh method"); >return result; >
Этот метод асинхронный, дайте ему некоторое время для завершения!
Странные штуки в подключении/отключении
Хотя операции подключения и отключения выглядят просто, есть некоторые особенности, которые нужно знать.
- Случайная ошибка 133 при подключении, выше мы разобрались как с ней работать;
- Периодическое зависание подключения, не срабатывает таймаут и не вызывается колбек onConnectionStateChange . Это случается не часто, но я видел такие случае при низком уровне батареи или когда устройство находится на границе доступности по расстоянию Bluetooth. Скорее всего общение с устройством происходит, но затем прерывается и зависает. Мой обходной путь – использовать свой таймер подключения и в случае таймаута – закрывать соединение и отключаться;
- Некоторые смартфоны имеют проблему с подключением во время сканирования. Например, Huawei P8 Lite один из таких. Останавливаем сканнер перед любым подключением (Прим. переводчика: это правило соблюдаем строго!);
- Все вызовы подключения/отключения асинхронные. То есть неблокирующие, но при этом им нужно время, чтобы выполнится до конца. Избегайте быстрый запуск их друг за другом (Прим. переводчика: я обычно использую задержку 50-100мс между вызовами).
Следующая статья: чтение и запись характеристик.
Теперь мы разобрались с подключением/отключением и обнаружением сервисов, следующая статья – о том, как работать с характеристиками.
Не терпится поработать с BLE? Попробуйте мою библиотеку Blessed for Android. Она использует все подходы из этой серии статей и упрощает работу с BLE в вашем приложении.
- Android
- bluetooth
- bluetooth le
- bluetooth low energy
Как отключить low energy e bluetooth
HackWare.ru Этичный хакинг и тестирование на проникновение, информационная безопасность Что такое Bluetooth Low Energy (BLE) и как его взламывают Чтобы вы не ушли пока читаете… Подробнее » Как отключить low energy e bluetooth
Как открутить шуруп в утюге
- автор: admin
- 27.07.2023
Разборка современного утюга Несмотря на кажущуюся простоту утюга, разобрать его не всем хозяевам под силу. Все дело в многочисленных крепежных элементах, спрятанных под декоративную отделку.… Подробнее » Как открутить шуруп в утюге
Как определить экономическую плотность тока
- автор: admin
- 27.07.2023
2.16 Понятие об экономической плотности тока. Выбор сечения по экономической плотности тока Экономической называют такую плотность тока в проводнике, при которой затраты на сооружение и… Подробнее » Как определить экономическую плотность тока
Как осуществить подбор оборудования для пнр
- автор: admin
- 27.07.2023
Как осуществить подбор оборудования для пнр Пусконаладочные работы занимают важнейшее место и являются завершающими в общем комплексе строительства и монтажа энергетических объектов. От того настолько… Подробнее » Как осуществить подбор оборудования для пнр
Как определить тип конденсатора
- автор: admin
- 27.07.2023
Виды и аналоги конденсаторов Конденсаторы – электронные компоненты, состоящие из двух проводников-обкладок и находящимся между ними диэлектриком. Существует множество видов конденсаторов, имеющих сходную конструкцию, но… Подробнее » Как определить тип конденсатора
How do I fix Bluetooth Low Energy?
Users can disable system-level Bluetooth background scanning by going to Settings > Security & Location > Location > Scanning and disabling the toggle for Bluetooth scanning.
How do I know if my phone has Bluetooth low energy?
This allows Android apps to communicate with BLE devices that have low power requirements, such as proximity sensors, heart rate monitors, fitness devices, and so on. To check if your device support Bluetooth Low Energy programmically, check (getPackageManager(). hasSystemFeature(PackageManager. FEATURE_BLUETOOTH_LE)).
How do I connect to a BLE device?
These are the general steps your app will need to do when establishing a connection with a BLE device.
- Scan For Devices (Scanning with a filter is ideal to limit the number or irrelevant devices found.)
- Connect to the Device (No pairing or approval process.
- Get GATT Client (see below for more on GATT)
What to do when your phone won’t connect to your Bluetooth?
Head into the Settings menu of your phone. Tap on Bluetooth. When your Bluetooth is On you will see a list of devices that you had paired it with. There is a settings icon next to the name of each device, tap on the icon and unpair the device. Do this for all the devices till you have no devices on your list.
Are there any Bluetooth Low Energy ( BLE ) devices?
Huawei phones are notorious of their bluetooth bug with BLE devices. Garmin, FitBit and Nokia/Withings activity trackers suffer from the same issue. Garmin developers have managed to somehow workaround this Huawei bug with a help of Huawei engineers. https://support.garmin.com/en-US/?faq=3qBFoCgeH428IUUfc0SpZ7
Why does my Bluetooth keep pairing with something else?
Some older devices are very simple. They just try to connect with the last thing they paired with. If a Bluetooth device was previously paired with something else, turn off that other gadget. And if you are no longer planning on using that other gadget, unpair it from your Bluetooth device to prevent future issues.
Why does my Android phone not pair with my phone?
Bluetooth is a technology that allows you to connect two devices wirelessly so that data and information can flow between the two devices. An important step to connect the devices is known as pairing. When you pair the Bluetooth devices successfully, data can be transferred easily from one to another.
Is Bluetooth Low Energy Bluetooth 5?
With Bluetooth 5.0, all audio devices communicate over Bluetooth Low Energy, which means reduced power usage and longer battery life. They use Bluetooth 4.2 and the special Apple W1 chip for an improved connection. On Android, Bluetooth 5.0 should help make Bluetooth headphones something you’d want to use.
What’s the difference between Bluetooth 4 and 5?
Bluetooth 5 is faster than Bluetooth 4 with the format having 2Mbps, twice the speed of Bluetooth 4 about 1 Mbps making the Bluetooth 5 able to meet one of IoT requirements. This is thanks to the 5Mbps bandwidth of the Bluetooth 5 in comparison to 2.1 Mbps of the Bluetooth 4.
What are Bluetooth Low Energy devices?
Bluetooth Low Energy is a power-conserving variant of Bluetooth personal area network (PAN) technology, designed for use by Internet-connected machines and appliances. Also marketed as Bluetooth Smart, Bluetooth LE was introduced in the Bluetooth 4.0 specification as an alternative to Bluetooth Classic.
How to send data from Android to Bluetooth LE?
In the original example code haven’t handle send data from Android to Bluetooth LE device. It’s modified to echo the received data back HM-10, to sender. Modify BluetoothLeService.java to add method writeCharacteristic (BluetoothGattCharacteristic characteristic) to write data.
How to use Android BLE to communicate with Bluetooth devices?
Use this hack method to convert from an integer to UUID: Ensures Bluetooth is available on the device and it is enabled. If not, display a dialog requesting user permission to enable Bluetooth:
What are the components of Bluetooth in Android?
First, let’s write down all the key components we have on bluetooth in a short and concise definition to get a feel of what we have to do. BLE: Android built-in platform to discover devices, request and transmit information from our bluetooth device. GATT: Generic Attribute Profile to define how to exchange data using predefined attributes.
How to use Bluetooth Classic or Bluetooth Low Energy?
To do some Bluetooth Classic or Bluetooth Low Energy actions on a particular Bluetooth device, we need to obtain a BluetoothDevice object. If we already know the MAC address of our Bluetooth device and we want to hardcode it in our mobile app, we can just use: Otherwise, we need to start the discovery process.