Read characteristic ble android

Bluetooth Low Energy

In this document

Key classes

See Also

Video

DevBytes: Bluetooth Low Energy API

Android 4.3 (API Level 18) introduces built-in platform support for Bluetooth Low Energy in the central role and provides APIs that apps can use to discover devices, query for services, and read/write characteristics. In contrast to Classic Bluetooth, Bluetooth Low Energy (BLE) is designed to provide significantly lower power consumption. 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.

Key Terms and Concepts

Here is a summary of key BLE terms and concepts:

  • Generic Attribute Profile (GATT)—The GATT profile is a general specification for sending and receiving short pieces of data known as «attributes» over a BLE link. All current Low Energy application profiles are based on GATT.
    • The Bluetooth SIG defines many profiles for Low Energy devices. A profile is a specification for how a device works in a particular application. Note that a device can implement more than one profile. For example, a device could contain a heart rate monitor and a battery level detector.
  • Attribute Protocol (ATT)—GATT is built on top of the Attribute Protocol (ATT). This is also referred to as GATT/ATT. ATT is optimized to run on BLE devices. To this end, it uses as few bytes as possible. Each attribute is uniquely identified by a Universally Unique Identifier (UUID), which is a standardized 128-bit format for a string ID used to uniquely identify information. The attributes transported by ATT are formatted as characteristics and services.
  • Characteristic—A characteristic contains a single value and 0-n descriptors that describe the characteristic’s value. A characteristic can be thought of as a type, analogous to a class.
  • Descriptor—Descriptors are defined attributes that describe a characteristic value. For example, a descriptor might specify a human-readable description, an acceptable range for a characteristic’s value, or a unit of measure that is specific to a characteristic’s value.
  • Service—A service is a collection of characteristics. For example, you could have a service called «Heart Rate Monitor» that includes characteristics such as «heart rate measurement.» You can find a list of existing GATT-based profiles and services on bluetooth.org.

Roles and Responsibilities

Here are the roles and responsibilities that apply when an Android device interacts with a BLE device:

  • Central vs. peripheral. This applies to the BLE connection itself. The device in the central role scans, looking for advertisement, and the device in the peripheral role makes the advertisement.
  • GATT server vs. GATT client. This determines how two devices talk to each other once they’ve established the connection.

To understand the distinction, imagine that you have an Android phone and an activity tracker that is a BLE device. The phone supports the central role; the activity tracker supports the peripheral role (to establish a BLE connection you need one of each—two things that only support peripheral couldn’t talk to each other, nor could two things that only support central).

Once the phone and the activity tracker have established a connection, they start transferring GATT metadata to one another. Depending on the kind of data they transfer, one or the other might act as the server. For example, if the activity tracker wants to report sensor data to the phone, it might make sense for the activity tracker to act as the server. If the activity tracker wants to receive updates from the phone, then it might make sense for the phone to act as the server.

In the example used in this document, the Android app (running on an Android device) is the GATT client. The app gets data from the GATT server, which is a BLE heart rate monitor that supports the Heart Rate Profile. But you could alternatively design your Android app to play the GATT server role. See BluetoothGattServer for more information.

BLE Permissions

In order to use Bluetooth features in your application, you must declare the Bluetooth permission BLUETOOTH . You need this permission to perform any Bluetooth communication, such as requesting a connection, accepting a connection, and transferring data.

If you want your app to initiate device discovery or manipulate Bluetooth settings, you must also declare the BLUETOOTH_ADMIN permission. Note: If you use the BLUETOOTH_ADMIN permission, then you must also have the BLUETOOTH permission.

Declare the Bluetooth permission(s) in your application manifest file. For example:

If you want to declare that your app is available to BLE-capable devices only, include the following in your app’s manifest:

Читайте также:  Датчики давления шинах для android

However, if you want to make your app available to devices that don’t support BLE, you should still include this element in your app’s manifest, but set required=»false» . Then at run-time you can determine BLE availability by using PackageManager.hasSystemFeature() :

