feat: read demo characteristic
This commit is contained in:
@@ -20,9 +20,11 @@ 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.BluetoothProfile
|
import android.bluetooth.BluetoothProfile
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import java.util.UUID
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue
|
import java.util.concurrent.ConcurrentLinkedQueue
|
||||||
|
|
||||||
@@ -51,6 +53,12 @@ data class MtuRequest(
|
|||||||
) : BleOperationType()
|
) : BleOperationType()
|
||||||
|
|
||||||
|
|
||||||
|
data class ReadChar(
|
||||||
|
override val device: BluetoothDevice,
|
||||||
|
val serviceId: UUID,
|
||||||
|
val charId: UUID
|
||||||
|
) : BleOperationType()
|
||||||
|
|
||||||
data class DiscoverServicesRequest(
|
data class DiscoverServicesRequest(
|
||||||
override val device: BluetoothDevice,
|
override val device: BluetoothDevice,
|
||||||
) : BleOperationType()
|
) : 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")
|
@SuppressLint("MissingPermission")
|
||||||
object ConnectionManager {
|
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
|
// - Beginning of PRIVATE functions
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
@@ -192,6 +220,16 @@ object ConnectionManager {
|
|||||||
gatt.discoverServices()
|
gatt.discoverServices()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is ReadChar -> with(operation) {
|
||||||
|
val characteristic = gatt.getService(serviceId)?.getCharacteristic(charId)
|
||||||
|
if (characteristic?.isReadable() == true) {
|
||||||
|
gatt.readCharacteristic(characteristic)
|
||||||
|
} else {
|
||||||
|
signalEndOfOperation()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
is Connect -> {
|
is Connect -> {
|
||||||
Log.e("ConnectionManager", "Shouldn't get here")
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,9 @@ 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.ConnectionManager
|
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
|
private const val ENABLE_BLUETOOTH_REQUEST_CODE = 1
|
||||||
@@ -39,6 +42,11 @@ private const val GATT_MAX_MTU_SIZE = 517
|
|||||||
// Top level declaration
|
// Top level declaration
|
||||||
private const val GATT_REQUESTED_MTU_SIZE = GATT_MAX_MTU_SIZE
|
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 {
|
fun Context.hasPermission(permissionType: String): Boolean {
|
||||||
return ContextCompat.checkSelfPermission(this, permissionType) ==
|
return ContextCompat.checkSelfPermission(this, permissionType) ==
|
||||||
PackageManager.PERMISSION_GRANTED
|
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")
|
@SuppressLint("MissingPermission")
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
@@ -101,9 +135,11 @@ 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|||||||
Reference in New Issue
Block a user