I have an Android application running with RxAndroidBle library and I want to change it to Nordic-Ble-Android library. I'm not that good with Bluetooth and the actual version of the app is not developed by me so I'm in trouble trying to make it work.
Our connection with the wireless bluetooth sensor is following a few steps:
- The APP establishes a connection with the wireless sensor
- The APP send a request connection command frame to wireless sensor
- The APP send a command to the wireless sensor to start blinking
- User tap on a button on the app to confirm that the APP is blinking
- The APP send one more command to confirm the connection and now it's done
If the user does not confirm that the sensor is blinking, the wireless sensor will automatically timeout within 10 seconds.
I'm successfully establishing a connection with the sensor using this method:
val man = MyBleManager(this@BluetoothService)
man.setGattCallbacks(this@BluetoothService)
bleManagers?.put(device, man)
man.connect(device)
.done {
Log.d("BLESERVICE", "success")
//not working
man.sendRequestConnectionCommand(DeviceType.SENSOR)
//not working
startBlinkingDeviceLed(man, DeviceType.SENSOR)
connectionFlowListeners.forEach { it.onConnectionEstablished(device) }
}
.fail { d, status ->
Log.d("BLESERVICE", "fail")
devicesList?.remove(device)
bleManagers?.remove(device)
}
.timeout(10000)
.enqueue()
The issue is writing characteristcs after that, It seems that I'm doing something wrong.
The current version of the connection with RxAndroidBle is written below:
fun connectToDevice(device: BraincareDevice, pairColor: Int) {
BleLogHelper.writeLog("Connecting to ${device.name}")
val deviceType = if (device is Sensor) DeviceType.SENSOR else DeviceType.DONGLE
if (deviceType == DeviceType.SENSOR) {
sensorConnectionSubscription?.dispose()
} else {
dongleConnectionSubscription?.dispose()
}
val connectionSubscription = device.device.establishConnection(false)
.flatMapSingle { connection ->
if (device is Sensor) {
sensorConnection = connection
connectedSensor = device
} else if (device is Dongle) {
dongleConnection = connection
connectedDongle = device
}
BleLogHelper.writeLog("Send Request Connection Command $deviceType")
val command = BraincareBluetoothCommandProtocol.createRequestConnectionCommandFrame(deviceType)
connection.writeCharacteristic(BraincareBluetoothProtocol.rxCharacteristicUUID, command)
}
.delay(300, TimeUnit.MILLISECONDS)
.subscribe({
BleLogHelper.writeLog("Connection Established ${device.type}")
if (device.device.connectionState == RxBleConnection.RxBleConnectionState.CONNECTED) {
startBlinkingDeviceLed(deviceType, pairColor)
connectionFlowListeners.forEach { it.onConnectionEstablished(device) }
}
}
You can see that it's using the connection to write the characteristic that sends the request command frame within .flatMapSingle
Also, the command to startBlinking is inside the .subscribe method. You can see as well that it's not using any service at all to use the characteristic rxCharacteristicUUID, and looking on Blinky and Toolbox examples given, it seems that I have to initialize the characteristic using gatt.getService, and I got confused because there is no need to use a service for these commands on actual application.
I tried to do exactly like this snippet, but using Android-Ble-Nordic library, but I could not really understand how I have to do that.
Below is my entire version of my BleManager following the examples given on Android-nRF-Blinky and Android-nRF-Toolbox.
package io.b4c.brain4care.modules.ble
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCharacteristic
import android.content.Context
import android.graphics.Color
import android.support.annotation.NonNull
import android.support.annotation.Nullable
import android.util.Log
import io.b4c.brain4care.BraincareApplication
import io.b4c.brain4care.modules.monitoringservice.BraincareBluetoothManager
import io.b4c.brain4care.modules.monitoringservice.protocol.BraincareBluetoothCommandProtocol
import io.b4c.brain4care.modules.monitoringservice.protocol.BraincareBluetoothProtocol
import io.b4c.brain4care.util.constants.AppConstants
import io.b4c.brain4care.util.enums.DeviceType
import io.b4c.brain4care.util.extensions.toByteStringRepresentation
import io.b4c.brain4care.util.helpers.BleLogHelper
import io.reactivex.schedulers.Schedulers
import no.nordicsemi.android.ble.BleManager
import no.nordicsemi.android.ble.BleManagerCallbacks
import no.nordicsemi.android.ble.data.Data
import no.nordicsemi.android.log.ILogSession
import no.nordicsemi.android.log.LogContract
import no.nordicsemi.android.log.Logger
class MyBleManager(context: Context): BleManager<BleManagerCallbacks>(context) {
override fun getGattCallback(): BleManagerGattCallback {
return mGattCallback
}
private var mLogSession: ILogSession? = null
private var rxCharacteristic: BluetoothGattCharacteristic? = null
private val mGattCallback = object : BleManagerGattCallback() {
override fun onDeviceDisconnected() {
}
override fun isRequiredServiceSupported(gatt: BluetoothGatt): Boolean {
//?? i don't need this i guess
val service = gatt.getService(BraincareBluetoothProtocol.serviceDeviceInformationUUID)
return true
}
protected override fun initialize() {
super.initialize()
Log.d("BLESERVICE","initializing")
//I not so sure if I need this
setNotificationCallback(rxCharacteristic).with(mLedCallback)
enableNotifications(rxCharacteristic).with(commandCallback)
enableIndications(rxCharacteristic).with(commandCallback)
}
}
private val commandCallback = object : CommandSentCallback {
override fun onDataSent(device: BluetoothDevice, data: Data) {
super.onDataSent(device, data)
Log.d("BLESERVICE", "sent something")
}
}
private val mLedCallback = object : ManufectureDataCallback() {
override fun onReceived(device: BluetoothDevice, value: Int) {
Log.d("BLESERVICE", "received something")
}
}
fun sendRequestConnectionCommand(deviceType: DeviceType){
Log.d("BLESERVICE","sending request command")
val command = BraincareBluetoothCommandProtocol.createRequestConnectionCommandFrame(deviceType)
writeCharacteristic(rxCharacteristic, command).enqueue()
}
private fun getRandomPairColor(): Int {
val colorOptions = AppConstants.lighsyncColorOptions
val colorIndex = (0 until colorOptions.size).shuffled().first()
return context.getColor(colorOptions[colorIndex])
}
fun sendSetLedProfileCommand(deviceType: DeviceType, color: Int) {
BleLogHelper.writeLog("Device Blinked $deviceType")
Log.d("BLESERVICE","sending blink command")
val command = BraincareBluetoothCommandProtocol.createBlinkLedCommandFrame(Color.red(color), Color.green(color), Color.blue(color), 2)
writeCharacteristic(rxCharacteristic, command).with(commandCallback).enqueue()
/*val connection = if (deviceType == DeviceType.SENSOR) BraincareBluetoothManager.sensorConnection else BraincareBluetoothManager.dongleConnection
BraincareBluetoothManager.connectionFlowListeners.forEach { it.onDeviceBlinkSuccess(color) }
val disposable = connection?.writeCharacteristic(BraincareBluetoothProtocol.rxCharacteristicUUID, command)
?.subscribeOn(Schedulers.io())
?.subscribe({
}, {
BleLogHelper.writeError("Error on sendSetLedProfileCommand", it)
})
BraincareBluetoothManager.sensorCompositeDisposable.add(disposable ?: return)*/
}
/**
* Sets the log session to log into.
*
* @param session nRF Logger log session to log inti, or null, if nRF Logger is not installed.
*/
fun setLogger(@Nullable session: ILogSession) {
mLogSession = session
}
override fun log(priority: Int, @NonNull message: String) {
Logger.log(mLogSession, LogContract.Log.Level.fromPriority(priority), message)
Log.println(priority, "BleManager", message)
}
}