نقل بيانات BLE

بعد الاتصال بـ BLE GATT الخادم، يمكنك استخدام الاتصال لمعرفة الخدمات المتاحة على الجهاز، والاستعلام عن البيانات من الجهاز وطلب الإشعارات عند وجود إحدى خصائص GATT التغييرات.

الاطّلاع على الخدمات

أول شيء يجب فعله عند الاتصال بخادم GATT على جهاز BLE هو لاكتشاف الخدمة. تقدّم هذه الصفحة معلومات عن الخدمات. المتاحة على الجهاز البعيد بالإضافة إلى خصائص الخدمة واصفات البيانات. وفي المثال التالي، بعد اتصال الخدمة بنجاح الجهاز (يشار إليه من خلال الاتصال المناسب onConnectionStateChange() دالة BluetoothGattCallback), الـ discoverServices() تستعلم الدالة من المعلومات من جهاز BLE.

تحتاج الخدمة إلى إلغاء onServicesDiscovered() الوظيفة في BluetoothGattCallback ويتم استدعاء هذه الدالة عندما يرسل الجهاز تقريرًا عن خدماته المتاحة.

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);             }         }     }; } 

تستخدم الخدمة عمليات البث لإشعار الأخرى. وبعد أن يتم اكتشاف الخدمات، يمكن للخدمة الاتصال getServices() إلى الحصول على البيانات التي تم الإبلاغ عنها.

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();     } } 

حينئذ يمكن للنشاط استدعاء هذه الدالة عندما يتلقى قصد البث، إلى أن اكتشاف الخدمة قد اكتمل.

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());             }         }     }; } 

قراءة خصائص BLE

بعد اتصال تطبيقك بخادم GATT واكتشاف الخدمات، يمكنهم قراءة السمات وكتابتها، عندما يكون ذلك متاحًا. على سبيل المثال، ما يلي المقتطف من خلال خدمات الخادم وخصائصه وعروضه في واجهة المستخدم:

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);          }     ...     } ... } 

تقدم خدمة GATT قائمة بالخصائص التي يمكنك قراءتها من الخاص بك. للاستعلام عن البيانات، استدعِ readCharacteristic() في BluetoothGatt، مرورًا في BluetoothGattCharacteristic التي تريد قراءتها.

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);     } } 

في هذا المثال، تنفذ الخدمة دالة لاستدعاء readCharacteristic() هذه مكالمة غير متزامنة. يتم إرسال النتائج إلى BluetoothGattCallback الوظيفة 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);             }         }     }; } 

عند تشغيل رد اتصال معين، فإنه يستدعي الأمر broadcastUpdate() كمساعد وتمريره إجراءً. لاحظ أن البيانات يتم إجراء التحليل في هذا القسم بالتوافق مع معدل ضربات القلب عن طريق البلوتوث مواصفات ملف القياس

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); } 

تلقّي إشعارات GATT

من الشائع أن تطلب تطبيقات البلوتوث منخفض الطاقة (BLE) أن يتم إشعارك عند توفّر خاصية معيّنة. التغييرات على الجهاز. في المثال التالي، تنفِّذ الخدمة لاستدعاء دالة setCharacteristicNotification() :

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);         }     } } 

عند تمكين الإشعارات لإحدى الخصائص، onCharacteristicChanged() يتم تفعيل هذه الميزة في حال تغيّرت الخاصية على الجهاز البعيد:

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);         }     }; }