Setting Up BLE

Before your application can communicate over BLE, you need to verify that BLE is supported on the device, and if so, ensure that it is enabled. Note that this check is only necessary if is set to false.

If BLE is not supported, then you should gracefully disable any BLE features. If BLE is supported, but disabled, then you can request that the user enable Bluetooth without leaving your application. This setup is accomplished in two steps, using the BluetoothAdapter .

The BluetoothAdapter is required for any and all Bluetooth activity. The BluetoothAdapter represents the device’s own Bluetooth adapter (the Bluetooth radio). There’s one Bluetooth adapter for the entire system, and your application can interact with it using this object. The snippet below shows how to get the adapter. Note that this approach uses )»>getSystemService() to return an instance of BluetoothManager , which is then used to get the adapter. Android 4.3 (API Level 18) introduces BluetoothManager :

Next, you need to ensure that Bluetooth is enabled. Call isEnabled() to check whether Bluetooth is currently enabled. If this method returns false, then Bluetooth is disabled. The following snippet checks whether Bluetooth is enabled. If it isn’t, the snippet displays an error prompting the user to go to Settings to enable Bluetooth:

If you want to scan for only specific types of peripherals, you can instead call startLeScan(UUID[], BluetoothAdapter.LeScanCallback) , providing an array of UUID objects that specify the GATT services your app supports.

Here is an implementation of the BluetoothAdapter.LeScanCallback , which is the interface used to deliver BLE scan results:

Note: You can only scan for Bluetooth LE devices or scan for Classic Bluetooth devices, as described in Bluetooth. You cannot scan for both Bluetooth LE and classic devices at the same time.

Connecting to a GATT Server

The first step in interacting with a BLE device is connecting to it— more specifically, connecting to the GATT server on the device. To connect to a GATT server on a BLE device, you use the connectGatt() method. This method takes three parameters: a Context object, autoConnect (boolean indicating whether to automatically connect to the BLE device as soon as it becomes available), and a reference to a BluetoothGattCallback :

This connects to the GATT server hosted by the BLE device, and returns a BluetoothGatt instance, which you can then use to conduct GATT client operations. The caller (the Android app) is the GATT client. The BluetoothGattCallback is used to deliver results to the client, such as connection status, as well as any further GATT client operations.

In this example, the BLE app provides an activity ( DeviceControlActivity ) to connect, display data, and display GATT services and characteristics supported by the device. Based on user input, this activity communicates with a Service called BluetoothLeService , which interacts with the BLE device via the Android BLE API:

When a particular callback is triggered, it calls the appropriate broadcastUpdate() helper method and passes it an action. Note that the data parsing in this section is performed in accordance with the Bluetooth Heart Rate Measurement profile specifications:

Back in DeviceControlActivity , these events are handled by a BroadcastReceiver :

Reading BLE Attributes

Once your Android app has connected to a GATT server and discovered services, it can read and write attributes, where supported. For example, this snippet iterates through the server’s services and characteristics and displays them in the UI:

Receiving GATT Notifications

It’s common for BLE apps to ask to be notified when a particular characteristic changes on the device. This snippet shows how to set a notification for a characteristic, using the setCharacteristicNotification() method:

Once notifications are enabled for a characteristic, an onCharacteristicChanged() callback is triggered if the characteristic changes on the remote device:

Closing the Client App

Once your app has finished using a BLE device, it should call close() so the system can release resources appropriately:

Источник

Android Bluetooth Low Energy (BLE) — готовим правильно, часть #1 (scanning)

Содержание

Часть #1 (scanning), вы здесь.

Могу точно сказать – это было сложней, чем представлял, мне пришлось приложить немало усилий для стабильной работы под Android. Я изучил много статей в свободном доступе, некоторые оказались ошибочными, многие были очень полезными и помогли в деле. В этой серии статей я хочу описать свои выводы, чтобы вы не тратили уйму времени на поиски как я.

Особенности работы BLE под Android

