MyTetra Share
Делитесь знаниями!
Обзор Bluetooth BLE в Android (Bluetooth Low Energy)
19.08.2018
17:36
Автор: xintrea
Текстовые метки: android, андроид, java, bluetooth, ble, low energy, низкое энергопотребление, перевод, документация
Раздел: Компьютер - Android - Программирование под Андроид

Ниже представлен автоматический перевод статьи с официального сайта developer.android.com. В статье, помимо общего описания взаимодействия по Bluetooth BLE есть раздел, в котором расписываются опции, которые надо прописать в манифест AndroidManifest.xml, чтобы Android-приложение смогло работать с Bluetooth.


Обзор Bluetooth BLE

Android 4.3 (API уровня 18) представляет встроенную поддержку платформы для Bluetooth Low Energy (BLE) в центральной роли и предоставляет API, которые приложения могут использовать для обнаружения устройств, для запросов на услуги и для передачи информации.

В общем случае, использование включает следующее:

  • Передача небольших объемов данных между соседними устройствами.
  • Взаимодействие с датчиками приближения, такими как Google Beacons, чтобы дать пользователям индивидуальный опыт, основанный на их текущем местоположении.

В отличие от классического Bluetooth , технология Bluetooth Low Energy (BLE) предназначена для обеспечения значительно более низкого энергопотребления. Это позволяет приложениям Android взаимодействовать с устройствами BLE, которые имеют более строгие требования к питанию, такие как датчики приближения, мониторы сердечного ритма и фитнес-устройства.

Ключевые термины и понятия

Ниже приведен краткий обзор ключевых терминов и понятий BLE:

  • Профиль общих атрибутов (GATT). Профиль GATT является общей спецификацией для отправки и получения коротких фрагментов данных, известных как «атрибуты» по ссылке BLE. Все текущие профили приложений Low Energy основаны на GATT.
    • Bluetooth SIG определяет множество профилей для устройств с низкой энергией. Профиль - это спецификация того, как устройство работает в конкретном приложении. Обратите внимание, что устройство может реализовать более одного профиля. Например, устройство может содержать монитор сердечного ритма и детектор уровня заряда батареи.
  • Протокол атрибута (ATT) -GATT построен поверх протокола атрибутов (ATT). Это также называется GATT / ATT. ATT оптимизирован для работы на устройствах BLE. Для этого он использует как можно меньше байтов. Каждый атрибут уникально идентифицируется универсально уникальным идентификатором (UUID), который является стандартизованным 128-битным форматом для идентификатора строки, используемого для уникальной идентификации информации. Атрибуты , передаваемые ATT, отформатированы как характеристики и сервисы .
  • Характеристика - Характеристика содержит одно значение и 0-n дескрипторы, которые описывают значение признака. Характеристику можно рассматривать как тип, аналогичный классу.
  • Дескриптор - дескрипторы - это атрибуты, которые описывают характеристическое значение. Например, дескриптор может указывать читаемое человеком описание, допустимый диапазон для значения признака или единицу измерения, которая является специфической для значения признака.
  • Сервис- Сервис - это набор характеристик. Например, у вас может быть услуга «Heart Rate Monitor», которая включает такие характеристики, как «измерение сердечного ритма». Вы можете найти список существующих профилей и сервисов на основе GATT на bluetooth.org .

Роли и обязанности

Вот роли и обязанности, которые применяются, когда устройство Android взаимодействует с устройством BLE:

  • Центральный и периферийный. Это относится к самому соединению BLE. Объявление в центральной роли сканирует, ищет рекламу и устройство в периферийной роли.
  • GATT-сервер против клиента GATT. Это определяет, как два устройства разговаривают друг с другом, как только они установили соединение.

Чтобы понять различие, представьте, что у вас есть телефон Android и трекер активности, который является устройством BLE. Телефон поддерживает центральную роль; отслеживатель активности поддерживает периферийную роль (для установления соединения BLE вам понадобится одна из двух вещей, которые поддерживают только периферийные устройства, не могут разговаривать друг с другом, а также две вещи, которые поддерживают только центральную).

Как только телефон и отслеживатель активности установили соединение, они начинают передавать метаданные GATT друг другу. В зависимости от типа передаваемых данных один или другой может действовать как сервер. Например, если трекер активности хочет сообщить данные о датчике на телефон, может быть смысл отслеживать активность в качестве сервера. Если трекер активности хочет получать обновления с телефона, тогда для телефона может иметь смысл действовать как сервер.

В примере, используемом в этом документе, приложение Android (работает на устройстве Android) является клиентом GATT. Приложение получает данные с сервера GATT, который является монитором сердечного ритма BLE, который поддерживает профиль сердечного ритма. Но вы могли бы также разработать свое приложение для Android, чтобы играть роль сервера GATT. См. BluetoothGattServer для получения дополнительной информации.

Разрешения BLE

Чтобы использовать функции Bluetooth в своем приложении, вы должны объявить разрешение Bluetooth BLUETOOTH . Это разрешение требуется для выполнения любой связи Bluetooth, такой как запрос на соединение, прием соединения и передача данных.

Если вы хотите, чтобы ваше приложение инициировало обнаружение устройства или манипулировало настройками Bluetooth, вы также должны объявить разрешение BLUETOOTH_ADMIN . Примечание. Если вы используете разрешение BLUETOOTH_ADMIN , вы также должны иметь разрешение BLUETOOTH .

Объявите разрешения (ы) Bluetooth в файле манифеста приложения. Например:

<uses-permission android: name = "android.permission.BLUETOOTH" />

<uses-permission android: name = "android.permission.BLUETOOTH_ADMIN" />

Если вы хотите заявить, что ваше приложение доступно только для устройств с поддержкой BLE, включите в манифест приложения следующее:

<uses-feature android: name = "android.hardware.bluetooth_le" android: required = "true" />

Однако, если вы хотите, чтобы ваше приложение было доступно для устройств, которые не поддерживают BLE, вы все равно должны включать этот элемент в манифест вашего приложения, но установите required="false" . Затем во время выполнения вы можете определить доступность BLE с помощью PackageManager.hasSystemFeature() :

// Используйте эту проверку, чтобы определить, поддерживается ли BLE на устройстве. затем

// вы можете выборочно отключать связанные с BLE функции.

if (! getPackageManager (). hasSystemFeature (PackageManager.FEATURE_BLUETOOTH_LE)) {

Toast.makeText (это, R.string.ble_not_supported, Toast.LENGTH_SHORT) .show ();

Конец();

}

Примечание: Маяки LE часто связаны с местоположением. Чтобы использовать BluetoothLeScanner без фильтра, вы должны запросить разрешение пользователя, объявив либо разрешение ACCESS_COARSE_LOCATION либо ACCESS_FINE_LOCATION в файле манифеста приложения. Без этих разрешений сканирование не вернет никаких результатов.

Настроить BLE

Прежде чем ваше приложение сможет связываться через BLE, вам необходимо убедиться, что BLE поддерживается на устройстве, и если да, убедитесь, что он включен. Обратите внимание, что эта проверка необходима только в том случае, если для параметра <uses-feature.../> установлено значение false.

Если BLE не поддерживается, вы должны изящно отключить любые функции BLE. Если BLE поддерживается, но отключен, вы можете запросить, чтобы пользователь включил Bluetooth, не покидая приложение. Эта настройка выполняется в два этапа с использованием BluetoothAdapter .

  1. Получить BluetoothAdapter
  2. BluetoothAdapter необходим для любой активности Bluetooth. BluetoothAdapter представляет собой собственный Bluetooth-адаптер устройства (Bluetooth-радио). Для всей системы есть один адаптер Bluetooth, и ваше приложение может взаимодействовать с ним с помощью этого объекта. Ниже показан фрагмент ниже, как получить адаптер. Обратите внимание, что этот подход использует getSystemService() для возврата экземпляра BluetoothManager , который затем используется для получения адаптера. Android 4.3 (API Level 18) представляет BluetoothManager :

    частный Bluetooth-адаптер mBluetoothAdapter;

    ...

    // Инициализирует адаптер Bluetooth.

    окончательный BluetoothManager bluetoothManager =

    (BluetoothManager) getSystemService (Context.BLUETOOTH_SERVICE);

    mBluetoothAdapter = bluetoothManager.getAdapter ();

  3. Включить Bluetooth

Затем вам нужно убедиться, что Bluetooth включен. Вызовите isEnabled() чтобы проверить, включен ли Bluetooth. Если этот метод возвращает false, Bluetooth отключен. Следующий фрагмент проверяет, включен ли Bluetooth. Если это не так, в фрагменте отображается сообщение об ошибке, указывающее пользователю перейти к настройкам, чтобы включить Bluetooth:

// Обеспечивает доступность Bluetooth на устройстве, и он включен. Если не,

// отображает диалоговое окно с запросом разрешения пользователя на включение Bluetooth.

if (mBluetoothAdapter == null ||! mBluetoothAdapter.isEnabled ()) {

Intent enableBtIntent = новый Intent (BluetoothAdapter.ACTION_REQUEST_ENABLE);

startActivityForResult (enableBtIntent, REQUEST_ENABLE_BT);

}

Примечание: Константа REQUEST_ENABLE_BT передается в startActivityForResult(android.content.Intent, int) - это локально определенное целое число (которое должно быть больше 0), которое система возвращает вам в ваш onActivityResult(int, int, android.content.Intent) в качестве параметра requestCode .

Найти устройства BLE

Чтобы найти устройства BLE, вы можете использовать метод startLeScan() . Этот метод в качестве параметра принимает BluetoothAdapter.LeScanCallback. Вы должны реализовать этот обратный вызов, потому что так возвращаются результаты сканирования. Поскольку сканирование требует больших затрат энергии, вы должны соблюдать следующие рекомендации:

  • Как только вы найдете нужное устройство, прекратите сканирование.
  • Никогда не просматривайте петлю и не устанавливайте лимит времени на сканирование. Устройство, которое было ранее доступно, возможно, вышло за пределы допустимого диапазона и продолжает сканировать дренирование батареи.

Следующий фрагмент показывает, как запустить и остановить сканирование:

/ **

* Активность для сканирования и отображения доступных устройств BLE.

* /

открытый класс DeviceScanActivity расширяет ListActivity {


частный Bluetooth-адаптер mBluetoothAdapter;

private boolean mScanning;

частный обработчик mHandler;


// Остановка сканирования через 10 секунд.

частный статический конечный длинный SCAN_PERIOD = 10000;

...

private void scanLeDevice (конечное булевское разрешение) {

if (enable) {

// Остановка сканирования после заданного периода сканирования.

mHandler.postDelayed (новый Runnable () {

@Override

public void run () {

mScanning = false;

mBluetoothAdapter.stopLeScan (mLeScanCallback);

}

}, SCAN_PERIOD);


mScanning = true;

mBluetoothAdapter.startLeScan (mLeScanCallback);

} else {

mScanning = false;

mBluetoothAdapter.stopLeScan (mLeScanCallback);

}

...

}

...

}

Если вы хотите сканировать только определенные типы периферийных устройств, вы можете вместо этого вызвать startLeScan(UUID[], BluetoothAdapter.LeScanCallback) , предоставляя массив объектов UUID которые определяют службы GATT, поддерживаемые вашим приложением.

Ниже приведена реализация BluetoothAdapter.LeScanCallback , который является интерфейсом, используемым для доставки результатов сканирования BLE:

частный LeDeviceListAdapter mLeDeviceListAdapter;

...

// Обратный вызов сканирования устройства.

