BluetoothGatt — คลาส Android ที่ให้ API สำหรับการทำงานของ GATT (Generic Attribute Profile) ไคลเอ็นต์ผ่านการเชื่อมต่อ BLE BluetoothGatt ห่อหุ้มการเชื่อมต่อไปยังเซิร์ฟเวอร์ GATT ระยะไกล (อุปกรณ์ BLE ต่อพ่วง) และจัดการการดำเนินการโปรไฟล์ทั้งหมด: การค้นหาบริการ การอ่านและเขียนคุณลักษณะ การสมัครรับการแจ้งเตือนและการบอกกล่าว อินสแตนซ์ BluetoothGatt ได้รับผ่าน BluetoothDevice.connectGatt() พร้อมกับ BluetoothGattCallback ตาม Android Developers, 2026 BluetoothGatt เป็นคลาสหลักสำหรับการสื่อสาร BLE แบบสองทิศทาง รองรับการดำเนินการ GATT ตั้งแต่ BLE 4.0 ถึง BLE 5.4
ประเด็นสำคัญ
BluetoothGatt — ออบเจกต์พรอกซีที่แสดงการเชื่อมต่อ GATT ระหว่างอุปกรณ์ Android (ศูนย์กลาง) และอุปกรณ์ต่อพ่วง BLE (เซิร์ฟเวอร์) แต่ละอินสแตนซ์ BluetoothGatt สอดคล้องกับการเชื่อมต่อ BLE ที่ทำงานอยู่หนึ่งรายการ การดำเนินการโปรไฟล์ GATT ทั้งหมดจะดำเนินการผ่านมัน: การค้นหา การอ่าน การเขียน การแจ้งเตือน BluetoothGatt ไม่ได้ถูกสร้างขึ้นโดยตรง — มันถูกส่งคืนโดยเมธอด BluetoothDevice.connectGatt()
การสร้าง BluetoothGatt ต้องใช้พารามิเตอร์สี่ตัว Context — บริบทของแอปพลิเคชัน (Activity หรือ Application) autoConnect — ถ้าเป็น false Android จะเริ่มการเชื่อมต่อโดยตรงทันที ถ้าเป็น true Android จะเชื่อมต่อโดยอัตโนมัติเมื่อตรวจพบอุปกรณ์ (มีประโยชน์สำหรับการเชื่อมต่อพื้นหลัง) BluetoothGattCallback — callback ที่จำเป็นสำหรับเหตุการณ์ GATT ทั้งหมด transport — BluetoothDevice.TRANSPORT_LE (BLE) หรือ TRANSPORT_BREDR (Classic) บนอุปกรณ์ BLE ให้ใช้ TRANSPORT_LE เสมอ
วงจรชีวิตของ BluetoothGatt ประกอบด้วยห้าสถานะ DISCONNECTED — สถานะเริ่มต้น CONNECTING — หลังจากเรียก connectGatt ก่อนการยืนยัน CONNECTED — หลังจาก onConnectionStateChange ด้วย STATE_CONNECTED หลังจากการเชื่อมต่อ จะเรียก discoverServices() เพื่อรับลำดับชั้น GATT หลังจากทำงานเสร็จ — disconnect() และ close() เพื่อปลดปล่อยทรัพยากรระบบ หากไม่มี close() แอปพลิเคชันอาจทำให้ขีดจำกัดการเชื่อมต่อ BLE ของ Android หมด (ปกติ 4–8)
// กำลังสร้างการเชื่อมต่อ BluetoothGatt
import android.bluetooth.*
class GattConnector(private val context: Context) {
private var bluetoothGatt: BluetoothGatt? = null
fun connect(device: BluetoothDevice): BluetoothGatt? {
// ปิดการเชื่อมต่อก่อนหน้าหากมี
close()
bluetoothGatt = device.connectGatt(
context,
false, // autoConnect = false ( )
object : BluetoothGattCallback() {
override fun onConnectionStateChange(
gatt: BluetoothGatt, status: Int, newState: Int
) {
when (newState) {
BluetoothProfile.STATE_CONNECTED -> {
// 2. การเชื่อมต่อสำเร็จ → การค้นหา
gatt.discoverServices()
}
BluetoothProfile.STATE_DISCONNECTED -> {
// 3. การเชื่อมต่อขาดหาย
close()
}
}
}
override fun onServicesDiscovered(
gatt: BluetoothGatt, status: Int
) {
if (status == BluetoothGatt.GATT_SUCCESS) {
// 4. รับลำดับชั้น GATT แล้ว
onGattReady(gatt)
}
}
},
BluetoothDevice.TRANSPORT_LE
)
return bluetoothGatt
}
private fun onGattReady(gatt: BluetoothGatt) {
// GATT พร้อมสำหรับการดำเนินการอ่าน/เขียน
}
fun close() {
bluetoothGatt?.disconnect()
bluetoothGatt?.close()
bluetoothGatt = null
}
}
คลาส GattConnector แสดงการสร้าง BluetoothGatt ที่ถูกต้อง พารามิเตอร์ autoConnect=false หมายถึงการเชื่อมต่อโดยตรง (สำหรับอุปกรณ์ที่สแกน) เมื่อ onConnectionStateChange ด้วย STATE_CONNECTED จะเรียก discoverServices() ทันที onServicesDiscovered บ่งชี้ว่า GATT พร้อมแล้ว close() เรียก disconnect() และ close() ตามลำดับ — หากไม่มี close() ทรัพยากรระบบจะไม่ถูกปลดปล่อย ทำให้เกิดการรั่วไหลของการเชื่อมต่อ BLE
discoverServices() — เมธอด GATT แรกที่ถูกเรียกหลังจากการเชื่อมต่อ มันเริ่มการค้นหาแบบอะซิงโครนัสของบริการทั้งหมดบนอุปกรณ์ต่อพ่วง BLE ผลลัพธ์มาใน onServicesDiscovered() พร้อมรหัสสถานะ: GATT_SUCCESS (0) — สำเร็จ, 133 — GATT_ERROR, 8 — GATT_CONNECTION_TIMEOUT หลังจากการค้นหาสำเร็จ BluetoothGatt จะเติมรายการบริการที่เข้าถึงได้ผ่าน getServices()
แต่ละ BluetoothGattService มีรายการ BluetoothGattCharacteristic คุณลักษณะมี UUID, คุณสมบัติ (PROPERTY_READ, PROPERTY_WRITE, PROPERTY_NOTIFY) และตัวอธิบายเพิ่มเติม คุณสมบัติกำหนดว่าการดำเนินการใดได้รับอนุญาต: หากคุณลักษณะไม่มี PROPERTY_READ การเรียก readCharacteristic จะส่งคืนข้อผิดพลาด ใช้ getDescriptors() เพื่อรับตัวอธิบายคุณลักษณะ
// การค้นหาบริการและการค้นหาคุณลักษณะ
class GattServiceExplorer {
// ค้นหาบริการตาม UUID
fun findService(gatt: BluetoothGatt, uuid: UUID): BluetoothGattService? {
return gatt.services?.firstOrNull { it.uuid == uuid }
}
// ค้นหาคุณลักษณะในบริการ
fun findCharacteristic(
service: BluetoothGattService,
uuid: UUID
): BluetoothGattCharacteristic? {
return service.characteristics?.firstOrNull { it.uuid == uuid }
}
// รับการดำเนินการคุณลักษณะที่รองรับทั้งหมด
fun getCharacteristicProperties(chars: BluetoothGattCharacteristic): List<String> {
val props = mutableListOf<String>()
with(chars.properties) {
if (and(BluetoothGattCharacteristic.PROPERTY_READ) != 0) props.add("READ")
if (and(BluetoothGattCharacteristic.PROPERTY_WRITE) != 0) props.add("เขียน")
if (and(BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE) != 0) props.add("เขียน_ไม่ตอบกลับ")
if (and(BluetoothGattCharacteristic.PROPERTY_NOTIFY) != 0) props.add("NOTIFY")
if (and(BluetoothGattCharacteristic.PROPERTY_INDICATE) != 0) props.add("INDICATE")
}
return props
}
// บันทึกลำดับชั้น GATT ทั้งหมด
fun dumpGattTree(gatt: BluetoothGatt) {
gatt.services?.forEach { service ->
print("Service: ${service.uuid}")
service.characteristics?.forEach { char ->
print(" Characteristic: ${char.uuid}, properties: ${char.properties}")
char.descriptors?.forEach { desc ->
print(" Descriptor: ${desc.uuid}")
}
}
}
}
}
คลาส GattServiceExplorer ให้ยูทิลิตี้สำหรับนำทางลำดับชั้น GATT findService และ findCharacteristic ค้นหาบริการและคุณลักษณะตาม UUID getCharacteristicProperties ตรวจสอบบิตมาสก์ของคุณสมบัติผ่าน and() dumpGattTree พิมพ์ลำดับชั้นทั้งหมดในบันทึก — มีประโยชน์เมื่อดีบักอุปกรณ์ BLE การดำเนินการทั้งหมดบน BluetoothGatt ควรดำเนินการหลังจาก onServicesDiscovered สำเร็จ มิฉะนั้น getServices() จะส่งคืนรายการว่าง
readCharacteristic() — เมธอดแบบอะซิงโครนัสของ BluetoothGatt สำหรับอ่านค่าคุณลักษณะจากอุปกรณ์ BLE ระยะไกล ผลลัพธ์มาใน onCharacteristicRead() ของ BluetoothGattCallback หากอุปกรณ์มี 2+ คุณลักษณะที่ตรงกัน (ไม่น่าเป็นไปได้แต่เป็นไปได้) readCharacteristic() อาจอ่านค่าที่ไม่ใช่เป้าหมาย — ปลอดภัยกว่าที่จะเรียก readCharacteristic() บนอินสแตนซ์ BluetoothGattCharacteristic แทนที่จะใช้ UUID
readDescriptor() — เมธอดสำหรับอ่านค่าตัวอธิบายคุณลักษณะ ตัวอธิบายทั่วไปคือ CCCD (Client Characteristic Configuration Descriptor, UUID 0x2902) ซึ่งกำหนดว่าการแจ้งเตือนถูกเปิดใช้งานหรือไม่ ผลลัพธ์ใน onDescriptorRead() การอ่านตัวอธิบายไม่ค่อยจำเป็นในทางปฏิบัติ — CCCD จัดการโดย setCharacteristicNotification() แต่สำหรับตัวอธิบายแบบกำหนดเอง (User Description 0x2901, Presentation Format 0x2904) readDescriptor() เป็นวิธีเดียวที่จะรับข้อมูลเมตา
MTU และการอ่านข้อมูลขนาดใหญ่ — หากค่าคุณลักษณะเกิน MTU (23 ไบต์สำหรับ BLE 4.0) Android จะแยกส่วนและประกอบข้อมูลโดยอัตโนมัติผ่านลำดับคำขออ่านผ่านสแต็ก BLE สำหรับ BLE 5.0+ ที่มี MTU แบบขยาย (สูงสุด 251 ไบต์) ไม่จำเป็นต้องแยกส่วน — การอ่านครั้งเดียวจะส่งคืนข้อมูลทั้งหมด ก่อนอ่าน คุณสามารถเรียก requestMtu() เพื่อเจรจา MTU สูงสุด
// อ่านคุณลักษณะและตัวอธิบายผ่าน BluetoothGatt
class GattReader {
fun readHeartRate(gatt: BluetoothGatt) {
// UUID บริการอัตราการเต้นของหัวใจ = 0x180D
val service = gatt.getService(UUID.fromString("0000180D-0000-1000-8000-00805F9B34FB"))
?: return
// UUID การวัดอัตราการเต้นของหัวใจ = 0x2A37
val characteristic = service.getCharacteristic(
UUID.fromString("00002A37-0000-1000-8000-00805F9B34FB")
) ?: return
if (hasProperty(characteristic, BluetoothGattCharacteristic.PROPERTY_READ)) {
gatt.readCharacteristic(characteristic)
}
}
fun readDescriptor(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
// CCCD (0x2902)
val cccd = characteristic.getDescriptor(
UUID.fromString("00002902-0000-1000-8000-00805F9B34FB")
)
cccd?.let { gatt.readDescriptor(it) }
}
// ประมวลผลข้อมูลใน callback onCharacteristicRead:
fun parseHeartRate(value: ByteArray): Int {
// BLE อัตราการเต้นของหัวใจ: bytes = flags, second = bpm
return if (value.isNotEmpty()) value[1].toInt() and 0xFF else 0
}
private fun hasProperty(char: BluetoothGattCharacteristic, prop: Int): Boolean {
return char.properties and prop != 0
}
}
คลาส GattReader แสดงการอ่านคุณลักษณะอัตราการเต้นของหัวใจ บริการ 0x180D มีคุณลักษณะ 0x2A37 (Heart Rate Measurement) — โปรไฟล์ BLE มาตรฐานของ Bluetooth SIG ก่อนอ่าน จะตรวจสอบคุณสมบัติ PROPERTY_READ ผ่าน hasProperty parseHeartRate แยกวิเคราะห์รูปแบบ BLE ของชีพจร: ไบต์แรก — flags (รูปแบบข้อมูล), ไบต์ที่สอง — ค่า bpm ตัวอธิบาย CCCD (0x2902) ถูกอ่านเพื่อตรวจสอบสถานะการแจ้งเตือน
writeCharacteristic() — เมธอด BluetoothGatt สำหรับเขียนข้อมูลไปยังอุปกรณ์ต่อพ่วง BLE บน Android API 33+ writeCharacteristic() ถูกแทนที่ด้วย writeCharacteristic(request) โดย BluetoothGattCharacteristicWriteRequest เป็นออบเจกต์คำขอที่มีคุณลักษณะ อาร์เรย์ไบต์ และ WriteType เมธอดเก่า writeCharacteristic(characteristic) ด้วย setValue() ไม่แนะนำแล้ว WriteType กำหนดพฤติกรรมของคำขอ: WRITE_TYPE_DEFAULT (ขึ้นอยู่กับคุณสมบัติคุณลักษณะ), WRITE_TYPE_NO_RESPONSE (ไม่มีการตอบกลับ) และ WRITE_TYPE_SIGNED (การอนุญาต)
การเลือก WriteType ส่งผลต่อความเร็วและความน่าเชื่อถือ WRITE_TYPE_DEFAULT มักจะสอดคล้องกับ withResponse (หากคุณลักษณะมี PROPERTY_WRITE) หรือ withoutResponse (หากมี PROPERTY_WRITE_NO_RESPONSE) สำหรับข้อมูลสตรีมมิ่ง (อัปเดต OTA, บันทึก) ให้ใช้ WRITE_TYPE_NO_RESPONSE — ปริมาณงานสูงสุด สำหรับคำสั่งที่ต้องการการรับประกันการส่ง (เปิดใช้งาน, กำหนดค่า) — WRITE_TYPE_DEFAULT พร้อมการยืนยันผ่าน onCharacteristicWrite
// การเขียนคุณลักษณะ BLE บน Android API 33+
class GattWriter {
// เขียนพร้อมการตอบกลับ (withResponse)
fun writeWithResponse(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, data: ByteArray) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
// API 33+: writeCharacteristic
val request = BluetoothGattCharacteristicWriteRequest(
characteristic,
data,
BluetoothGattCharacteristicWriteRequest.WRITE_TYPE_DEFAULT,
@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
)
gatt.writeCharacteristic(request)
} else {
// API < 33: (deprecated)
characteristic.setValue(data)
gatt.writeCharacteristic(characteristic)
}
}
// เขียนโดยไม่ตอบกลับ (ความเร็วสูงสุด)
fun writeWithoutResponse(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic, data: ByteArray) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val request = BluetoothGattCharacteristicWriteRequest(
characteristic,
data,
BluetoothGattCharacteristicWriteRequest.WRITE_TYPE_NO_RESPONSE,
@android.annotation.RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
)
gatt.writeCharacteristic(request)
} else {
characteristic.setValue(data)
characteristic.writeType = BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE
gatt.writeCharacteristic(characteristic)
}
}
// Callback onCharacteristicWrite (API 33+)
private val writeCallback = object : BluetoothGattCallback() {
override fun onCharacteristicWrite(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic,
value: ByteArray,
status: Int,
callbackType: Int
) {
if (status == BluetoothGatt.GATT_SUCCESS) {
print("Write success: ${value.size} bytes")
}
}
}
}
คลาส GattWriter รองรับ WriteType ทั้งสองสำหรับระดับ API ที่แตกต่างกัน writeWithResponse ใช้ WRITE_TYPE_DEFAULT — อุปกรณ์ BLE ยืนยันการเขียนผ่าน onCharacteristicWrite writeWithoutResponse ใช้ WRITE_TYPE_NO_RESPONSE — ข้อมูลถูกส่งโดยไม่มีการยืนยัน ปริมาณงานสูงสุด บน API 33+ ใช้ writeCharacteristic(request) ใหม่กับ BluetoothGattCharacteristicWriteRequest บน API < 33 — ใช้ setValue() + writeCharacteristic() แบบเก่า
setCharacteristicNotification() — เมธอด BluetoothGatt สำหรับสมัครรับการแจ้งเตือนเกี่ยวกับการเปลี่ยนแปลงคุณลักษณะบนอุปกรณ์ต่อพ่วง หลังจากเปิดใช้งานการสมัคร อุปกรณ์ BLE จะส่งค่าใหม่ผ่าน onCharacteristicChanged() อย่างไรก็ตาม setCharacteristicNotification() เพียงเปิดใช้งานการแจ้งเตือนภายในของ Android — เพื่อเปิดใช้งานการแจ้งเตือนบนอุปกรณ์ BLE เอง คุณต้องเขียนค่า 0x0100 ลงในตัวอธิบาย CCCD (0x2902) ด้วย
CCCD (Client Characteristic Configuration Descriptor) — ตัวอธิบายที่ควบคุมการส่งการแจ้งเตือนจากอุปกรณ์ต่อพ่วง BLE ค่า 0x0000 — ปิดการแจ้งเตือน, 0x0100 — เปิดการแจ้งเตือน, 0x0200 — เปิดการบอกกล่าว การเขียนใน CCCD ทำผ่าน writeDescriptor() บน BluetoothGatt หลังจากเรียก setCharacteristicNotification() Android ไม่ได้เขียน CCCD โดยอัตโนมัติ — ความรับผิดชอบนี้อยู่ที่นักพัฒนา
// การสมัครรับการแจ้งเตือน BLE ที่ถูกต้อง
class GattNotificationManager {
// 1. เปิดการแจ้งเตือน
fun enableNotification(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
// ขั้นตอนที่ 1: การสมัครภายใน Android
val success = gatt.setCharacteristicNotification(characteristic, true)
if (!success) {
print("ไม่สามารถสมัครได้")
return
}
// ขั้นตอนที่ 2: เขียน CCCD (0x2902) บนอุปกรณ์ BLE
val cccdDescriptor = characteristic.getDescriptor(
UUID.fromString("00002902-0000-1000-8000-00805F9B34FB")
) ?: return
// 0x0100 = การแจ้งเตือน, 0x0200 = การบอกกล่าว
val cccdValue = if (characteristic.properties
and BluetoothGattCharacteristic.PROPERTY_NOTIFY != 0) {
BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE // [0x01, 0x00]
} else {
BluetoothGattDescriptor.ENABLE_INDICATION_VALUE // [0x02, 0x00]
}
cccdDescriptor.setValue(cccdValue)
gatt.writeDescriptor(cccdDescriptor)
}
// 2. การค้นหาคุณลักษณะ
fun disableNotification(gatt: BluetoothGatt, characteristic: BluetoothGattCharacteristic) {
gatt.setCharacteristicNotification(characteristic, false)
val cccdDescriptor = characteristic.getDescriptor(
UUID.fromString("00002902-0000-1000-8000-00805F9B34FB")
) ?: return
cccdDescriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE)
gatt.writeDescriptor(cccdDescriptor)
}
// 3. Callback การแจ้งเตือน
private val notificationCallback = object : BluetoothGattCallback() {
override fun onCharacteristicChanged(
gatt: BluetoothGatt,
characteristic: BluetoothGattCharacteristic,
value: ByteArray,
callbackType: Int
) {
// ค่าใหม่จากอุปกรณ์ต่อพ่วง BLE
print("Notification: ${value.size} bytes")
}
}
}
คลาส GattNotificationManager ดำเนินโปรโตคอลการสมัครรับการแจ้งเตือน BLE สองขั้นตอนที่ถูกต้อง enableNotification เรียก setCharacteristicNotification(true) บน Android ก่อน จากนั้นเขียน 0x0100 ลงในตัวอธิบาย CCCD ผ่าน writeDescriptor disableNotification ดำเนินการย้อนกลับ หากไม่มีการเขียน CCCD อุปกรณ์ BLE จะไม่ส่งการแจ้งเตือน — นี่เป็นข้อผิดพลาดที่พบบ่อยที่สุดของนักพัฒนา BLE บน Android
ตัวอย่างสมบูรณ์ ของ GATT ไคลเอ็นต์ใน Kotlin ที่รวมการสร้าง BluetoothGatt การค้นหา การอ่าน และการสมัครรับการแจ้งเตือนในตัวจัดการเดียวโดยใช้โคโรทีนสำหรับการประมวลผลแบบอะซิงโครนัส
// GATT ไคลเอ็นต์ที่สมบูรณ์ด้วยโคโรทีนใน Kotlin
class GattClient(context: Context) {
private val context = context.applicationContext
private var gatt: BluetoothGatt? = null
// เชื่อมต่อด้วยโคโรทีน
suspend fun connect(device: BluetoothDevice): Boolean =
suspendCoroutine { continuation ->
gatt = device.connectGatt(
context, false,
object : BluetoothGattCallback() {
override fun onConnectionStateChange(
gatt: BluetoothGatt, status: Int, newState: Int
) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
gatt.discoverServices()
} else {
continuation.resume(false)
}
}
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
continuation.resume(status == BluetoothGatt.GATT_SUCCESS)
}
},
BluetoothDevice.TRANSPORT_LE
)
}
// อ่านคุณลักษณะผ่านโคโรทีน
suspend fun readCharacteristicValue(char: BluetoothGattCharacteristic): ByteArray? =
suspendCoroutine { continuation ->
gatt?.let { gatt ->
// บันทึกแท็กคุณลักษณะสำหรับการระบุ callback
gatt.setCharacteristic(char, null) // สำหรับ API < 33
gatt.readCharacteristic(char)
}
}
// ปิดการเชื่อมต่อ
fun release() {
gatt?.disconnect()
gatt?.close()
gatt = null
}
}
GATT ไคลเอ็นต์ GattClient ใช้โคโรทีน (suspendCoroutine) เพื่อแปลง API BluetoothGatt ที่ใช้ callback เป็นการเรียกตามลำดับ connect() รอ onServicesDiscovered หลังจากนั้นลำดับชั้น GATT พร้อมใช้งาน readCharacteristicValue() รอ onCharacteristicRead วิธีการนี้ช่วยกำจัดการซ้อนกันของ callback และทำให้โค้ด BLE เป็นเชิงเส้น release() รับประกันการทำความสะอาดทรัพยากร — การเรียกที่จำเป็นใน onDestroy ของ Activity หรือ ViewModel.onCleared
คำถามที่พบบ่อย
BluetoothGatt — คลาส Android สำหรับ GATT ไคลเอ็นต์ที่จัดการการเชื่อมต่อ BLE กับอุปกรณ์ต่อพ่วง มันถูกสร้างขึ้นผ่าน BluetoothDevice.connectGatt() ซึ่งให้เมธอด discoverServices(), readCharacteristic(), writeCharacteristic(), setCharacteristicNotification() ผลลัพธ์ของการดำเนินการทั้งหมดมาแบบอะซิงโครนัสผ่าน BluetoothGattCallback หากไม่มี BluetoothGatt การสื่อสาร BLE แบบสองทิศทางบน Android เป็นไปไม่ได้
สถานะ 133 (GATT_ERROR) หมายถึงข้อผิดพลาดภายในของสแต็ก BLE ของ Android สาเหตุ: อุปกรณ์ตัดการเชื่อมต่อระหว่างการค้นหา MTU ต่ำกว่าขั้นต่ำ (23 ไบต์) หรือสแต็ก BLE โหลดเกิน วิธีแก้ไข: ลอง discoverServices() อีกครั้งด้วยความล่าช้า 500 มิลลิวินาที ตรวจสอบ RSSI ของอุปกรณ์ และตรวจสอบให้แน่ใจว่าอุปกรณ์ต่อพ่วงรองรับการค้นหา GATT ในสถานะปัจจุบัน
สำหรับการเขียนด้วยการยืนยัน ให้เรียก writeCharacteristic() ด้วย WRITE_TYPE_DEFAULT (API 33+: BluetoothGattCharacteristicWriteRequest) เมื่อสำเร็จ อุปกรณ์ BLE จะส่งการยืนยัน และ Android จะเรียก onCharacteristicWrite ด้วย GATT_SUCCESS หากอุปกรณ์ไม่ตอบสนองภายใน 30 วินาที (หมดเวลาสแต็ก) callback จะส่งคืนสถานะข้อผิดพลาด สำหรับ watchdog ให้ใช้ Handler กับ postDelayed
บน Android 13+ (API 33) เมธอด BluetoothGatt เปลี่ยนแปลง: writeCharacteristic() ตอนนี้รับ BluetoothGattCharacteristicWriteRequest, readCharacteristic() — BluetoothGattCharacteristicReadRequest เมธอดเก่า setValue()/writeCharacteristic() ไม่แนะนำแล้ว BluetoothGattCallback ก็เปลี่ยนแปลงเช่นกัน: onCharacteristicRead(), onCharacteristicWrite(), onCharacteristicChanged() รับ ByteArray value และ callbackType ใช้ Build.VERSION.SDK_INT สำหรับการแยกสาขา
Android รองรับ 4–8 การเชื่อมต่อ BLE GATT พร้อมกัน (ขึ้นอยู่กับผู้ผลิตและเวอร์ชัน Android) Pixel/Google: สูงสุด 7, Samsung: สูงสุด 5, Xiaomi: สูงสุด 4 เมื่อเกินขีดจำกัด connectGatt จะส่งคืน null หรือ onConnectionStateChange พร้อมข้อผิดพลาด สำหรับการทำงานกับอุปกรณ์จำนวนมาก ให้ใช้การเชื่อมต่อแบบวนหรือ Bluetooth Mesh
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม