get notifies for BLE packet arrival

pull/8/head
geeksville 2020-02-09 03:40:13 -08:00
rodzic 4af51e88c7
commit 78e6be2b38
2 zmienionych plików z 107 dodań i 8 usunięć

Wyświetl plik

@ -14,6 +14,7 @@ import com.geeksville.concurrent.DeferredExecution
import com.geeksville.util.toRemoteExceptions
import java.util.*
/* Info for the esp32 device side code. See that source for the 'gold' standard docs on this interface.
MeshBluetoothService UUID 6ba1b218-15a8-461f-9fa8-5dcae273eafd
@ -134,7 +135,6 @@ class RadioInterfaceService : Service(), Logging {
val service get() = safe.gatt!!.services.find { it.uuid == BTM_SERVICE_UUID }!!
private lateinit var fromRadio: BluetoothGattCharacteristic
private lateinit var fromNum: BluetoothGattCharacteristic
private val logSends = false
@ -176,7 +176,8 @@ class RadioInterfaceService : Service(), Logging {
private fun doReadFromRadio() {
if (!isConnected)
warn("Abandoning fromradio read because we are not connected")
else
else {
val fromRadio = service.getCharacteristic(BTM_FROMRADIO_CHARACTER)
safe.asyncReadCharacteristic(fromRadio) {
val b = it.getOrThrow().value
@ -190,6 +191,7 @@ class RadioInterfaceService : Service(), Logging {
debug("Done reading from radio, fromradio is empty")
}
}
}
}
@ -214,9 +216,13 @@ class RadioInterfaceService : Service(), Logging {
debug("requested MTU result=$mtuRes")
mtuRes.getOrThrow() // FIXME - why sometimes is the result Unit!?!
fromRadio = service.getCharacteristic(BTM_FROMRADIO_CHARACTER)
fromNum = service.getCharacteristic(BTM_FROMNUM_CHARACTER)
safe.setNotify(fromNum, true) {
debug("fromNum changed, so we are reading new messages")
doReadFromRadio()
}
// Now tell clients they can (finally use the api)
broadcastConnectionChanged(true)
isConnected = true

Wyświetl plik

@ -11,6 +11,7 @@ import com.geeksville.concurrent.Continuation
import com.geeksville.concurrent.SyncContinuation
import com.geeksville.util.exceptionReporter
import java.io.IOException
import java.util.*
/**
@ -26,7 +27,7 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
Logging {
/// Timeout before we declare a bluetooth operation failed
var timeoutMsec = 5 * 1000L
var timeoutMsec = 30 * 1000L
/// Users can access the GATT directly as needed
var gatt: BluetoothGatt? = null
@ -39,6 +40,9 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
private var connectionCallback: ((Result<Unit>) -> Unit)? = null
private var lostConnectCallback: (() -> Unit)? = null
/// from characteristic UUIDs to the handler function for notfies
private val notifyHandlers = mutableMapOf<UUID, (BluetoothGattCharacteristic) -> Unit>()
/// When we see the BT stack getting disabled/renabled we handle that as a connect/disconnect event
private val btStateReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) = exceptionReporter {
@ -64,6 +68,10 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
}
}
// 0x2902 org.bluetooth.descriptor.gatt.client_characteristic_configuration.xml
private val configurationDescriptorUUID =
UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")
init {
context.registerReceiver(
btStateReceiver,
@ -88,7 +96,6 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
}
}
private val gattCallback = object : BluetoothGattCallback() {
override fun onConnectionStateChange(
@ -112,7 +119,6 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
if (oldstate == BluetoothProfile.STATE_CONNECTED) {
info("Lost connection - aborting current work")
/*
Supposedly this reconnect attempt happens automatically
"If the connection was established through an auto connect, Android will
@ -125,6 +131,9 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
*/
failAllWork(IOException("Lost connection"))
// Cancel any notifications - because when the device comes back it might have forgotten about us
notifyHandlers.clear()
debug("calling lostConnect handler")
lostConnectCallback?.invoke()
@ -167,6 +176,59 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) {
completeWork(status, mtu)
}
/**
* Callback triggered as a result of a remote characteristic notification.
*
* @param gatt GATT client the characteristic is associated with
* @param characteristic Characteristic that has been updated as a result of a remote
* notification event.
*/
override fun onCharacteristicChanged(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic
) {
val handler = notifyHandlers.get(characteristic.uuid)
if (handler == null)
warn("Received notification from $characteristic, but no handler registered")
else {
exceptionReporter {
handler(characteristic)
}
}
}
/**
* Callback indicating the result of a descriptor write operation.
*
* @param gatt GATT client invoked [BluetoothGatt.writeDescriptor]
* @param descriptor Descriptor that was writte to the associated remote device.
* @param status The result of the write operation [BluetoothGatt.GATT_SUCCESS] if the
* operation succeeds.
*/
override fun onDescriptorWrite(
gatt: BluetoothGatt,
descriptor: BluetoothGattDescriptor,
status: Int
) {
completeWork(status, descriptor)
}
/**
* Callback reporting the result of a descriptor read operation.
*
* @param gatt GATT client invoked [BluetoothGatt.readDescriptor]
* @param descriptor Descriptor that was read from the associated remote device.
* @param status [BluetoothGatt.GATT_SUCCESS] if the read operation was completed
* successfully
*/
override fun onDescriptorRead(
gatt: BluetoothGatt,
descriptor: BluetoothGattDescriptor,
status: Int
) {
completeWork(status, descriptor)
}
}
@ -251,7 +313,6 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
}
}
/**
* start a connection attempt.
*
@ -322,7 +383,7 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
private fun queueWriteCharacteristic(
c: BluetoothGattCharacteristic,
cont: Continuation<BluetoothGattCharacteristic>
) = queueWork("writec", cont) { gatt!!.writeCharacteristic(c) }
) = queueWork("writeC", cont) { gatt!!.writeCharacteristic(c) }
fun asyncWriteCharacteristic(
c: BluetoothGattCharacteristic,
@ -332,6 +393,17 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
fun writeCharacteristic(c: BluetoothGattCharacteristic): BluetoothGattCharacteristic =
makeSync { queueWriteCharacteristic(c, it) }
private fun queueWriteDescriptor(
c: BluetoothGattDescriptor,
cont: Continuation<BluetoothGattDescriptor>
) = queueWork("writeD", cont) { gatt!!.writeDescriptor(c) }
fun asyncWriteDescriptor(
c: BluetoothGattDescriptor,
cb: (Result<BluetoothGattDescriptor>) -> Unit
) = queueWriteDescriptor(c, CallbackContinuation(cb))
private fun closeConnection() {
failAllWork(IOException("Connection closing"))
@ -348,5 +420,26 @@ class SafeBluetooth(private val context: Context, private val device: BluetoothD
context.unregisterReceiver(btStateReceiver)
}
/// asyncronously turn notification on/off for a characteristic
fun setNotify(
c: BluetoothGattCharacteristic,
enable: Boolean,
onChanged: (BluetoothGattCharacteristic) -> Unit
) {
debug("starting setNotify(${c.uuid}, $enable)")
notifyHandlers[c.uuid] = onChanged
// c.writeType = BluetoothGattCharacteristic.WRITE_TYPE_DEFAULT
gatt!!.setCharacteristicNotification(c, enable)
// per https://stackoverflow.com/questions/27068673/subscribe-to-a-ble-gatt-notification-android
val descriptor: BluetoothGattDescriptor = c.getDescriptor(configurationDescriptorUUID)!!
descriptor.value =
if (enable) BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE else BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE
asyncWriteDescriptor(descriptor) {
debug("Notify enable=$enable completed")
}
}
}