частный BluetoothAdapter.LeScanCallback mLeScanCallback =

новый BluetoothAdapter.LeScanCallback () {

@Override

public void onLeScan (окончательное устройство BluetoothDevice, int rssi,

byte [] scanRecord) {

runOnUiThread (новый Runnable () {

@Override

public void run () {

mLeDeviceListAdapter.addDevice (устройство);

mLeDeviceListAdapter.notifyDataSetChanged ();

}

});

}

};

Примечание. Вы можете сканировать только устройства Bluetooth LE или сканировать классические устройства Bluetooth, как описано в Bluetooth . Вы не можете одновременно сканировать как Bluetooth LE, так и классические устройства.

Подключение к серверу GATT

Первый шаг при взаимодействии с устройством BLE - это подключение к нему, в частности, подключение к серверу GATT на устройстве. Чтобы подключиться к серверу GATT на устройстве BLE, вы используете метод connectGatt() . Этот метод принимает три параметра: объект Context , autoConnect (логическое значение указывает, следует ли автоматически подключаться к устройству BLE, как только оно станет доступным) и ссылку на BluetoothGattCallback :

mBluetoothGatt = device.connectGatt (это, false, mGattCallback);

Это подключается к серверу GATT, размещенному устройством BLE, и возвращает экземпляр BluetoothGatt , который затем можно использовать для выполнения клиентских операций GATT. Вызывающим (приложение для Android) является клиент GATT. BluetoothGattCallback используется для доставки результатов клиенту, таких как состояние соединения, а также любые другие действия клиента GATT.

В этом примере приложение BLE обеспечивает активность ( DeviceControlActivity ) для подключения, отображения данных и отображения услуг и характеристик GATT, поддерживаемых устройством. Основываясь на пользовательском вводе, эта активность связывается с Service BluetoothLeService , который взаимодействует с устройством BLE через Android BLE API:

// Служба, которая взаимодействует с устройством BLE через Android BLE API.

Открытый класс BluetoothLeService расширяет службу {

private final static String TAG = BluetoothLeService.class.getSimpleName ();


частный BluetoothManager mBluetoothManager;

частный Bluetooth-адаптер mBluetoothAdapter;

private String mBluetoothDeviceAddress;

частный BluetoothGatt mBluetoothGatt;

private int mConnectionState = STATE_DISCONNECTED;


private static final int STATE_DISCONNECTED = 0;

private static final int STATE_CONNECTING = 1;

private static final int STATE_CONNECTED = 2;


public final static String ACTION_GATT_CONNECTED =

"Com.example.bluetooth.le.ACTION_GATT_CONNECTED";

public final static String ACTION_GATT_DISCONNECTED =

"Com.example.bluetooth.le.ACTION_GATT_DISCONNECTED";

public final static String ACTION_GATT_SERVICES_DISCOVERED =

"Com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED";

public final static String ACTION_DATA_AVAILABLE =

"Com.example.bluetooth.le.ACTION_DATA_AVAILABLE";

public final static String EXTRA_DATA =

"Com.example.bluetooth.le.EXTRA_DATA";


public final static UUID UUID_HEART_RATE_MEASUREMENT =

UUID.fromString (SampleGattAttributes.HEART_RATE_MEASUREMENT);


// Различные методы обратного вызова, определенные BLE API.

закрытый окончательный BluetoothGattCallback mGattCallback =

новый BluetoothGattCallback () {

@Override

public void onConnectionStateChange (BluetoothGatt gatt, int status,

int newState) {

String intentAction;

if (newState == BluetoothProfile.STATE_CONNECTED) {

intentAction = ACTION_GATT_CONNECTED;

mConnectionState = STATE_CONNECTED;

broadcastUpdate (intentAction);

Log.i (TAG, «Подключено к серверу GATT.»);

Log.i (TAG, «Попытка начать обнаружение службы:» +

mBluetoothGatt.discoverServices ());


} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {

intentAction = ACTION_GATT_DISCONNECTED;

mConnectionState = STATE_DISCONNECTED;

Log.i (TAG, «Отключено от сервера GATT.»);

broadcastUpdate (intentAction);

}

}


@Override

// Обнаружены новые службы

public void onServicesDiscovered (BluetoothGatt gatt, int status) {

if (status == BluetoothGatt.GATT_SUCCESS) {

broadcastUpdate (ACTION_GATT_SERVICES_DISCOVERED);

} else {

Log.w (TAG, "onServicesDiscovered received:" + status);

}

}


@Override

// Результат типичной операции чтения

public void onCharacteristicRead (BluetoothGatt gatt,

Характеристики BluetoothGattCharacteristic,

int status) {

if (status == BluetoothGatt.GATT_SUCCESS) {

broadcastUpdate (ACTION_DATA_AVAILABLE, характеристика);

}

}

...

};

...

}

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

