Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Connecting the Bean: the bumpy road of BLE on Android (lite)

Connecting the Bean: the bumpy road of BLE on Android (lite)

Abbreviated version of my bluetooth talk as seen at Droidcon DE.

Hugo Visser

June 05, 2015
Tweet

More Decks by Hugo Visser

Other Decks in Technology

Transcript

  1. Connecting the bean: The bumpy road of BLE on Android

    Hugo Visser Little Robots source: https://www.flickr.com/photos/klaasjan/6295632164
  2. The bumpy road of BLE on Android LightBlue Bean 3

    Prototyping Arduino + BLE Accelerometer, LED, temperature Coin cell battery punchthrough.com/bean
  3. The bumpy road of BLE on Android LightBlue Bean 3

    Prototyping Arduino + BLE Accelerometer, LED, temperature Coin cell battery punchthrough.com/bean
  4. The bumpy road of BLE on Android LightBlue Bean Unofficial

    Android SDK est. July 2014 bitbucket.org/littlerobots/beanlib MIT License 4
  5. The bumpy road of BLE on Android LightBlue Bean Unofficial

    Android SDK est. July 2014 bitbucket.org/littlerobots/beanlib MIT License 4
  6. The bumpy road of BLE on Android 5 Android BLE

    Platform API since Android 4.3 (API 18) Part of bluedroid BT Classic + BLE support
  7. The bumpy road of BLE on Android 6 First steps

    Declare permissions in AndroidManifest.xml <uses-permission android:name="android.permission.BLUETOOTH"/> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/> <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/> Require Bluetooth BLE if essential to the app
  8. The bumpy road of BLE on Android 7 First steps

    Check for BLE support if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) { // awww snap :( No LE support }
  9. The bumpy road of BLE on Android 8 First steps

    Get the Bluetooth Adapter BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); mBluetoothAdapter = bluetoothManager.getAdapter(); (but…this also works) mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
  10. The bumpy road of BLE on Android 9 First steps

    if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) { Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT); } Check that bluetooth is enabled
  11. The bumpy road of BLE on Android Find me a

    device BluetoothAdapter. startLeScan() and LeScanCallback 10 public boolean startLeScan (BluetoothAdapter.LeScanCallback callback)
  12. The bumpy road of BLE on Android Small bump #1

    What does false mean? Most likely: Bluetooth is disabled (but you checked) Or…infamous bugs (check logcat) 11
  13. The bumpy road of BLE on Android Small bump #2

    How many callbacks per device? a) One b) Many c) It depends 12
  14. The bumpy road of BLE on Android Small bump #2

    How many callbacks per device? a) One b) Many c) It depends 13
  15. The bumpy road of BLE on Android Find me type

    of device public boolean startLeScan (UUID[] serviceUuids, BluetoothAdapter.LeScanCallback callback) Does not work with 128 bit UUIDs Solution: DIY filtering stackoverflow.com/questions/18019161/startlescan-with-128-bit- uuids-doesnt-work-on-native-android-ble-implementation 14
  16. The bumpy road of BLE on Android BluetoothLeScanner Filtering Batching

    of results* Power modes* ParcelUuid uuid = ParcelUuid.fromString("a495ff10-c5b1-4b44-b512-1370f02d74de"); BluetoothManager bm = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE); BluetoothAdapter adapter = bm.getAdapter(); ScanFilter scanFilter = new Builder().setServiceUuid(uuid).build(); ScanSettings settings = new ScanSettings.Builder().build(); * this asterix might spoil the party 16
  17. The bumpy road of BLE on Android BluetoothLeScanner adapter.getBluetoothLeScanner().startScan(singletonList(scanFilter), settings,

    new ScanCallback() { @Override public void onScanResult(int callbackType, ScanResult result) { // process the result, there's currently only one callbackType defined } @Override public void onBatchScanResults(List<ScanResult> results) { // terms and conditions apply } @Override public void onScanFailed(int errorCode) { // handle error based on the errorCode! } }); 17
  18. The bumpy road of BLE on Android *one more thing

    Power modes & batching require hardware support 18
  19. The bumpy road of BLE on Android BluetoothGatt Manages the

    connection to the peripheral Don’t forget to close() when done, manage reference carefully! 20
  20. The bumpy road of BLE on Android Auto connect? Advice

    against using autoConnect = true Behaviour is unclear Disconnect to cancel does not work on 4.3 Recommendation: manage (re)connection in your app 21
  21. The bumpy road of BLE on Android BluetoothGattCallback Typical sequence:

    1. onConnectionStateChange() 2. call discoverServices() 3. onServicesDiscovered() 4. read / write characteristics and / or subscribe to updates 5. close() when done 22
  22. The bumpy road of BLE on Android Connection & discovery

    BluetoothDevice device = ... // obtained from scanning device.connectGatt(this, false, new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { if (status == BluetoothGatt.GATT_SUCCESS) { if (newState == BluetoothGatt.STATE_CONNECTED) { if (!gatt.discoverServices()) { // requesting service failed, make sure we clean up gatt.close(); } } } else { // connection failed, clean up gatt.close(); } } public void onServicesDiscovered(BluetoothGatt gatt, int status) { // find the service we like to use mSerialService = gatt.getService(BEAN_SERIAL_CHARACTERISTIC_UUID); if (mSerialService == null) { // service is not found, close the client gatt.close(); } } }); 23
  23. The bumpy road of BLE on Android Not in the

    docs Limited amount of LE connections (total!) 4 connections on 4.3 7 on 4.4+ 24
  24. The bumpy road of BLE on Android Characteristics public void

    onServicesDiscovered(BluetoothGatt gatt, int status) { BluetoothGattCharacteristic characteristic = service.getCharacteristic(BEAN_SERIAL_CHARACTERISTIC_UUID); if (!gatt.readCharacteristic(characteristic)) { gatt.close(); } } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { byte[] value = characteristic.getValue(); String stringValue = characteristic.getStringValue(0); } else if (status == BluetoothGatt.GATT_INSUFFICIENT_AUTHENTICATION) { boolean bonding = gatt.getDevice().createBond(); // note: API 19 gatt.close(); } else { gatt.close(); } } 25
  25. The bumpy road of BLE on Android Notifications public void

    onServicesDiscovered(BluetoothGatt gatt, int status) { BluetoothGattService service = gatt.getService(UUID.fromString("a495ff10-c5b1-4b44-b512-1370f02d74de")); BluetoothGattCharacteristic characteristic = service.getCharacteristic(BEAN_SERIAL_CHARACTERISTIC_UUID); if (characteristic == null) { gatt.close(); return; } if (gatt.setCharacteristicNotification(characteristic, true)) { UUID descriptorUuid = UUID.fromString("2902-0000-1000-8000-00805f9b34fb"); // remember? BluetoothGattDescriptor descriptor = characteristic.getDescriptor(descriptorUuid); descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); if (!gatt.writeDescriptor(descriptor)) { gatt.close(); } } } public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { if (status != BluetoothGatt.GATT_SUCCESS) { gatt.close(); } } 26
  26. The bumpy road of BLE on Android Profit! @Override public

    void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { // handle updated value } 27
  27. The bumpy road of BLE on Android Notification limits There’s

    a hard limit on the number of notifications you can have: 4 on Android 4.3 7 on Android 4.4 15 on Android 5 28
  28. The bumpy road of BLE on Android Writing characteristics public

    void write(byte[] data) { mCharacteristic.setValue(data); if (!mGatt.writeCharacteristic(mCharacteristic)) { mGatt.close(); } // important: wait for onCharacteristicWrite callback before attempting the next write! } 29
  29. The bumpy road of BLE on Android Juggling writes By

    design the Bean SDK does a lot of writing Split data in 20 byte packets, write to characteristic Assemble packets from notifications Single characteristic for read/write makes it harder 30
  30. The bumpy road of BLE on Android Conclusions Android has

    full support for BLE, maturing in Lollipop The BLE related API’s are really low level Error checking all over the place Documentation could use some work 31
  31. The bumpy road of BLE on Android Observations Complexity managing

    multiple characteristics in a single callback. Low level interface seems straightforward, but is difficult to get right. Bean SDK has GattClient to solve some of those problems 32
  32. The bumpy road of BLE on Android Learn more bitbucket.org/littlerobots/beanlib

    d.android.com/guide/topics/connectivity/bluetooth-le.html punchthrough.com/bean 33
  33. The bumpy road of BLE on Android Thank you @botteaap

    google.com/+hugovisser [email protected] www.littlerobots.nl 34