BluetoothGatt — คืออะไร เมธอด และโปรโตคอล GATT BLE ใน Android

ผู้แต่ง: IT Sectr เผยแพร่เมื่อ: 2026-07-16 เวลาอ่าน: 10 นาที

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 — คลาส Android สำหรับ GATT ไคลเอ็นต์ที่จัดการการเชื่อมต่อ BLE กับอุปกรณ์ต่อพ่วง
  • connectGatt() — เมธอด BluetoothDevice สำหรับสร้าง BluetoothGatt; รับบริบท autoConnect callback และ transport
  • discoverServices() — เมธอดเพื่อรับลำดับชั้น GATT: บริการ (BluetoothGattService), คุณลักษณะ (BluetoothGattCharacteristic)
  • readCharacteristic/writeCharacteristic — เมธอดการอ่านและเขียนพร้อมผลลัพธ์แบบอะซิงโครนัสผ่าน BluetoothGattCallback
  • setCharacteristicNotification — เมธอดสมัครรับการแจ้งเตือน BLE พร้อมการเขียนตัวอธิบาย CCCD ที่จำเป็น

BluetoothGatt คืออะไร: แก่นสารและการสร้างการเชื่อมต่อ

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)

kotlin
// กำลังสร้างการเชื่อมต่อ 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

การค้นหาบริการและคุณลักษณะผ่าน BluetoothGatt

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() เพื่อรับตัวอธิบายคุณลักษณะ

kotlin
// การค้นหาบริการและการค้นหาคุณลักษณะ
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() จะส่งคืนรายการว่าง

การอ่านคุณลักษณะ BLE: readCharacteristic และ readDescriptor

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 สูงสุด

kotlin
// อ่านคุณลักษณะและตัวอธิบายผ่าน 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 ด้วย WriteType

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

kotlin
// การเขียนคุณลักษณะ 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 และ CCCD

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 โดยอัตโนมัติ — ความรับผิดชอบนี้อยู่ที่นักพัฒนา

kotlin
// การสมัครรับการแจ้งเตือน 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 ด้วย BluetoothGattCallback

ตัวอย่างสมบูรณ์ ของ GATT ไคลเอ็นต์ใน Kotlin ที่รวมการสร้าง BluetoothGatt การค้นหา การอ่าน และการสมัครรับการแจ้งเตือนในตัวจัดการเดียวโดยใช้โคโรทีนสำหรับการประมวลผลแบบอะซิงโครนัส

kotlin
// 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 คืออะไร?

BluetoothGatt — คลาส Android สำหรับ GATT ไคลเอ็นต์ที่จัดการการเชื่อมต่อ BLE กับอุปกรณ์ต่อพ่วง มันถูกสร้างขึ้นผ่าน BluetoothDevice.connectGatt() ซึ่งให้เมธอด discoverServices(), readCharacteristic(), writeCharacteristic(), setCharacteristicNotification() ผลลัพธ์ของการดำเนินการทั้งหมดมาแบบอะซิงโครนัสผ่าน BluetoothGattCallback หากไม่มี BluetoothGatt การสื่อสาร BLE แบบสองทิศทางบน Android เป็นไปไม่ได้

ทำไม onServicesDiscovered ส่งคืนสถานะ 133?

สถานะ 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

วิธีทำงานกับ BLE บน Android 33+?

บน Android 13+ (API 33) เมธอด BluetoothGatt เปลี่ยนแปลง: writeCharacteristic() ตอนนี้รับ BluetoothGattCharacteristicWriteRequest, readCharacteristic() — BluetoothGattCharacteristicReadRequest เมธอดเก่า setValue()/writeCharacteristic() ไม่แนะนำแล้ว BluetoothGattCallback ก็เปลี่ยนแปลงเช่นกัน: onCharacteristicRead(), onCharacteristicWrite(), onCharacteristicChanged() รับ ByteArray value และ callbackType ใช้ Build.VERSION.SDK_INT สำหรับการแยกสาขา

Android รองรับการเชื่อมต่อ BLE กี่รายการ?

Android รองรับ 4–8 การเชื่อมต่อ BLE GATT พร้อมกัน (ขึ้นอยู่กับผู้ผลิตและเวอร์ชัน Android) Pixel/Google: สูงสุด 7, Samsung: สูงสุด 5, Xiaomi: สูงสุด 4 เมื่อเกินขีดจำกัด connectGatt จะส่งคืน null หรือ onConnectionStateChange พร้อมข้อผิดพลาด สำหรับการทำงานกับอุปกรณ์จำนวนมาก ให้ใช้การเชื่อมต่อแบบวนหรือ Bluetooth Mesh

สรุป

  • BluetoothGatt — GATT ไคลเอ็นต์ Android สำหรับการเชื่อมต่อ BLE สร้างผ่าน BluetoothDevice.connectGatt() ด้วย BluetoothGattCallback
  • discoverServices() — ขั้นตอนที่จำเป็นหลังการเชื่อมต่อเพื่อรับบริการ คุณลักษณะ และตัวอธิบายของอุปกรณ์ BLE
  • การอ่าน — readCharacteristic() พร้อมผลลัพธ์แบบอะซิงโครนัสใน onCharacteristicRead(); สำหรับข้อมูลขนาดใหญ่ต้องเจรจา MTU
  • การเขียน — writeCharacteristic() ด้วย WriteType: DEFAULT (withResponse) หรือ NO_RESPONSE (ไม่มีการยืนยัน)
  • การแจ้งเตือน — การเปิดใช้งานสองขั้นตอน: setCharacteristicNotification() + การเขียน CCCD (0x2902) ด้วยค่า 0x0100
  • API 33+ — เมธอดใหม่ writeCharacteristic(request) และ readCharacteristic(request) พร้อมออบเจกต์คำขอ
  • ปลดปล่อยทรัพยากร — การเรียกที่จำเป็น disconnect() และ close() เพื่อป้องกันการรั่วไหลของการเชื่อมต่อ BLE

เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร

IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ

ปรึกษาโครงการ

อ่านเพิ่มเติม