$30 off During Our Annual Pro Sale. View Details »

Connecting the Bean: the bumpy road of BLE on Android

Connecting the Bean: the bumpy road of BLE on Android

Slides from my talk at the Android Developer Days in Ankara

Hugo Visser

May 09, 2015
Tweet

More Decks by Hugo Visser

Other Decks in Programming

Transcript

  1. Connecting the bean:
    The bumpy road of BLE
    on Android
    Hugo Visser
    Little Robots
    source: https://www.flickr.com/photos/klaasjan/6295632164

    View Slide

  2. View Slide

  3. @botteaap
    +HugoVisser
    [email protected]

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  7. The bumpy road of BLE on Android
    LightBlue Bean
    BLE “serial” protocol
    Control LED & Arduino
    Read sensors & configuration
    4

    View Slide

  8. The bumpy road of BLE on Android
    LightBlue Bean
    BeanLoader for OS X
    LightBlue for iOS
    BeanLoader of iOS
    BeanLoader for Windows (2015)
    SDK for OS X and iOS
    5

    View Slide

  9. The bumpy road of BLE on Android
    LightBlue Bean
    BeanLoader for OS X
    LightBlue for iOS
    BeanLoader of iOS
    BeanLoader for Windows (2015)
    SDK for OS X and iOS
    5

    View Slide

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

    View Slide

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

    View Slide

  12. The bumpy road of BLE on Android
    Beanloader!
    Official Android app by Punch Through
    Released end of April 2015
    7

    View Slide

  13. The bumpy road of BLE on Android 8
    Next up:
    What is Bluetooth LE
    Code

    View Slide

  14. The bumpy road of BLE on Android 9
    Bluetooth Low Energy
    Branded: Bluetooth Smart
    Low power, low transmission rate (1 Mbps)
    Sports & fitness, health care, keyboards and mice,
    beacons, wearables, …

    View Slide

  15. The bumpy road of BLE on Android 10
    Generic Access Profile
    Peripheral is advertising it’s presence
    Observer scans for peripherals

    View Slide

  16. The bumpy road of BLE on Android 11
    Generic Attribute Profile
    Profiles
    Services
    Characteristics

    View Slide

  17. The bumpy road of BLE on Android
    Profile
    Service
    Characteristic
    Characteristic
    Service
    Characteristic
    11
    Profiles
    Services
    Characteristics
    GATT

    View Slide

  18. The bumpy road of BLE on Android 12
    UUIDs
    Unique identifier for services,
    characteristics (and descriptors)
    Profile
    a495ff10-c5b1-4b44-b512-1370f02d74de
    a495ff11-c5b1-4b44-b512-1370f02d74de
    0000180F-0000-1000-8000-00805F9B34FB
    Reserved base UUID
    00002A19-0000-1000-8000-00805F9B34FB

    View Slide

  19. The bumpy road of BLE on Android 12
    UUIDs
    Unique identifier for services,
    characteristics (and descriptors)
    Profile
    a495ff10-c5b1-4b44-b512-1370f02d74de
    a495ff11-c5b1-4b44-b512-1370f02d74de
    Reserved base UUID
    0000180F-0000-1000-8000-00805F9B34FB
    00002A19-0000-1000-8000-00805F9B34FB

    View Slide

  20. The bumpy road of BLE on Android 12
    UUIDs
    Unique identifier for services,
    characteristics (and descriptors)
    Profile
    a495ff10-c5b1-4b44-b512-1370f02d74de
    a495ff11-c5b1-4b44-b512-1370f02d74de
    Reserved base UUID
    180F
    2A19

    View Slide

  21. The bumpy road of BLE on Android 13
    What about beacons?
    Formatted advertisement packets
    iBeacon, UriBeacon, proprietary
    Use rssi to determine distance

    View Slide

  22. The bumpy road of BLE on Android 14
    Android BLE
    Platform API since Android 4.3 (API 18)
    Part of bluedroid

    View Slide

  23. The bumpy road of BLE on Android 15
    First steps
    Declare permissions in AndroidManifest.xml



    Require Bluetooth BLE if essential to the app

    View Slide

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

    View Slide

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

    View Slide

  26. The bumpy road of BLE on Android 18
    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

    View Slide

  27. The bumpy road of BLE on Android
    Find me a device
    BluetoothAdapter. startLeScan() and LeScanCallback
    19
    public boolean startLeScan (BluetoothAdapter.LeScanCallback callback)

    View Slide

  28. 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)
    20

    View Slide

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

    View Slide

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

    View Slide

  31. The bumpy road of BLE on Android
    Find me type of device
    public boolean startLeScan (UUID[] serviceUuids, BluetoothAdapter.LeScanCallback callback)
    23

    View Slide

  32. 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
    24

    View Slide

  33. The bumpy road of BLE on Android
    Now for some good news
    25

    View Slide

  34. The bumpy road of BLE on Android
    Now for some good news
    25

    View Slide

  35. 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
    26

    View Slide

  36. 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 results) {
    // terms and conditions apply
    }
    @Override
    public void onScanFailed(int errorCode) {
    // handle error based on the errorCode!
    }
    });
    27

    View Slide

  37. The bumpy road of BLE on Android
    *one more thing
    Power modes & batching require hardware support
    28

    View Slide

  38. The bumpy road of BLE on Android
    Connecting
    29

    View Slide

  39. The bumpy road of BLE on Android
    BluetoothGatt
    Manages the connection to the peripheral
    Don’t forget to close() when done, manage reference carefully!
    30

    View Slide

  40. 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
    31

    View Slide

  41. 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
    32

    View Slide

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

    View Slide

  43. 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+
    34

    View Slide

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

    View Slide

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

    View Slide

  46. The bumpy road of BLE on Android
    Profit!
    @Override
    public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic
    characteristic) {
    // handle updated value
    }
    37

    View Slide

  47. 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
    38

    View Slide

  48. 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!
    }
    39

    View Slide

  49. 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
    40

    View Slide

  50. The bumpy road of BLE on Android
    Conclusions
    Android has full support for BLE, maturing in Lollipop
    The BLE related API’s are is a really low level
    Error checking all over the place
    Documentation could use some work
    41

    View Slide

  51. 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
    42

    View Slide

  52. 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
    43

    View Slide

  53. The bumpy road of BLE on Android
    Thank you
    @botteaap
    google.com/+hugovisser
    www.littlerobots.nl
    44

    View Slide