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
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.punchthrough.blestarterappandroid.ble
|
||||
package com.example.sensortestingapp
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.bluetooth.BluetoothDevice
|
||||
import android.bluetooth.BluetoothGatt
|
||||
import android.bluetooth.BluetoothGattCallback
|
||||
import android.bluetooth.BluetoothGattCharacteristic
|
||||
import android.bluetooth.BluetoothGattDescriptor
|
||||
import android.bluetooth.BluetoothProfile
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
@@ -28,6 +28,8 @@ import java.util.UUID
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
|
||||
val CCC_DESCRIPTOR_UUID = UUID.fromString("00002902-0000-1000-8000-00805F9B34FB")
|
||||
|
||||
private const val GATT_MIN_MTU_SIZE = 23
|
||||
|
||||
/** Maximum BLE MTU size as defined in gatt_api.h. */
|
||||
@@ -59,6 +61,14 @@ data class ReadChar(
|
||||
val charId: UUID
|
||||
) : BleOperationType()
|
||||
|
||||
|
||||
data class SetNotification(
|
||||
override val device: BluetoothDevice,
|
||||
val serviceId: UUID,
|
||||
val charId: UUID,
|
||||
val enable: Boolean
|
||||
) : BleOperationType()
|
||||
|
||||
data class DiscoverServicesRequest(
|
||||
override val device: BluetoothDevice,
|
||||
) : BleOperationType()
|
||||
@@ -70,6 +80,12 @@ open class BleListener {
|
||||
characteristic: BluetoothGattCharacteristic
|
||||
) {
|
||||
}
|
||||
|
||||
open fun onCharChange(
|
||||
gatt: BluetoothGatt,
|
||||
characteristic: BluetoothGattCharacteristic
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -173,6 +189,15 @@ object ConnectionManager {
|
||||
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
|
||||
|
||||
@Synchronized
|
||||
@@ -235,6 +260,7 @@ object ConnectionManager {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
when (operation) {
|
||||
is Disconnect -> with(operation) {
|
||||
Log.w("ConnectionManager", "Disconnecting from ${device.address}")
|
||||
@@ -256,12 +282,67 @@ object ConnectionManager {
|
||||
if (characteristic?.isReadable() == true) {
|
||||
gatt.readCharacteristic(characteristic)
|
||||
} else {
|
||||
Log.e("ConnectionManager", "Char $charId (${serviceId}) is not readable!")
|
||||
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 -> {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
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.SimpleItemAnimator
|
||||
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.ByteOrder
|
||||
import java.util.UUID
|
||||
@@ -138,21 +136,36 @@ class MainActivity : AppCompatActivity() {
|
||||
ConnectionManager.connect(scanResult.device, applicationContext)
|
||||
ConnectionManager.requestMtu(scanResult.device, Int.MAX_VALUE)
|
||||
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() {
|
||||
|
||||
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(
|
||||
gatt: BluetoothGatt,
|
||||
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")
|
||||
|
||||
logDemoPayload(characteristic)
|
||||
}
|
||||
|
||||
override fun onCharChange(
|
||||
gatt: BluetoothGatt,
|
||||
characteristic: BluetoothGattCharacteristic
|
||||
) {
|
||||
logDemoPayload(characteristic)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user