Google документация по BLE очень общая, в некоторых случаях нет важной информации или она устарела, примеры приложений не показывают, как правильно использовать BLE. Я обнаружил лишь несколько источников, как правильно сделать BLE. Презентация Stuart Kent дает замечательный материал для старта. Для некоторых продвинутых тем есть хорошая статья Nordic.

Android BLE API это низкоуровневые операции, в реальных приложениях нужно использовать несколько слоев абстракции (как например сделано «из коробки» в iOS-CoreBluetooth). Обычно нужно самостоятельно сделать: очередь команд, bonding, обслуживание соединений, обработка ошибок и багов, мультипоточный доступ . Самые известные библиотеки: SweetBlue, RxAndroidBle и Nordic. На мой взгляд самая легкая для изучения — Nordic, см. детали тут.

Читайте также:  Как снять пин код с экрана андроида

Производители делают изменения в Android BLE стеке или полностью заменяют на свою реализацию. И надо учитывать разницу поведения для разных устройств в приложении. То что прекрасно работает на одном телефоне, может не работать на других! В целом не все так плохо, например реализация Samsung сделана лучше собственной реализации от Google!

В Android есть несколько известных (и неизвестных) багов которые должны быть обработаны, особенно в версиях 4,5 и 6. Более поздние версии работают намного лучше, но тоже имеют определенные проблемы, такие как случайные сбои соединения с ошибкой 133. Подробнее об этом ниже.

Не претендую на то, что я решил все проблемы, но мне удалось выйти на «приемлемый» уровень. Начнем со сканирования.

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

Перед подключением к устройству вам нужно его просканировать. Это делается при помощи класса BluetoothLeScanner :

Сканер пытается найти устройства в соответствии с настройками filters и scanSettings , при обнаружении устройства вызывается scanCallback :

В результате сканирования мы получаем экземпляр ScanResult , в котором есть объект BluetoothDevice , его используют для подключения к устройству. Но прежде чем начать подключаться, поговорим о сканировании подробнее, ScanResult содержит несколько полезных сведений об устройстве:

Advertisement data — массив байтов с информацией об устройстве, для большинства устройств это имя и UUID сервисов, можно задать в filters имя устройства и UUID сервисов для поиска конкретных устройств.

RSSI уровень — уровень сигнала (насколько близко устройство).

… дополнительные данные, см. документацию по ScanResult здесь.

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

Настраиваем фильтр для сканирования

Вообще можно передать null вместо фильтров и получить все ближайшие устройства, иногда это полезно, но чаще требуются устройства с определенным именем или набором UUID сервисов.

Сканирование устройств по UUID сервиса

Используется если вам необходимо найти устройства определенной категории, например мониторы артериального давления со стандартным сервисным UUID: 1810. При сканировании устройство может содержать в Advertisement data UUID сервис, который характеризует это устройство. На самом деле эти данные ненадежные, фактически сервисы могут не поддерживаться, или подделываться Advertisement data данные, в общем тут есть творческий момент.

Прим. переводчика: одно из моих устройств со специфичной прошивкой, вообще не содержало список UUID сервисов в Advertisement data, хотя все остальные прошивки этого устройства работали ожидаемо.

Пример сканирования службы с артериальным давлением:

Обратите внимание на короткий UUID (например 1810 ), он называется 16-bit UUID и является частью длинного 128-bit UUID (в данном случае 00001810-000000-1000-8000-000-00805f9b34fb ). Короткий UUID это BASE_PART длинного UUID, см. спецификацию здесь.

Сканирование устройств по имени

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

поиск конкретного устройства

поиск конкретной модели устройства, например, мой нагрудный напульсник Polar H7 определяется как «Polar H7 391BBB014», первая часть — «Polar H7» общая для всех таких устройств этой модели, а последняя часть «391BBB014» — уникальный серийный номер. Это очень распространенная практика. Если вы хотите найти все устройства «Polar H7», то фильтр по имени вам не поможет, придется искать подстроку у всех отсканированных устройств в ScanResult . Пример с поиском точно по имени:

Сканирование устройств по MAC-адресам.

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

Вероятно вы уже поняли, что можно комбинировать в фильтре UUID, имя и MAC-адрес устройства. Выглядит неплохо, но на практике я не применял такое. Хотя может быть вам это пригодится.

Настройка ScanSettings

ScanSettings объясняют Android как сканировать устройства. Там есть ряд настроек, которые можно задать, ниже полный пример:

ScanMode

Безусловно, это самый важный параметр. Определяет метод и время сканирования в Bluetooth стеке. Такая операция требует много энергии и необходим контроль над этим процессом, чтобы не разрядить батарею телефона быстро. Есть 4 режима работы, в соответствии с руководством Nordics и официальной документацией:

SCAN_MODE_LOW_POWER . В этом режиме Android сканирует 0.5с, потом делает паузу на 4.5с. Поиск может занять относительно длительное время, зависит от того насколько часто устройство посылает пакет advertisement данных.

SCAN_MODE_BALANCED . Время сканирования: 2с, время паузы: 3с, «компромиссный» режим работы.

SCAN_MODE_LOW_LATENCY . В этом случае, Android сканирует непрерывно, что очевидно требует больше энергозатрат, при этом получаются лучшие результаты сканирования. Режим подходит если вы хотите найти свое устройство как можно быстрее. Не стоит использовать для длительного сканирования.

SCAN_MODE_OPPORTUNISTIC . Результаты будут получены, если сканирование выполняется другими приложениями! Строго говоря, это вообще не гарантирует, что обнаружится ваше устройство. Стек Android использует этот режим в случае долгого сканирования, для понижения качества результатов (см. ниже «Непрерывное сканирование»).

Callback Type

Эта настройка контролирует как будет вызываться callback со ScanResult в соответствии с заданными фильтрами, есть 3 варианта:

CALLBACK_TYPE_ALL_MATCHES . Callback будет вызывать каждый раз, при получении advertisement пакета от устройств. На практике — каждые 200-500мс будет срабатывать сallback, в зависимости от частоты отправки advertisement пакетов устройствами.

Читайте также:  Android user permission file

CALLBACK_TYPE_FIRST_MATCH . Callback сработает один раз для устройства, даже если оно далее будет снова посылать advertisement пакеты.

CALLBACK_TYPE_MATCH_LOST . Callback будет вызван, если получен первый advertisement пакет от устройства и дальнейшие advertisement пакеты не обнаружены. Немного странное поведение.

В практике обычно используются настройка CALLBACK_TYPE_ALL_MATCHES или CALLBACK_TYPE_FIRST_MATCH . Правильный тип зависит от конкретного случая. Если не знаете — используйте CALLBACK_TYPE_ALL_MATCHES , это дает больше контроля при получении callback, если вы останавливаете сканирование после получения нужных результатов — фактически это CALLBACK_TYPE_FIRST_MATCH .

Match mode

Настройка того, как Android определяет «совпадения».

MATCH_MODE_AGGRESSIVE . Агрессивность обуславливается поиском минимального количества advertisement пакетов и устройств даже со слабым сигналом.

MATCH_MODE_STICKY . В противоположность, этот режим требует большего количества advertisement пакетов и хорошего уровня сигнала от устройств.

Я не тестировал эти настройки подробно, но я в основном использую MATCH_MODE_AGGRESSIVE , это помогает быстрее найти устройства.

Number of matches

Параметр определяет сколько advertisement данных необходимо для совпадения.

MATCH_NUM_ONE_ADVERTISEMENT . Одного пакета достаточно.

MATCH_NUM_FEW_ADVERTISEMENT . Несколько пакетов нужно для соответствия.

MATCH_NUM_MAX_ADVERTISEMENT . Максимальное количество advertisement данных, которые устройство может обработать за один временной кадр.

Нет большой необходимости в таком низкоуровневом контроле. Все что вам надо — быстро найти свое устройство, обычно используются первые 2 варианта.

