Burūberī
Less flaky Bluetooth Low Energy for Android.
Less flaky Bluetooth Low Energy for Android.
Working with Bluetooth Low Energy on Android can be a real pain. Major quality differences between manufacturers, mysterious undocumented error codes, bugs introduced between OS releases... These are not the things dreams are made of. Buruberi is a small library that wraps the Android Bluetooth Low Energy APIs, and tries to insulate you from as many of these problems as it can.
All interaction with Bluetooth through burūberī happens through an instance of the BluetoothStack
class. BluetoothStack
s are created through the Buruberi
builder class.
final BluetoothStack bluetoothStack = new Buruberi() .setApplicationContext(context) .build();
The Buruberi
class will choose an appropriate implementation of BluetoothStack
for the current runtime environment. The returned BluetoothStack
should be stored as a singleton value in your application, and shared between all Bluetooth code. Creating multiple BluetoothStack
objects may result in unexpected behavior.
Bluetooth Low Energy peripherals are discovered by passing a PeripheralCriteria
object to a BluetoothStack
. PeripheralCriteria
allows you to scan for peripherals by MAC address, by advertising data, or a combination of both.
final PeripheralCriteria criteria = new PeripheralCriteria(); criteria.setLimit(5); criteria.setDuration(15 * 1000L); final Observable<List<GattPeripheral>> discover = bluetoothStack.discoverPeripherals(criteria); discover.subscribe(peripherals -> { Log.i("Discover", "Found peripherals " + peripherals); }, error -> { Log.e("Discover", "Could not scan for peripherals.", error); });
After you‘ve completed a scan for your intended Bluetooth Low Energy peripheral, you can connect to it, and manage bonds. Note: on many phones running Lollipop, you cannot create a new bond once the phone has created a gatt connection to the peripheral.
final OperationTimeout timeout = peripheral.createOperationTimeout("Connect", 30, TimeUnit.SECONDS); final Observable<GattPeripheral> connect = peripheral.connect(GattPeripheral.CONNECT_FLAG_DEFAULTS, timeout); connect.subscribe(p -> { Log.i("Connect", "Connected to peripheral " + p); }, error -> { Log.e("Connect", "Could not connect to peripheral.", error); }); final Observable<GattPeripheral> bond = peripheral.createBond(); bond.subscribe(p -> { Log.i("Bond", "Created bond with peripheral " + p); }, error -> { Log.e("Bond", "Could not create bond with peripheral.", error); });
Once you‘ve connected to a peripheral, you can perform service discovery on it.
final OperationTimeout timeout = peripheral.createOperationTimeout("Services", 30, TimeUnit.SECONDS); final Observable<Map<UUID, ? extends GattService>> services = peripheral.discoverServices(timeout); services.subscribe(allServices -> { Log.i("Services", "Discovered services " + allServices); }, error -> { Log.e("Services", "Could not discover services", error); });
After you‘ve connected to a peripheral, and performed service discovery on it, you can read the values of characteristics.
final GattCharacteristic characteristic = service.getCharacteristic(MY_CHARACTERISTIC); final OperationTimeout timeout = peripheral.createOperationTimeout("Read", 30, TimeUnit.SECONDS); final Observable<byte[]> read = characteristic.read(timeout); read.subscribe(payload -> { Log.i("Write", "Read from characteristic " + characteristic.getUuid() + ": " + Bytes.toString(payload)); }, error -> { Log.e("Write", "Could not read from characteristic " + characteristic.getUuid(), error); });
Writing to characteristics follows a similar pattern to reading from them.
final GattCharacteristic characteristic = service.getCharacteristic(MY_CHARACTERISTIC); final OperationTimeout timeout = peripheral.createOperationTimeout("Write", 30, TimeUnit.SECONDS); final byte[] payload = { 0x4, 0x2 }; final Observable<Void> write = characteristic.write(GattPeripheral.WriteType.DEFAULT, payload, timeout); write.subscribe(ignored -> { Log.i("Write", "Wrote to characteristic " + characteristic.getUuid()); }, error -> { Log.e("Write", "Could not write to characteristic " + characteristic.getUuid(), error); });
Incoming packets from characteristic notifications are routed through a user-defined PacketListener
object provided to a GattCharacteristic
. From the packet listener, your client code can parse and route the contents of the packets as appropriate for your peripheral.
final PacketHandler printingHandler = new PacketHandler() { final GattCharacteristic characteristic = service.getCharacteristic(MY_CHARACTERISTIC); final GattCharacteristic.PacketListener printingListener = new GattCharacteristic.PacketListener() { @Override public boolean processIncomingPacket(@NonNull UUID characteristicIdentifier, @NonNull byte[] payload) { public void onCharacteristicNotify(@NonNull UUID characteristic, @NonNull byte[] payload) { Log.i("Packets", "Got payload " + Bytes.toString(payload)); return true; } @Override public void transportDisconnected() { public void onPeripheralDisconnected() { Log.i("Packets", "Peripheral disconnected"); } }; characteristic.setPacketListener(printingListener); peripheral.setPacketHandler(printingHandler); final OperationTimeout timeout = peripheral.createOperationTimeout("Enable", 30, TimeUnit.SECONDS); final Observable<UUID> notify = characteristic.enableNotification(MY_DATA_DESCRIPTOR, timeout); notify.subscribe(descriptorId -> { Log.i("Write", "Enabled notification for descriptor " + descriptorId); }, error -> { Log.e("Write", "Could not enable notifications for descriptor", error); });
dependencies { compile 'is.hello:buruberi-core:*' testCompile 'is.hello:buruberi-testing:*' }
Does not include convenience resources.
DownloadThe source for Burūberī is available on GitHub
Get SourceIf you'd like to contribute to android-buruberi
, fork the project on GitHub, and submit a pull request with your changes. Please be sure to include unit tests for any changes you make, and follow the coding style of the project as closely as possible. The full contribution guidelines can be found here.
Copyright 2015 Hello Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.