Chuyển dữ liệu BLE

Sau khi bạn kết nối với BLE GATT , bạn có thể sử dụng kết nối để tìm hiểu xem những dịch vụ nào dùng được trên thiết bị, truy vấn dữ liệu từ thiết bị rồi yêu cầu thông báo khi một đặc điểm GATT nhất định thay đổi.

Khám phá các dịch vụ

Việc đầu tiên cần làm sau khi bạn kết nối với Máy chủ GATT trên thiết bị BLE là để khám phá dịch vụ. Trang này cung cấp thông tin về các dịch vụ có trên thiết bị từ xa cũng như các đặc điểm dịch vụ và mã mô tả. Trong ví dụ sau, sau khi dịch vụ kết nối thành công với thiết bị (được biểu thị bằng lệnh gọi thích hợp đến onConnectionStateChange() của BluetoothGattCallback), thời gian discoverServices() hàm truy vấn thông tin từ thiết bị BLE.

Dịch vụ cần ghi đè onServicesDiscovered() trong BluetoothGattCallback. Hàm này được gọi khi thiết bị báo cáo về các dịch vụ hiện có.

Kotlin

 class BluetoothLeService : Service() {  ...  private val bluetoothGattCallback = object : BluetoothGattCallback() {     override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) {         if (newState == BluetoothProfile.STATE_CONNECTED) {             // successfully connected to the GATT Server             broadcastUpdate(ACTION_GATT_CONNECTED)             connectionState = STATE_CONNECTED             // Attempts to discover services after successful connection.             bluetoothGatt?.discoverServices()         } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {             // disconnected from the GATT Server             broadcastUpdate(ACTION_GATT_DISCONNECTED)             connectionState = STATE_DISCONNECTED         }     }      override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) {         if (status == BluetoothGatt.GATT_SUCCESS) {             broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED)         } else {             Log.w(BluetoothLeService.TAG, "onServicesDiscovered received: $status")         }     } }  ...  companion object {   const val ACTION_GATT_CONNECTED = "com.example.bluetooth.le.ACTION_GATT_CONNECTED"   const val ACTION_GATT_DISCONNECTED =               "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED"   const val ACTION_GATT_SERVICES_DISCOVERED =               "com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED"    private const val STATE_DISCONNECTED = 0   private const val STATE_CONNECTED = 2 } 

Java

 class BluetoothLeService extends Service {      public final static String ACTION_GATT_SERVICES_DISCOVERED =             "com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED";      ...      private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {         @Override         public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {             if (newState == BluetoothProfile.STATE_CONNECTED) {                 // successfully connected to the GATT Server                 connectionState = STATE_CONNECTED;                 broadcastUpdate(ACTION_GATT_CONNECTED);                 // Attempts to discover services after successful connection.                 bluetoothGatt.discoverServices();             } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {                 // disconnected from the GATT Server                 connectionState = STATE_DISCONNECTED;                 broadcastUpdate(ACTION_GATT_DISCONNECTED);             }         }          @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);             }         }     }; } 

Dịch vụ này sử dụng thông báo truyền tin để thông báo của bạn. Sau khi phát hiện dịch vụ, dịch vụ có thể gọi getServices() đến nhận dữ liệu được báo cáo.

Kotlin

 class BluetoothLeService : Service() {  ...    fun getSupportedGattServices(): List<BluetoothGattService?>? {       return bluetoothGatt?.services   } } 

Java

 class BluetoothLeService extends Service {  ...      public List<BluetoothGattService> getSupportedGattServices() {         if (bluetoothGatt == null) return null;         return bluetoothGatt.getServices();     } } 

Sau đó, hoạt động có thể gọi hàm này khi nhận được ý định truyền tin, cho biết rằng quá trình khám phá dịch vụ đã hoàn tất.

Kotlin

 class DeviceControlActivity : AppCompatActivity() {  ...      private val gattUpdateReceiver: BroadcastReceiver = object : BroadcastReceiver() {         override fun onReceive(context: Context, intent: Intent) {             when (intent.action) {                 BluetoothLeService.ACTION_GATT_CONNECTED -> {                     connected = true                     updateConnectionState(R.string.connected)                 }                 BluetoothLeService.ACTION_GATT_DISCONNECTED -> {                     connected = false                     updateConnectionState(R.string.disconnected)                         }                 BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED -> {                     // Show all the supported services and characteristics on the user interface.                     displayGattServices(bluetoothService?.getSupportedGattServices())                 }             }         }     } } 

Java

 class DeviceControlsActivity extends AppCompatActivity {  ...      private final BroadcastReceiver gattUpdateReceiver = new BroadcastReceiver() {         @Override         public void onReceive(Context context, Intent intent) {             final String action = intent.getAction();             if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {                 connected = true;                 updateConnectionState(R.string.connected);             } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {                 connected = false;                 updateConnectionState(R.string.disconnected);             } else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {                 // Show all the supported services and characteristics on the user interface.                 displayGattServices(bluetoothService.getSupportedGattServices());             }         }     }; } 

Đọc đặc điểm BLE

Sau khi ứng dụng của bạn kết nối với máy chủ GATT và phát hiện ra các dịch vụ, có thể đọc và ghi các thuộc tính (nếu được hỗ trợ). Ví dụ: như sau đoạn mã lặp lại thông qua các dịch vụ, đặc điểm và màn hình của máy chủ trong giao diện người dùng:

Kotlin

 class DeviceControlActivity : Activity() {      // Demonstrates how to iterate through the supported GATT     // Services/Characteristics.     // In this sample, we populate the data structure that is bound to the     // ExpandableListView on the UI.     private fun displayGattServices(gattServices: List<BluetoothGattService>?) {         if (gattServices == null) return         var uuid: String?         val unknownServiceString: String = resources.getString(R.string.unknown_service)         val unknownCharaString: String = resources.getString(R.string.unknown_characteristic)         val gattServiceData: MutableList<HashMap<String, String>> = mutableListOf()         val gattCharacteristicData: MutableList<ArrayList<HashMap<String, String>>> =                 mutableListOf()         mGattCharacteristics = mutableListOf()          // Loops through available GATT Services.         gattServices.forEach { gattService ->             val currentServiceData = HashMap<String, String>()             uuid = gattService.uuid.toString()             currentServiceData[LIST_NAME] = SampleGattAttributes.lookup(uuid, unknownServiceString)             currentServiceData[LIST_UUID] = uuid             gattServiceData += currentServiceData              val gattCharacteristicGroupData: ArrayList<HashMap<String, String>> = arrayListOf()             val gattCharacteristics = gattService.characteristics             val charas: MutableList<BluetoothGattCharacteristic> = mutableListOf()              // Loops through available Characteristics.             gattCharacteristics.forEach { gattCharacteristic ->                 charas += gattCharacteristic                 val currentCharaData: HashMap<String, String> = hashMapOf()                 uuid = gattCharacteristic.uuid.toString()                 currentCharaData[LIST_NAME] = SampleGattAttributes.lookup(uuid, unknownCharaString)                 currentCharaData[LIST_UUID] = uuid                 gattCharacteristicGroupData += currentCharaData             }             mGattCharacteristics += charas             gattCharacteristicData += gattCharacteristicGroupData         }     } } 

Java

 public class DeviceControlActivity extends Activity {     ...     // Demonstrates how to iterate through the supported GATT     // Services/Characteristics.     // In this sample, we populate the data structure that is bound to the     // ExpandableListView on the UI.     private void displayGattServices(List<BluetoothGattService> gattServices) {         if (gattServices == null) return;         String uuid = null;         String unknownServiceString = getResources().                 getString(R.string.unknown_service);         String 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 =                 new ArrayList<ArrayList<BluetoothGattCharacteristic>>();          // Loops through available GATT Services.         for (BluetoothGattService gattService : gattServices) {             HashMap<String, String> currentServiceData =                     new 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>>();             List<BluetoothGattCharacteristic> gattCharacteristics =                     gattService.getCharacteristics();             ArrayList<BluetoothGattCharacteristic> charas =                     new ArrayList<BluetoothGattCharacteristic>();            // Loops through available Characteristics.             for (BluetoothGattCharacteristic gattCharacteristic :                     gattCharacteristics) {                 charas.add(gattCharacteristic);                 HashMap<String, String> currentCharaData =                         new 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(charas);             gattCharacteristicData.add(gattCharacteristicGroupData);          }     ...     } ... } 

Dịch vụ GATT cung cấp một danh sách các đặc điểm mà bạn có thể đọc được từ thiết bị. Để truy vấn dữ liệu, hãy gọi phương thức readCharacteristic() trên BluetoothGatt, truyền vào giá trị BluetoothGattCharacteristic mà bạn muốn đọc.

Kotlin

 class BluetoothLeService : Service() {  ...      fun readCharacteristic(characteristic: BluetoothGattCharacteristic) {         bluetoothGatt?.let { gatt ->             gatt.readCharacteristic(characteristic)         } ?: run {             Log.w(TAG, "BluetoothGatt not initialized")             Return         }     } } 

Java

 class BluetoothLeService extends Service {  ...      public void readCharacteristic(BluetoothGattCharacteristic characteristic) {         if (bluetoothGatt == null) {             Log.w(TAG, "BluetoothGatt not initialized");             return;         }         bluetoothGatt.readCharacteristic(characteristic);     } } 

Trong ví dụ này, dịch vụ sẽ triển khai một hàm để gọi readCharacteristic(). Đây là lệnh gọi không đồng bộ. Kết quả được gửi đến BluetoothGattCallback hàm onCharacteristicRead().

Kotlin

 class BluetoothLeService : Service() {  ...      private val bluetoothGattCallback = object : BluetoothGattCallback() {          ...          override fun onCharacteristicRead(             gatt: BluetoothGatt,             characteristic: BluetoothGattCharacteristic,             status: Int             ) {                 if (status == BluetoothGatt.GATT_SUCCESS) {                 broadcastUpdate(BluetoothLeService.ACTION_DATA_AVAILABLE, characteristic)             }         }     } } 

Java

 class BluetoothLeService extends Service {  ...      private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {      ...          @Override         public void onCharacteristicRead(         BluetoothGatt gatt,         BluetoothGattCharacteristic characteristic,         int status         ) {             if (status == BluetoothGatt.GATT_SUCCESS) {                 broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);             }         }     }; } 

Khi một lệnh gọi lại cụ thể được kích hoạt, lệnh gọi lại thích hợp broadcastUpdate() và truyền cho phương thức trợ giúp đó một hành động. Xin lưu ý rằng dữ liệu quá trình phân tích cú pháp trong phần này được thực hiện theo Nhịp tim Bluetooth Quy cách của hồ sơ đo lường.

Kotlin

 private fun broadcastUpdate(action: String, characteristic: BluetoothGattCharacteristic) {     val intent = Intent(action)      // This is special handling for the Heart Rate Measurement profile. Data     // parsing is carried out as per profile specifications.     when (characteristic.uuid) {         UUID_HEART_RATE_MEASUREMENT -> {             val flag = characteristic.properties             val format = when (flag and 0x01) {                 0x01 -> {                     Log.d(TAG, "Heart rate format UINT16.")                     BluetoothGattCharacteristic.FORMAT_UINT16                 }                 else -> {                     Log.d(TAG, "Heart rate format UINT8.")                     BluetoothGattCharacteristic.FORMAT_UINT8                 }             }             val heartRate = characteristic.getIntValue(format, 1)             Log.d(TAG, String.format("Received heart rate: %d", heartRate))             intent.putExtra(EXTRA_DATA, (heartRate).toString())         }         else -> {             // For all other profiles, writes the data formatted in HEX.             val data: ByteArray? = characteristic.value             if (data?.isNotEmpty() == true) {                 val hexString: String = data.joinToString(separator = " ") {                     String.format("%02X", it)                 }                 intent.putExtra(EXTRA_DATA, "$data\n$hexString")             }         }     }     sendBroadcast(intent) } 

Java

 private void broadcastUpdate(final String action,                              final BluetoothGattCharacteristic characteristic) {     final Intent intent = new Intent(action);      // This is special handling for the Heart Rate Measurement profile. Data     // parsing is carried out as per profile specifications.     if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {         int flag = characteristic.getProperties();         int format = -1;         if ((flag & 0x01) != 0) {             format = BluetoothGattCharacteristic.FORMAT_UINT16;             Log.d(TAG, "Heart rate format UINT16.");         } else {             format = BluetoothGattCharacteristic.FORMAT_UINT8;             Log.d(TAG, "Heart rate format UINT8.");         }         final int heartRate = characteristic.getIntValue(format, 1);         Log.d(TAG, String.format("Received heart rate: %d", heartRate));         intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));     } else {         // For all other profiles, writes the data formatted in HEX.         final byte[] data = characteristic.getValue();         if (data != null && data.length > 0) {             final StringBuilder stringBuilder = new StringBuilder(data.length);             for(byte byteChar : data)                 stringBuilder.append(String.format("%02X ", byteChar));             intent.putExtra(EXTRA_DATA, new String(data) + "\n" +                     stringBuilder.toString());         }     }     sendBroadcast(intent); } 

Nhận thông báo về GATT

Thông thường, các ứng dụng BLE sẽ yêu cầu được thông báo khi có một đặc điểm cụ thể các thay đổi trên thiết bị. Trong ví dụ sau, dịch vụ triển khai một để gọi phương thức setCharacteristicNotification() phương thức:

Kotlin

 class BluetoothLeService : Service() {  ...      fun setCharacteristicNotification(     characteristic: BluetoothGattCharacteristic,     enabled: Boolean     ) {         bluetoothGatt?.let { gatt ->         gatt.setCharacteristicNotification(characteristic, enabled)          // This is specific to Heart Rate Measurement.         if (BluetoothLeService.UUID_HEART_RATE_MEASUREMENT == characteristic.uuid) {             val descriptor = characteristic.getDescriptor(UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG))             descriptor.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE             gatt.writeDescriptor(descriptor)         }         } ?: run {             Log.w(BluetoothLeService.TAG, "BluetoothGatt not initialized")         }     } } 

Java

 class BluetoothLeService extends Service {  ...      public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,boolean enabled) {         if (bluetoothGatt == null) {             Log.w(TAG, "BluetoothGatt not initialized");             Return;         }         bluetoothGatt.setCharacteristicNotification(characteristic, enabled);          // This is specific to Heart Rate Measurement.         if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {             BluetoothGattDescriptor descriptor = characteristic.getDescriptor(UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));             descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);             bluetoothGatt.writeDescriptor(descriptor);         }     } } 

Sau khi bật thông báo cho một đặc điểm, onCharacteristicChanged() lệnh gọi lại được kích hoạt nếu đặc điểm thay đổi trên thiết bị từ xa:

Kotlin

 class BluetoothLeService : Service() {  ...      private val bluetoothGattCallback = object : BluetoothGattCallback() {         ...          override fun onCharacteristicChanged(         gatt: BluetoothGatt,         characteristic: BluetoothGattCharacteristic         ) {             broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic)         }     } } 

Java

 class BluetoothLeService extends Service {  ...      private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {     ...          @Override         public void onCharacteristicChanged(         BluetoothGatt gatt,         BluetoothGattCharacteristic characteristic         ) {             broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);         }     }; }