Bluetooth Low Energy

for Android

Android Alliance Philadelphia - November 11, 2015

Don Coleman - Chariot Solutions

@doncoleman

Lightbulb Service

  • Light Switch
  • Dimmer Setting

Lightbulb Service - FF10

16 bit UUIDs

0000FF10-0000-1000-8000-00805F9B34FB

0000FF11-0000-1000-8000-00805F9B34FB

0000FF12-0000-1000-8000-00805F9B34FB

Lightbulb Service - FF10

Properties

Attributes

Permissions

Descriptors

Advertising

Advertising

  • 78:C5:E5:9A:88:EE
  • Services: [ 0x180A, 0xFF10, 0XFF20, …]
  • Manufacturer Data: <00007465 6d70>
  • Connectable: True
  • TX Power Level: 4
  • RSSI: -80
  • Name: Smart Lightbulb

After Connecting...

  • Get a list of services
  • For each service, get a list of characteristics
  • For each characteristic, get properties
  • Save handle to read or write later
  • Subscribe to be notified on changes

Read Value

Write Value

Write Command

Heart Rate Service - 180d

org.bluetooth.service.heart_rate.xml
org.bluetooth.characteristic.heart_rate_measurement.xml

Notification

Indication

Lightbulb Service - FF10

Android Bluetooth

Permissions

  • android.permission.BLUETOOTH
  • android.permission.BLUETOOTH_ADMIN

Permissions (android-23)

  • android.permission.BLUETOOTH
  • android.permission.BLUETOOTH_ADMIN
  • android.permission.ACCESS_COARSE_LOCATION
  • android.permission.ACCESS_FINE_LOCATION
Marshmallow needs to ask for permissions at runtime http://developer.android.com/training/permissions/requesting.html

Bluetooth Adapter


  BluetoothManager manager =
       activity.getSystemService(Context.BLUETOOTH_SERVICE);
  BluetoothAdapter adapter = manager.getAdapter();
    

Scanning


  bluetoothAdapter.startLeScan(callback);
    

Scanning (better)


  bluetoothAdapter.startLeScan(serviceUuids, callback);
    

BluetoothAdapter.LeScanCallback


  public void onLeScan(BluetoothDevice device,
                       int rssi,
                       byte[] scanRecord) {
    // do something
  }
    
Scanning 5.0, 21+

ScanCallback


  ScanCallback scanCallback = new ScanCallback() {
    public void onScanResult(int callbackType, ScanResult result) {
    }

    public void onScanFailed(int errorCode) {
    }
  };

Scanning


  BluetoothLeScanner scanner = adapter.getBluetoothLeScanner();
  List<ScanFilter> scanFilters = new ArrayList<>();
  scanFilters.add(
    new ScanFilter.Builder().setServiceUuid(
      new ParcelUuid(BLEThermometer.SERVICE_UUID)).build()
  );
  ScanSettings scanSettings = new ScanSettings.Builder().build();
  scanner.startScan(scanFilters, scanSettings, scanCallback);
    

BluetoothGatt

BluetoothGattCallback

Connection to Device


  device.connectGatt(context, autoConnect, bluetoothGattCallback);
    

Connection State Change


  public void onConnectionStateChange(BluetoothGatt gatt,
                            int status, int state) {

    if (state == BluetoothGatt.STATE_CONNECTED) {
      this.gatt = gatt;
      gatt.discoverServices();
    }
  }
    

Services Discovered


public void onServicesDiscovered(BluetoothGatt gatt, int status) {
  BluetoothGattService service = gatt.getService(SERVICE_UUID);
  switchCharacteristic = service.getCharacteristic(SWITCH_UUID);
  brightnessCharacteristic = service.getCharacteristic(DIMMER_UUID);
}
    

Read Characteristic


    gatt.readCharacteristic(switchCharacteristic);
    

Read Characteristic Callback


  public void onCharacteristicRead(gatt, characteristic, status) {
    UUID uuid = characteristic.getUuid();

    if (uuid.equals(SWITCH_UUID)) {
      int switchState =
        characteristic.getIntValue(FORMAT_UINT8, 0);

       // update UI
      }
  }
    

Write Characteristic


  byte data[] = { 0x01 };
  characteristic.setValue(data);
  gatt.writeCharacteristic(characteristic);
    

Write Characteristic


  switchCharacteristic.setValue(1, FORMAT_UINT8, 0);
  gatt.writeCharacteristic(switchCharacteristic);
    

Write Characteristic Callback


  public void onCharacteristicWrite(BluetoothGatt gatt,
      BluetoothGattCharacteristic characteristic, int status) {
    Log.d(TAG, "Wrote " + characteristic);
  }

Demo