private void broadcastUpdate (окончательное действие String) {

окончательное намерение намерения = новое намерение (действие);

sendBroadcast (намерение);

}


private void broadcastUpdate (final String action,

окончательная характеристика BluetoothGattCharacteristic) {

окончательное намерение намерения = новое намерение (действие);


// Это специальная обработка для профиля измерения сердечного ритма. Данные

// Разбор выполняется согласно спецификациям профиля.

if (UUID_HEART_RATE_MEASUREMENT.equals (character.getUuid ())) {

int flag = character.getProperties ();

int format = -1;

if ((flag & 0x01)! = 0) {

format = BluetoothGattCharacteristic.FORMAT_UINT16;

Log.d (TAG, «Формат частоты сердечных сокращений UINT16.»);

} else {

format = BluetoothGattCharacteristic.FORMAT_UINT8;

Log.d (TAG, «Формат частоты сердечных сокращений UINT8.»);

}

final int heartRate = character.getIntValue (формат, 1);

Log.d (TAG, String.format («Полученный сердечный ритм:% d», heartRate));

intent.putExtra (EXTRA_DATA, String.valueOf (heartRate));

} else {

// Для всех других профилей записывает данные, отформатированные в HEX.

final byte [] data = character.getValue ();

if (data! = null && data.length> 0) {

final StringBuilder stringBuilder = новый StringBuilder (data.length);

for (byte byteChar: data)

stringBuilder.append (String.format ("% 02X", byteChar));

intent.putExtra (EXTRA_DATA, новая строка (данные) + "\ n" +

stringBuilder.toString ());

}

}

sendBroadcast (намерение);

}

Назад в DeviceControlActivity , эти события обрабатываются BroadcastReceiver :

// Обрабатывает различные события, запущенные службой.

// ACTION_GATT_CONNECTED: подключен к серверу GATT.

// ACTION_GATT_DISCONNECTED: отключен от сервера GATT.

// ACTION_GATT_SERVICES_DISCOVERED: обнаружены службы GATT.

// ACTION_DATA_AVAILABLE: полученные данные с устройства. Это может быть

// результат операций чтения или уведомления.

частный окончательный BroadcastReceiver mGattUpdateReceiver = новый BroadcastReceiver () {

@Override

public void onReceive (контекстный контекст, намерение намерения) {

final String action = intent.getAction ();

if (BluetoothLeService.ACTION_GATT_CONNECTED.equals (действие)) {

mConnected = true;

updateConnectionState (R.string.connected);

invalidateOptionsMenu ();

} else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals (действие)) {

mConnected = false;

updateConnectionState (R.string.disconnected);

invalidateOptionsMenu ();

clearUI ();

} else if (BluetoothLeService.

ACTION_GATT_SERVICES_DISCOVERED.equals (действие)) {

// Показывать все поддерживаемые службы и характеристики на

// пользовательский интерфейс.

displayGattServices (mBluetoothLeService.getSupportedGattServices ());

} else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals (действие)) {

displayData (intent.getStringExtra (BluetoothLeService.EXTRA_DATA));

}

}

};