Report delay

Задержка для вызова сallback в миллисекундах. Если она больше нуля, Android будет собирать результаты в течение этого времени и вышлет их сразу все в обработчике onBatchScanResults . Важно понимать что onScanResult не будет вызываться. Обычно применяется, когда есть несколько устройств одного типа и мы хотим дать пользователю выбрать одно из них. Единственная проблема здесь — предоставить информацию пользователю для выбора, это должен быть не только MAC-адрес (например имя устройства).

Важно: есть известный баг для Samsung S6 / Samsung S6 Edge, когда все результаты сканирования имеют один и тот же RSSI (уровень сигнала) при задержке больше нуля.

Кеширование Android Bluetooth стека

В результате процесса сканирования вы получаете список BLE устройств и при этом данные устройств «кешируются» в Bluetooth стеке. Там хранится основная информация: имя, MAC-адрес, тип адреса (публичный, случайный), тип устройства (Classic, Dual, BLE) и т.д. Android нужны эти данные, чтобы подключится к устройству быстрее. Он кеширует все устройства, которые видит при сканировании. Для каждого из них записывается небольшой файл с данными. Когда вы пытаетесь подключиться к устройству, стек Android ищет соответствующий файл, чтобы прочитать данные для подключения. Важный момент — одного MAC-адреса недостаточно для успешного подключения к устройству!

Очистка кеша

Bluetooth кеш, как и любой другой, не существует вечно, есть 3 ситуации, когда он очищается:

Выключение и включение системного переключателя Bluetooth

Очистка данных приложения (в ручном режиме в настройках телефона)

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

Это значит, что нельзя полагаться на данные об устройстве из BT кеша. Есть небольшой трюк, он поможет узнать закешировано ли устройство или нет:

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

Непрерывное сканирование?

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

Плохая новость в том, что Google в последнее время ограничивает (неофициально) непрерывное сканирование:

c Android 8.1 сканирование без фильтров блокируется при выключенном экране. Если у вас нет никаких ScanFilters , Android приостановит сканирование, когда экран выключен и продолжит, когда экран снова будет включен. Комментарии от Google. Это очевидно очередной способ энергосбережения от Google.

c Android 7 вы можете сканировать только в течение 30 минут, после чего Android меняет параметры на SCAN_MODE_OPPORTUNISTIC . Очевидное решение, перезапускать сканирование с периодом менее, чем 30 мин. Посмотрите commit в исходном коде.

с Android 7 запуск и останов сканирования более 5 раз за 30 секунд временно отключает сканирование.

Непрерывное сканирование в фоне

Google значительно усложнил сканирование на переднем плане. Для фонового режима вы столкнетесь с еще большими трудностями! Новые версии Android имеют лимиты на работу служб в фоновом режиме, обычно после 10 минут работы, фоновый сервис прекращает свою работу принудительно. Посмотрите возможные решения этой проблемы:

Проверка разрешений (permissions)

Есть еще несколько важных моментов, прежде чем мы закончим статью. Для начала сканирования нужны системные разрешения (permissions):

Убедитесь, что все разрешения одобрены, или запросите их у пользователя. Разрешение ACCESS_COARSE_LOCATION Google считает «опасным» и для него требуется обязательное согласие пользователя.

Прим. переводчика, в моем проекте для корректной работы с BLE потребовалось еще 2 разрешения: ACCESS_FINE_LOCATION (для API ACCESS_BACKGROUND_LOCATION обсуждение на Stackoverflow.

В итоге полный список разрешений включая версию Android10:

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

Заключение

Мы научились запускать сканирование BLE устройств с учетом жизненного цикла Activity (Fragment / Service), использовать фильтры и различные настройки сканирования, также узнали все нужные разрешения (permissions) для удачного запуска сканирования и особенности работы Android-Bluetooth кеша. В следующей статье мы погрузимся глубже в процесс подключения и отключения к устройствам.

Источник

Оцените статью