Thermometer Service - bbb0

Register for Notification


if (gatt.setCharacteristicNotification(characteristic, true)) {
  // 0x2902 client characteristic configuration
  UUID uuid = UUID.fromString("00002902-0000-1000-8000-00805F9B34FB");

  BluetoothGattDescriptor descriptor =
                characteristic.getDescriptor(uuid);
  descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION);
  gatt.writeDescriptor(descriptor);
}
    

On Characteristic Changed


  public void onCharacteristicChanged(BluetoothGatt gatt,
               BluetoothGattCharacteristic characteristic) {

    if (characteristic.getUuid().equals(TEMPERATURE_UUID)) {
      float temperature = getFloat(characteristic, 0);
      Log.d(TAG, String.valueOf(temperature));

      float fahrenheit = 1.8f * temperature + 32;
      Log.d(TAG, String.valueOf(fahrenheit));

      // update the UI
    }
  }

Demo

The API is async but MUST BE CALLED SERIALLY

iBeacon

  • UUID: 12345678-aaaa-bbbb-cccc-123456789abc
  • Major: 0x01
  • Minor: 0x01

Eddystone Beacon

  • http://example.com
Create test beacons with Node.js https://github.com/don/node-eddystone-beacon

Creating Peripherals

Requires Lollipop (21) & compatible hardware

bluetoothAdapter.isMultipleAdvertisementSupported()

Create Service & Characteristics

BluetoothGattService service =
    new BluetoothGattService(SERVICE_UUID, SERVICE_TYPE_PRIMARY);

BluetoothGattCharacteristic switchCharacteristic =
  new BluetoothGattCharacteristic(
    SWITCH_UUID,
    PROPERTY_READ | PROPERTY_WRITE,
    PERMISSION_READ | PERMISSION_WRITE);

BluetoothGattCharacteristic dimmerCharacteristic =
  new BluetoothGattCharacteristic(
    DIMMER_UUID,
    PROPERTY_READ | PROPERTY_WRITE,
    PERMISSION_READ | PERMISSION_WRITE);

service.addCharacteristic(switchCharacteristic);
service.addCharacteristic(dimmerCharacteristic);

BluetoothGattServerCallback


BluetoothGattServerCallback callback = new BluetoothGattServerCallback(){
     @Override
     public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
     }

     @Override
     public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
     }
 };

onCharacteristicReadRequest


  if (SWITCH_UUID.equals(characteristic.getUuid())) {
      gattServer.sendResponse(device,
              requestId,
              BluetoothGatt.GATT_SUCCESS,
              0,
              getSwitchValue());
  }

onCharacteristicWriteRequest


  if (SWITCH_UUID.equals(characteristic.getUuid())) {
      setSwitchValue(value);
  } else if (DIMMER_UUID.equals(characteristic.getUuid())) {
      setDimmerValue(value);
  }

  if (responseNeeded) {
      gattServer.sendResponse(device,
            requestId,
            BluetoothGatt.GATT_SUCCESS,
            0,
            value);
  }

Add Service


  gattServer = bluetoothManager.openGattServer(
                    context, bluetoothGattServerCallback);
  gattServer.addService(service);

Advertise Settings


private AdvertiseSettings getAdvertiseSettings() {
  AdvertiseSettings.Builder builder = new AdvertiseSettings.Builder();
  builder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED);
  builder.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH);

  return builder.build();
}
    

Advertise Data


  private AdvertiseData getAdvertisementData() {
    AdvertiseData.Builder builder = new AdvertiseData.Builder();
    builder.setIncludeDeviceName(true);
    builder.addServiceUuid(new ParcelUuid(SERVICE_UUID));

    return builder.build();
  }
    

Advertise Service


  BluetoothLeAdvertiser bluetoothLeAdvertiser =
        bluetoothAdapter.getBluetoothLeAdvertiser();
  AdvertiseData advertisementData = getAdvertisementData();
  AdvertiseSettings advertiseSettings = getAdvertiseSettings();

  bluetoothLeAdvertiser.startAdvertising(advertiseSettings,
                                       advertisementData,
                                       advertiseCallback);

Check & enable Bluetooth


  if (!bluetoothAdapter.isEnabled()) {
    Intent intent =
         new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    this.startActivityForResult(intent, ENABLE_BLUETOOTH_REQUEST);
    // process the result in onActivityResult
  }

Demo


pre-order on Amazon

Thank You

Don Coleman

@doncoleman

don@chariotsolutions.com

Slides http://don.github.io/slides/

 

Creative Commons License
Bluetooth Low Energy for Android by Don Coleman
is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License.
Based on a work at https://github.com/don/.../2015-11-11-phillyandroid.