BLE ডেটা স্থানান্তর করুন

একবার আপনি একটি BLE GATT সার্ভারের সাথে সংযুক্ত হয়ে গেলে, আপনি ডিভাইসে কোন পরিষেবা পাওয়া যায় তা খুঁজে বের করতে, ডিভাইস থেকে ডেটা অনুসন্ধান করতে এবং একটি নির্দিষ্ট GATT বৈশিষ্ট্য পরিবর্তন হলে বিজ্ঞপ্তির অনুরোধ করতে সংযোগটি ব্যবহার করতে পারেন।

পরিষেবাগুলি আবিষ্কার করুন

আপনি একবার BLE ডিভাইসে GATT সার্ভারের সাথে সংযোগ করার পরে প্রথম কাজটি করতে হবে পরিষেবা আবিষ্কার করা। এটি দূরবর্তী ডিভাইসে উপলব্ধ পরিষেবাগুলির পাশাপাশি পরিষেবার বৈশিষ্ট্য এবং তাদের বর্ণনাকারী সম্পর্কে তথ্য প্রদান করে৷ নিম্নলিখিত উদাহরণে, একবার পরিষেবাটি সফলভাবে ডিভাইসের সাথে সংযোগ করলে ( BluetoothGattCallback onConnectionStateChange() ফাংশনে উপযুক্ত কল দ্বারা নির্দেশিত), discoverServices() ফাংশন BLE ডিভাইস থেকে তথ্য অনুসন্ধান করে।

পরিষেবাটিকে BluetoothGattCallbackonServicesDiscovered() ফাংশন ওভাররাইড করতে হবে। যখন ডিভাইসটি তার উপলব্ধ পরিষেবাগুলির বিষয়ে রিপোর্ট করে তখন এই ফাংশনটি বলা হয়৷

কোটলিন জাভা
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
}
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() কল করতে পারে৷

কোটলিন জাভা
class BluetoothLeService : Service() {

...

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

...

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

ক্রিয়াকলাপটি তখন এই ফাংশনটিকে কল করতে পারে যখন এটি সম্প্রচারের অভিপ্রায় গ্রহণ করে, এটি নির্দেশ করে যে পরিষেবা আবিষ্কার শেষ হয়েছে৷

কোটলিন জাভা
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())
               
}
           
}
       
}
   
}
}
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 সার্ভারের সাথে সংযুক্ত হয়ে গেলে এবং পরিষেবাগুলি আবিষ্কার করলে, এটি সমর্থিত বৈশিষ্ট্যগুলি পড়তে এবং লিখতে পারে। উদাহরণস্বরূপ, নিম্নলিখিত স্নিপেট সার্ভারের পরিষেবা এবং বৈশিষ্ট্যগুলির মাধ্যমে পুনরাবৃত্তি করে এবং সেগুলিকে UI-তে প্রদর্শন করে:

কোটলিন জাভা
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
       
}
   
}
}
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 পরিষেবা বৈশিষ্ট্যগুলির একটি তালিকা প্রদান করে যা আপনি ডিভাইস থেকে পড়তে পারেন৷ ডেটা জিজ্ঞাসা করার জন্য, BluetoothGattreadCharacteristic() ফাংশনটিকে কল করুন, আপনি যে BluetoothGattCharacteristic পড়তে চান তাতে পাস করুন৷

কোটলিন জাভা
class BluetoothLeService : Service() {

...

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

...

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

এই উদাহরণে, পরিষেবাটি readCharacteristic() কল করার জন্য একটি ফাংশন প্রয়োগ করে। এটি একটি অ্যাসিঙ্ক্রোনাস কল। ফলাফলগুলি onCharacteristicRead() এর BluetoothGattCallback ফাংশনে পাঠানো হয়।

কোটলিন জাভা
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)
           
}
       
}
   
}
}
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() সহায়ক পদ্ধতিকে কল করে এবং এটিকে একটি ক্রিয়া পাস করে। মনে রাখবেন যে এই বিভাগে ডেটা পার্সিং করা হয় ব্লুটুথ হার্ট রেট পরিমাপের প্রোফাইল স্পেসিফিকেশন অনুযায়ী।

কোটলিন জাভা
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)
}
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 বিজ্ঞপ্তি পান

ডিভাইসে একটি নির্দিষ্ট বৈশিষ্ট্য পরিবর্তন হলে বিএলই অ্যাপের জন্য বিজ্ঞাপিত হওয়ার জন্য বলা সাধারণ। নিম্নলিখিত উদাহরণে, পরিষেবাটি setCharacteristicNotification() পদ্ধতিতে কল করার জন্য একটি ফাংশন প্রয়োগ করে:

কোটলিন জাভা
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")
       
}
   
}
}

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() কলব্যাক ট্রিগার হয়:

কোটলিন জাভা
class BluetoothLeService : Service() {

...

   
private val bluetoothGattCallback = object : BluetoothGattCallback() {
       
...

       
override fun onCharacteristicChanged(
        gatt
: BluetoothGatt,
        characteristic
: BluetoothGattCharacteristic
       
) {
            broadcastUpdate
(ACTION_DATA_AVAILABLE, characteristic)
       
}
   
}
}
class BluetoothLeService extends Service {

...

   
private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {
   
...

       
@Override
       
public void onCharacteristicChanged(
       
BluetoothGatt gatt,
       
BluetoothGattCharacteristic characteristic
       
) {
            broadcastUpdate
(ACTION_DATA_AVAILABLE, characteristic);
       
}
   
};
}