Android Bluetooth Hemat Energi (BLE) - Memasak dengan Benar, Bagian # 1

Tahun lalu saya mengembangkan aplikasi Bluetooth Low Energy ( BLE ) untuk iOS dan ternyata cukup sederhana. Lalu ada porting ke Android ... betapa sulitnya itu?





Saya dapat mengatakan dengan pasti - itu lebih sulit daripada yang saya bayangkan, saya harus berusaha keras untuk operasi yang stabil di bawah Android. Saya mempelajari banyak artikel di domain publik, beberapa ternyata salah, banyak yang sangat berguna dan membantu dalam masalah ini. Dalam rangkaian artikel ini, saya ingin menjelaskan temuan saya sehingga Anda tidak membuang banyak waktu untuk mencari seperti yang saya lakukan.





Fitur BLE untuk Android

  • Dokumentasi Google tentang BLE sangat umum , dalam beberapa kasus informasi penting hilang atau ketinggalan zaman, aplikasi contoh tidak menunjukkan cara menggunakan BLE dengan benar. Saya hanya menemukan beberapa sumber tentang bagaimana melakukan BLE dengan benar. Presentasi Stuart Kent  memberikan bahan awal yang sangat baik. Untuk beberapa topik lanjutan, ada artikel Nordik yang bagus  .





  • Android BLE API adalah operasi tingkat rendah , dalam aplikasi nyata Anda perlu menggunakan beberapa lapisan abstraksi (seperti yang dilakukan di luar kotak di iOS-CoreBluetooth). Biasanya Anda perlu melakukannya sendiri: antrian perintah, pengikatan, pemeliharaan koneksi, penanganan kesalahan dan bug, akses multithread. Yang paling terkenal adalah perpustakaan  SweetBlueRxAndroidBle  dan  Nordic . Menurut saya yang paling mudah dipelajari adalah Nordik,  lihat detailnya di sini .





  • Android BLE   . . , ! , Samsung Google!





  • Android ( )   , 4,5 6. , , 133. .





, , ยซยป . .





.  BluetoothLeScanner



:





BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
BluetoothLeScanner scanner = adapter.getBluetoothLeScanner();

if (scanner != null) {
    scanner.startScan(filters, scanSettings, scanCallback);
    Log.d(TAG, "scan started");
}  else {
    Log.e(TAG, "could not get scanner object");
}
      
      



  filters



  scanSettings



,  scanCallback



:





private final ScanCallback scanCallback = new ScanCallback() {
    @Override
    public void onScanResult(int callbackType, ScanResult result) {
        BluetoothDevice device = result.getDevice();
        // ...do whatever you want with this found device
    }

    @Override
    public void onBatchScanResults(List<ScanResult> results) {
        // Ignore for now
    }

    @Override
    public void onScanFailed(int errorCode) {
        // Ignore for now
    }
};
      
      



 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 BLP_SERVICE_UUID = UUID.fromString("00001810-0000-1000-8000-00805f9b34fb");
UUID[] serviceUUIDs = new UUID[]{BLP_SERVICE_UUID};
List<ScanFilter> filters = null;
if(serviceUUIDs != null) {
    filters = new ArrayList<>();
    for (UUID serviceUUID : serviceUUIDs) {
        ScanFilter filter = new ScanFilter.Builder()
                .setServiceUuid(new ParcelUuid(serviceUUID))
                .build();
        filters.add(filter);
    }
}
scanner.startScan(filters, scanSettings, scanCallback);
      
      



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



    .    :





String[] names = new String[]{"Polar H7 391BB014"};
List<ScanFilter> filters = null;
if(names != null) {
    filters = new ArrayList<>();
    for (String name : names) {
        ScanFilter filter = new ScanFilter.Builder()
                .setDeviceName(name)
                .build();
        filters.add(filter);
    }
}
scanner.startScan(filters, scanSettings, scanCallback);
      
      



MAC-.

   . MAC- , , , . , , Bluetooth.





String[] peripheralAddresses = new String[]{"01:0A:5C:7D:D0:1A"};
// Build filters list
List<ScanFilter> filters = null;
if (peripheralAddresses != null) {
    filters = new ArrayList<>();
    for (String address : peripheralAddresses) {
        ScanFilter filter = new ScanFilter.Builder()
                .setDeviceAddress(address)
                .build();
        filters.add(filter);
    }
}
scanner.startScan(filters, scanSettings, scanByServiceUUIDCallback);
      
      



, UUID, MAC- . , . .





ScanSettings

ScanSettings



Android . , , :





ScanSettings scanSettings = new ScanSettings.Builder()
        .setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
        .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
        .setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE)
        .setNumOfMatches(ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT)
        .setReportDelay(0L)
        .build();
      
      



ScanMode

, . Bluetooth . , . 4 ,  Nordics :





  1. SCAN_MODE_LOW_POWER



    . Android 0.5, 4.5. , advertisement .





  2. SCAN_MODE_BALANCED



    . : 2, : 3, ยซยป .





  3. SCAN_MODE_LOW_LATENCY



    . , Android , , . . .





  4. SCAN_MODE_OPPORTUNISTIC



    . , ! , , . Android , (. ยซ ยป).





Callback Type

callback  ScanResult



  , 3 :





  1. CALLBACK_TYPE_ALL_MATCHES



    . Callback , advertisement . - 200-500 allback, advertisement .





  2. CALLBACK_TYPE_FIRST_MATCH



    . Callback , advertisement .





  3. 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 ยซยป.





  1. MATCH_MODE_AGGRESSIVE



    . advertisement .





  2. MATCH_MODE_STICKY



    . , advertisement .





,  MATCH_MODE_AGGRESSIVE



, .





Number of matches

advertisement .





  1. MATCH_NUM_ONE_ADVERTISEMENT



    . .





  2. MATCH_NUM_FEW_ADVERTISEMENT



    . .





  3. 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 , :





  1. Bluetooth









  2. ( )





, , - . , Samsung, Bluetooth.





, BT . , :





// Get device object for a mac address
BluetoothDevice device = bluetoothAdapter.getRemoteDevice(peripheralAddress)
// Check if the peripheral is cached or not
int deviceType = device.getType();
if(deviceType == BluetoothDevice.DEVICE_TYPE_UNKNOWN) {
    // The peripheral is not cached
} else {
    // The peripheral is cached
}
      
      



, , . .





?

โ€“ , , , . , 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 , . :





  •  StackOverflow





  •  David Young





(permissions)

, . (permissions):





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



, , .  ACCESS_COARSE_LOCATION



 Google ยซยป .





private boolean hasPermissions() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if (getApplicationContext().checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            requestPermissions(new String[] { Manifest.permission.ACCESS_COARSE_LOCATION }, ACCESS_COARSE_LOCATION_REQUEST);
            return false;
        }
    }
    return true;
}
      
      



. , BLE 2 : ACCESS_FINE_LOCATION



 ( API<23)  ACCESS_BACKGROUND_LOCATION



  Stackoverflow.





Android10:





<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
      
      



, Bluetooth, -  Intent



  :





BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (!bluetoothAdapter.isEnabled()) {
    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
      
      







BLE Activity (Fragment / Service), , (permissions) Android-Bluetooth . .





!








All Articles