Чтение атрибутов BLE

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

Открытый класс DeviceControlActivity расширяет действие {

...

// Демонстрирует, как итерации через поддерживаемый GATT

// Услуги / Характеристики.

// В этом примере мы заполняем структуру данных, привязанную к

// ExpandableListView в пользовательском интерфейсе.

private void displayGattServices (Список <BluetoothGattService> gattServices) {

if (gattServices == null) return;

String uuid = null;

Строка unknownServiceString = getResources ().

GetString (R.string.unknown_service);

Строка unknownCharaString = getResources ().

GetString (R.string.unknown_characteristic);

ArrayList <HashMap <String, String >> gattServiceData =

new ArrayList <HashMap <String, String >> ();

ArrayList <ArrayList <HashMap <String, String >>> gattCharacteristicData

= new ArrayList <ArrayList <HashMap <String, String >>> ();

mGattCharacteristics =

новый ArrayList <ArrayList <BluetoothGattCharacteristic >> ();


// Петли через доступные службы GATT.

для (BluetoothGattService gattService: gattServices) {

HashMap <String, String> currentServiceData =

новый HashMap <String, String> ();

uuid = gattService.getUuid (). toString ();

currentServiceData.put (

LIST_NAME, SampleGattAttributes.

lookup (uuid, unknownServiceString));

currentServiceData.put (LIST_UUID, uuid);

gattServiceData.add (currentServiceData);


ArrayList <HashMap <String, String >> gattCharacteristicGroupData =

new ArrayList <HashMap <String, String >> ();

Список <BluetoothGattCharacteristic> gattCharacteristics =

gattService.getCharacteristics ();

ArrayList <BluetoothGattCharacteristic> charas =

новый ArrayList <BluetoothGattCharacteristic> ();

// Петли через доступные характеристики.

для (BluetoothGattCharacteristic gattCharacteristic:

gattCharacteristics) {

charas.add (gattCharacteristic);

HashMap <String, String> currentCharaData =

новый HashMap <String, String> ();

uuid = gattCharacteristic.getUuid (). toString ();

currentCharaData.put (

LIST_NAME, SampleGattAttributes.lookup (uuid,

unknownCharaString));

currentCharaData.put (LIST_UUID, uuid);

gattCharacteristicGroupData.add (currentCharaData);

}

mGattCharacteristics.add (чарас);

gattCharacteristicData.add (gattCharacteristicGroupData);

}

...

}

...

}

Получать уведомления GATT

Обычно приложения BLE просят уведомить вас, когда на устройстве изменяется конкретный характер. Этот фрагмент показывает, как установить уведомление для характеристики, используя метод setCharacteristicNotification() :

частный BluetoothGatt mBluetoothGatt;

Характеристики BluetoothGattCharacteristic;

boolean enabled;

...

mBluetoothGatt.setCharacteristicNotification (характеристика, включена);

...

Дескриптор BluetoothGattDescriptor = character.getDescriptor (

UUID.fromString (SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));

descriptor.setValue (BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);

mBluetoothGatt.writeDescriptor (дескриптор);

После включения уведомлений для характеристики onCharacteristicChanged() обратный вызов onCharacteristicChanged() , если характерные изменения на удаленном устройстве:

@Override

// Уведомление о характеристиках

public void onCharacteristicChanged (BluetoothGatt gatt,

Характеристика BluetoothGattCharacteristic) {

broadcastUpdate (ACTION_DATA_AVAILABLE, характеристика);

}

Закройте клиентское приложение

Как только ваше приложение закончит использовать устройство BLE, оно должно вызвать функцию close() чтобы система могла соответствующим образом выделять ресурсы:

public void close () {

if (mBluetoothGatt == null) {

вернуть;

}

mBluetoothGatt.close ();

mBluetoothGatt = null;

}


 
MyTetra Share v.0.52
Яндекс индекс цитирования