feat: read demo characteristic

This commit is contained in:
Fabian Christoffel
2023-06-20 15:14:34 +02:00
parent 5700bc8645
commit 96d1cb985d
2 changed files with 109 additions and 0 deletions

View File

@@ -20,9 +20,11 @@ import android.annotation.SuppressLint
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothGatt
import android.bluetooth.BluetoothGattCallback
import android.bluetooth.BluetoothGattCharacteristic
import android.bluetooth.BluetoothProfile
import android.content.Context
import android.util.Log
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentLinkedQueue
@@ -51,6 +53,12 @@ data class MtuRequest(
) : BleOperationType()
data class ReadChar(
override val device: BluetoothDevice,
val serviceId: UUID,
val charId: UUID
) : BleOperationType()
data class DiscoverServicesRequest(
override val device: BluetoothDevice,
) : BleOperationType()
@@ -76,6 +84,22 @@ private fun BluetoothGatt.printGattTable() {
}
fun BluetoothGattCharacteristic.isReadable(): Boolean =
containsProperty(BluetoothGattCharacteristic.PROPERTY_READ)
fun BluetoothGattCharacteristic.isWritable(): Boolean =
containsProperty(BluetoothGattCharacteristic.PROPERTY_WRITE)
fun BluetoothGattCharacteristic.isWritableWithoutResponse(): Boolean =
containsProperty(BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE)
fun BluetoothGattCharacteristic.containsProperty(property: Int): Boolean {
return properties and property != 0
}
fun ByteArray.toHexString(): String =
joinToString(separator = "", prefix = "0x") { String.format("%02X", it) }
@SuppressLint("MissingPermission")
object ConnectionManager {
@@ -114,6 +138,10 @@ object ConnectionManager {
}
fun readChar(device: BluetoothDevice, service: UUID, char: UUID) {
enqueueOperation(ReadChar(device, service, char))
}
// - Beginning of PRIVATE functions
@Synchronized
@@ -192,6 +220,16 @@ object ConnectionManager {
gatt.discoverServices()
}
is ReadChar -> with(operation) {
val characteristic = gatt.getService(serviceId)?.getCharacteristic(charId)
if (characteristic?.isReadable() == true) {
gatt.readCharacteristic(characteristic)
} else {
signalEndOfOperation()
}
}
is Connect -> {
Log.e("ConnectionManager", "Shouldn't get here")
@@ -260,6 +298,41 @@ object ConnectionManager {
}
}
override fun onCharacteristicRead(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic,
status: Int
) {
with(characteristic) {
when (status) {
BluetoothGatt.GATT_SUCCESS -> {
Log.i(
"ConnectionManager",
"Read characteristic $uuid (service: ${service.uuid}): ${value.toHexString()}"
)
}
BluetoothGatt.GATT_READ_NOT_PERMITTED -> {
Log.e(
"BluetoothGattCallback",
"Read not permitted for $uuid (service: ${service.uuid})!"
)
}
else -> {
Log.e(
"BluetoothGattCallback",
"Characteristic read failed for $uuid (service: ${service.uuid}), error: $status"
)
}
}
}
if (pendingOperation is ReadChar) {
signalEndOfOperation()
}
}
}

View File

@@ -27,6 +27,9 @@ import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.SimpleItemAnimator
import com.example.sensortestingapp.databinding.ActivityMainBinding
import com.punchthrough.blestarterappandroid.ble.ConnectionManager
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.util.UUID
private const val ENABLE_BLUETOOTH_REQUEST_CODE = 1
@@ -39,6 +42,11 @@ private const val GATT_MAX_MTU_SIZE = 517
// Top level declaration
private const val GATT_REQUESTED_MTU_SIZE = GATT_MAX_MTU_SIZE
private val DEMO_SERVICE_UUID = UUID.fromString("00000000-0001-11E1-9AB4-0002A5D5C51B")
private val DEMO_CHAR_UUID = UUID.fromString("00140000-0001-11E1-AC36-0002A5D5C51B")
fun Context.hasPermission(permissionType: String): Boolean {
return ContextCompat.checkSelfPermission(this, permissionType) ==
PackageManager.PERMISSION_GRANTED
@@ -53,6 +61,32 @@ fun Context.hasRequiredRuntimePermissions(): Boolean {
}
}
data class DemoPayload(val ts: Int, val pressure: Float, val temperature: Float)
fun bytesToUInt16(arr: ByteArray, start: Int): Int {
return ByteBuffer.wrap(arr, start, 2)
.order(ByteOrder.LITTLE_ENDIAN).short.toInt() and 0xFFFF
}
fun bytesToInt16(arr: ByteArray, start: Int): Short {
return ByteBuffer.wrap(arr, start, 2)
.order(ByteOrder.LITTLE_ENDIAN).short
}
fun bytesToInt32(arr: ByteArray, start: Int): Int {
return ByteBuffer.wrap(arr, start, 4)
.order(ByteOrder.LITTLE_ENDIAN).int
}
fun decodeDemoPayload(bytes: ByteArray): DemoPayload {
val ts = bytesToUInt16(bytes, 0)
val pressure = bytesToInt32(bytes, 2) / 100.0f
val temp = bytesToInt16(bytes, 6) / 10.0f;
return DemoPayload(ts, pressure, temp)
}
@SuppressLint("MissingPermission")
class MainActivity : AppCompatActivity() {
@@ -101,9 +135,11 @@ 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)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
WindowCompat.setDecorFitsSystemWindows(window, false)
super.onCreate(savedInstanceState)