feat: subscribe to demo char changes
This commit is contained in:
@@ -13,14 +13,14 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
package com.example.sensortestingapp
|
||||||
package com.punchthrough.blestarterappandroid.ble
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.bluetooth.BluetoothDevice
|
import android.bluetooth.BluetoothDevice
|
||||||
import android.bluetooth.BluetoothGatt
|
import android.bluetooth.BluetoothGatt
|
||||||
import android.bluetooth.BluetoothGattCallback
|
import android.bluetooth.BluetoothGattCallback
|
||||||
import android.bluetooth.BluetoothGattCharacteristic
|
import android.bluetooth.BluetoothGattCharacteristic
|
||||||
|
import android.bluetooth.BluetoothGattDescriptor
|
||||||
import android.bluetooth.BluetoothProfile
|
import android.bluetooth.BluetoothProfile
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
@@ -28,6 +28,8 @@ import java.util.UUID
|
|||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue
|
import java.util.concurrent.ConcurrentLinkedQueue
|
||||||
|
|
||||||
|
val CCC_DESCRIPTOR_UUID = UUID.fromString("00002902-0000-1000-8000-00805F9B34FB")
|
||||||
|
|
||||||
private const val GATT_MIN_MTU_SIZE = 23
|
private const val GATT_MIN_MTU_SIZE = 23
|
||||||
|
|
||||||
/** Maximum BLE MTU size as defined in gatt_api.h. */
|
/** Maximum BLE MTU size as defined in gatt_api.h. */
|
||||||
@@ -59,6 +61,14 @@ data class ReadChar(
|
|||||||
val charId: UUID
|
val charId: UUID
|
||||||
) : BleOperationType()
|
) : BleOperationType()
|
||||||
|
|
||||||
|
|
||||||
|
data class SetNotification(
|
||||||
|
override val device: BluetoothDevice,
|
||||||
|
val serviceId: UUID,
|
||||||
|
val charId: UUID,
|
||||||
|
val enable: Boolean
|
||||||
|
) : BleOperationType()
|
||||||
|
|
||||||
data class DiscoverServicesRequest(
|
data class DiscoverServicesRequest(
|
||||||
override val device: BluetoothDevice,
|
override val device: BluetoothDevice,
|
||||||
) : BleOperationType()
|
) : BleOperationType()
|
||||||
@@ -70,6 +80,12 @@ open class BleListener {
|
|||||||
characteristic: BluetoothGattCharacteristic
|
characteristic: BluetoothGattCharacteristic
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
open fun onCharChange(
|
||||||
|
gatt: BluetoothGatt,
|
||||||
|
characteristic: BluetoothGattCharacteristic
|
||||||
|
) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -173,6 +189,15 @@ object ConnectionManager {
|
|||||||
enqueueOperation(ReadChar(device, service, char))
|
enqueueOperation(ReadChar(device, service, char))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun enableNotification(device: BluetoothDevice, service: UUID, char: UUID) {
|
||||||
|
enqueueOperation(SetNotification(device, service, char, true))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun disableNotification(device: BluetoothDevice, service: UUID, char: UUID) {
|
||||||
|
enqueueOperation(SetNotification(device, service, char, false))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// - Beginning of PRIVATE functions
|
// - Beginning of PRIVATE functions
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
@@ -235,6 +260,7 @@ object ConnectionManager {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
when (operation) {
|
when (operation) {
|
||||||
is Disconnect -> with(operation) {
|
is Disconnect -> with(operation) {
|
||||||
Log.w("ConnectionManager", "Disconnecting from ${device.address}")
|
Log.w("ConnectionManager", "Disconnecting from ${device.address}")
|
||||||
@@ -256,12 +282,67 @@ object ConnectionManager {
|
|||||||
if (characteristic?.isReadable() == true) {
|
if (characteristic?.isReadable() == true) {
|
||||||
gatt.readCharacteristic(characteristic)
|
gatt.readCharacteristic(characteristic)
|
||||||
} else {
|
} else {
|
||||||
|
Log.e("ConnectionManager", "Char $charId (${serviceId}) is not readable!")
|
||||||
signalEndOfOperation()
|
signalEndOfOperation()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is SetNotification -> with(operation) {
|
||||||
|
val characteristic = gatt.getService(serviceId)?.getCharacteristic(charId);
|
||||||
|
if (characteristic == null) {
|
||||||
|
Log.e("ConnectionManager", "Char $charId (${serviceId}) not found!")
|
||||||
|
signalEndOfOperation()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val descriptor = characteristic.getDescriptor(CCC_DESCRIPTOR_UUID)
|
||||||
|
|
||||||
|
if (!characteristic.isNotifiable() && !characteristic.isIndicatable()) {
|
||||||
|
Log.e(
|
||||||
|
"ConnectionManager", "Char ${characteristic.uuid} (service: $serviceId)" +
|
||||||
|
" doesn't support notifications/indications"
|
||||||
|
)
|
||||||
|
signalEndOfOperation()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val payload: ByteArray
|
||||||
|
|
||||||
|
if (enable) {
|
||||||
|
if (!gatt.setCharacteristicNotification(characteristic, true)) {
|
||||||
|
Log.e(
|
||||||
|
"ConnectionManager", "setCharacteristicNotification to true " +
|
||||||
|
"failed for ${characteristic.uuid} (service: $serviceId)"
|
||||||
|
)
|
||||||
|
signalEndOfOperation()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
payload =
|
||||||
|
if (characteristic.isIndicatable())
|
||||||
|
BluetoothGattDescriptor.ENABLE_INDICATION_VALUE
|
||||||
|
else BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if (!gatt.setCharacteristicNotification(characteristic, false)) {
|
||||||
|
Log.e(
|
||||||
|
"ConnectionManager", "setCharacteristicNotification to false " +
|
||||||
|
"failed for ${characteristic.uuid} (service: $serviceId)"
|
||||||
|
)
|
||||||
|
signalEndOfOperation()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
payload = BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE
|
||||||
|
}
|
||||||
|
|
||||||
|
gatt.let { gatt ->
|
||||||
|
descriptor.value = payload
|
||||||
|
gatt.writeDescriptor(descriptor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// necessary because of IDE type inference bug
|
||||||
|
// https://youtrack.jetbrains.com/issue/KTIJ-20749/Exhaustive-when-check-does-not-take-into-account-the-values-excluded-by-previous-if-conditions
|
||||||
is Connect -> {
|
is Connect -> {
|
||||||
Log.e("ConnectionManager", "Shouldn't get here")
|
Log.e("ConnectionManager", "Shouldn't get here")
|
||||||
}
|
}
|
||||||
@@ -365,11 +446,33 @@ object ConnectionManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pendingOperation is ReadChar) {
|
val op = pendingOperation
|
||||||
|
if (op is ReadChar && op.charId == characteristic.uuid && op.serviceId == characteristic.service.uuid) {
|
||||||
signalEndOfOperation()
|
signalEndOfOperation()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onDescriptorWrite(
|
||||||
|
gatt: BluetoothGatt,
|
||||||
|
descriptor: BluetoothGattDescriptor,
|
||||||
|
status: Int
|
||||||
|
) {
|
||||||
|
val op = pendingOperation
|
||||||
|
if (op is SetNotification &&
|
||||||
|
descriptor.uuid == CCC_DESCRIPTOR_UUID
|
||||||
|
&& op.charId == descriptor.characteristic.uuid && op.serviceId == descriptor.characteristic.service.uuid
|
||||||
|
) {
|
||||||
|
signalEndOfOperation()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCharacteristicChanged(
|
||||||
|
gatt: BluetoothGatt,
|
||||||
|
characteristic: BluetoothGattCharacteristic
|
||||||
|
) {
|
||||||
|
notifyListeners { listener -> listener.onCharChange(gatt, characteristic) }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,8 +28,6 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
import androidx.recyclerview.widget.SimpleItemAnimator
|
||||||
import com.example.sensortestingapp.databinding.ActivityMainBinding
|
import com.example.sensortestingapp.databinding.ActivityMainBinding
|
||||||
import com.punchthrough.blestarterappandroid.ble.BleListener
|
|
||||||
import com.punchthrough.blestarterappandroid.ble.ConnectionManager
|
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
import java.nio.ByteOrder
|
import java.nio.ByteOrder
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
@@ -138,21 +136,36 @@ class MainActivity : AppCompatActivity() {
|
|||||||
ConnectionManager.connect(scanResult.device, applicationContext)
|
ConnectionManager.connect(scanResult.device, applicationContext)
|
||||||
ConnectionManager.requestMtu(scanResult.device, Int.MAX_VALUE)
|
ConnectionManager.requestMtu(scanResult.device, Int.MAX_VALUE)
|
||||||
ConnectionManager.discoverServices(scanResult.device)
|
ConnectionManager.discoverServices(scanResult.device)
|
||||||
ConnectionManager.readChar(scanResult.device, DEMO_SERVICE_UUID, DEMO_CHAR_UUID)
|
//ConnectionManager.readChar(scanResult.device, DEMO_SERVICE_UUID, DEMO_CHAR_UUID)
|
||||||
|
ConnectionManager.enableNotification(
|
||||||
|
scanResult.device,
|
||||||
|
DEMO_SERVICE_UUID,
|
||||||
|
DEMO_CHAR_UUID
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val bleListener = object : BleListener() {
|
private val bleListener = object : BleListener() {
|
||||||
|
|
||||||
|
fun logDemoPayload(characteristic: BluetoothGattCharacteristic) {
|
||||||
|
if (characteristic.service.uuid == DEMO_SERVICE_UUID && characteristic.uuid == DEMO_CHAR_UUID) {
|
||||||
|
val payload = decodeDemoPayload(characteristic.value)
|
||||||
|
Log.i("BleListener", "Demo char received: $payload")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onSuccessfulCharRead(
|
override fun onSuccessfulCharRead(
|
||||||
gatt: BluetoothGatt,
|
gatt: BluetoothGatt,
|
||||||
characteristic: BluetoothGattCharacteristic
|
characteristic: BluetoothGattCharacteristic
|
||||||
) {
|
) {
|
||||||
|
logDemoPayload(characteristic)
|
||||||
|
}
|
||||||
|
|
||||||
if (characteristic.service.uuid == DEMO_SERVICE_UUID && characteristic.uuid == DEMO_CHAR_UUID) {
|
override fun onCharChange(
|
||||||
val payload = decodeDemoPayload(characteristic.value)
|
gatt: BluetoothGatt,
|
||||||
Log.i("BleListener", "Demo char received: $payload")
|
characteristic: BluetoothGattCharacteristic
|
||||||
|
) {
|
||||||
}
|
logDemoPayload(characteristic)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user