Simple Bike Computer1
#about me volcer@cbase @volkersfreunde mobile software about.me/falkorichter
#Content
- What is BTLE
- How does it work on Android
- Simple Bike Computer
- Hardware/setup - bike
- Hardware - phone
- How does it work on other platforms?
- Swift iOS/MacOSX
#BTLE Bluetooth Low Energy®, a.k.a. Bluetooth Smart®
- simple low energy data transfer
- send simple bits of data fast
- don´t sent alot of data
- easy binding
#BTLE
To help consumers identify compatibility and ensure connectivity with products and applications incorporating Bluetooth® Core Specification version 4.0 (or higher), the Bluetooth SIG has developed the Bluetooth Smart and Bluetooth Smart Ready trademarks.2
#BTLE basics Services
Services are collections of characteristics and relationships to other services that encapsulate the behavior of part of a device.3
#BTLE basics
Characteristics are defined attribute types that contain a single logical value.4
#BTLE basics
"Descriptors are defined attributes that describe a characteristic value."5
#Why a bike computer?
##I ride my bike ##It´s not too expensive ##I want to know how far I go each week/month/year ##health foo #ftw
#Ingredients Speed and Cadence Sensor
#Ingredients Amazon: "Geschwindigkeit und Trittfrequenz" AliExpress: "Speed Cadence Sensor" (not yet tested, but: a lot cheaper, unlabeled, same build -> same internals(?))
No Nexus 4!!!
Remove the label o the device with nail polish remover
#Speed and Cadence
- official Profile all
- Cycling Speed and Cadence
It´s all nicely documented.
#BTLE on Android
- BluetoothManager
- BluetoothGattCallback
- onConnectionStateChange
- onReadRemoteRssi
- onServicesDiscovered
- onCharacteristicChanged Connect.java
#BTLE on Android
- Subscribe to Notify Characteristic org.bluetooth.characteristic.csc_measurement
- extract values
- display them
- read RSSI (monitor the connection)
#Android: Scan and connect
final BluetoothAdapter adapter = bluetooth.getAdapter();
UUID[] serviceUUIDs = new UUID[]{CSC_SERVICE_UUID};
adapter.startLeScan(serviceUUIDs, new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
device.connectGatt(Connect.this, autoConnectCheckBox.isChecked(), bluetoothGattCallback);
}
});
#Android: Scan for Services ######BluetoothGattCallback:
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int state) {
super.onConnectionStateChange(gatt, status, state);
switch (state) {
case BluetoothGatt.STATE_CONNECTED: {
showText("STATE_CONNECTED", Style.INFO);
setConnectedGatt(gatt);
gatt.discoverServices();
break;
}
}
}
#Android: Register for Updates ######Register for Updates of the org.bluetooth.characteristic.csc_measurement ######BluetoothGattCallback:
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
BluetoothGattCharacteristic valueCharacteristic = gatt.getService(CSC_SERVICE_UUID).getCharacteristic(CSC_CHARACTERISTIC_UUID);
boolean notificationSet = gatt.setCharacteristicNotification(valueCharacteristic, true);
BluetoothGattDescriptor descriptor = valueCharacteristic.getDescriptor(BTLE_NOTIFICATION_DESCRIPTOR_UUID);
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
boolean writeDescriptorSuccess = gatt.writeDescriptor(descriptor);
}
#Android: Monitor the RSSI ######BluetoothGattCallback:
@Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
super.onReadRemoteRssi(gatt, rssi, status);
listener.updateRssiDisplay(rssi);
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
gatt.readRemoteRssi();
}
######Also when scanning:
final BluetoothAdapter adapter = bluetooth.getAdapter();
UUID[] serviceUUIDs = new UUID[]{CSC_SERVICE_UUID};
adapter.startLeScan(serviceUUIDs, new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
listener.updateRssiDisplay(rssi);
}
});
#Android
On Google's Android BLE stack there is a hard limit of 4 unique notification subscriptions during a single connection's lifetime. This is not SensorTag specific. To get around this you must disconnect, and then reconnect with up to 4 new notifications.6
#Android Read/Write Async interface:
- one BluetoothGattCallback per device
- async execution of commands with callbacks
onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status)
- async execution of commands with callbacks
#Android Read and Write, read/write multiple values
- read, write, read tricky since the
BluetoothGattCharacteristic
contains the value- Helper class needed
BluetoothGattCommand
andBluetoothGattCommandQueue
#Android Read and Write, read/write multiple values ######still work in progress7
final BluetoothAdapter adapter = bluetooth.getAdapter();
UUID[] serviceUUIDs = new UUID[]{CSC_SERVICE_UUID};
final GattCommandServiceGroup service = new GattCommandServiceGroup(BTUUID.Service.device_information);
service.addCharacteristicOperation(GattCommand.CommandOperation.OPERATION_READ, BTUUID.Characteristic.manufacturer_name_string);
service.addCharacteristicOperation(GattCommand.CommandOperation.OPERATION_READ, BTUUID.Characteristic.model_number_string);
service.addCharacteristicOperation(GattCommand.CommandOperation.OPERATION_READ, BTUUID.Characteristic.firmware_revision_string);
service.addCharacteristicOperation(GattCommand.CommandOperation.OPERATION_READ, BTUUID.Characteristic.hardware_revision_string);
service.addCharacteristicOperation(GattCommand.CommandOperation.OPERATION_READ, BTUUID.Characteristic.serial_number_string);
final GattCommandQueue queue = new GattCommandQueue();
queue.add(service);
queue.setGattCommandQueueCallback(<your callback>)
queue.executeWhenConnected();
[...]
#Android Wearables Of course all this works directly on Android Wear
- minimal sample included.
- same code
- no host app needed
- wear app => main app, phone app not needed
#Android
Meet the AndroidSimpleBikeComputer
git clone https://github.com/deadfalkon/android-simple-bike-computer.git
git clone https://github.com/deadfalkon/simple-bike-computer-presentation.git
#Swift
everything is a little easier iOS & MacOSX
func startBroadcasting(){
heartRateService.characteristics = [hearRateChracteristic]
infoService.characteristics = [infoNameCharacteristics]
peripheralManager.addService(infoService)
peripheralManager.addService(heartRateService)
var advertisementData = [
CBAdvertisementDataServiceUUIDsKey:[infoService.UUID, heartRateService.UUID],
CBAdvertisementDataLocalNameKey : "mac of falko"
]
peripheralManager.startAdvertising(advertisementData)
}
[...]
#Swift get notified :1 10
let CSC_SERVICE = CBUUID(string: "1816")
let CSC_MEASUREMENT = CBUUID(string: "2A5B")
func centralManagerDidUpdateState(central: CBCentralManager!){
switch (central.state){
case .PoweredOn:
central.scanForPeripheralsWithServices([CSC_SERVICE], options: nil)
default:
println("not powered on")
}
}
#Swift get notified :2
func centralManager(central: CBCentralManager!, didConnectPeripheral peripheral: CBPeripheral!){
peripheral.discoverServices([CSC_SERVICE])
}
#Swift get notified :3
func peripheral(peripheral: CBPeripheral!, didDiscoverServices error: NSError!){
if(error != nil) {
for service in peripheral.services {
if service.UUID == CSC_SERVICE {
peripheral.discoverCharacteristics([CSC_MEASUREMENT], forService: service as CBService)
}
}
}
}
#Swift get notified :4
func peripheral(peripheral: CBPeripheral!, didDiscoverCharacteristicsForService service: CBService!, error: NSError!) {
if(error != nil){
if service.UUID == CSC_SERVICE {
for characteristic in service.characteristics{
if (characteristic as CBCharacteristic).UUID == CSC_MEASUREMENT {
peripheral.setNotifyValue(true, forCharacteristic: characteristic as CBCharacteristic);
}
}
}
}
}
#Swift get notified :5
func peripheral(peripheral: CBPeripheral!, didUpdateValueForCharacteristic characteristic: CBCharacteristic!, error: NSError!) {
//do your thing
}
Footnotes
-
Presentation made with DecksetApp ↩
-
bluetooth.org/en-us/bluetooth-brand/how-to-use-smart-marks ↩
-
developer.bluetooth.org/gatt/services/Pages/ServicesHome.aspx ↩
-
[developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicsHome.aspx)[https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicsHome.aspx] ↩
-
developer.bluetooth.org/gatt/descriptors/Pages/DescriptorsHomePage.aspx ↩
-
done: github.com/deadfalkon/swift-simple-bike-computer/blob/master/Shared/HeartBeatPeripheral.swift ↩
-
needed: github.com/deadfalkon/swift-simple-bike-computer/blob/master/Shared/SpeedAndCadencePeripheral.swift ↩
-
https://github.com/deadfalkon/swift-simple-bike-computer/blob/master/Shared/CadenceConnector.swift ↩