ขั้นตอนแรกในการโต้ตอบกับอุปกรณ์ BLE คือการเชื่อมต่อกับอุปกรณ์ กล่าวโดยละเอียดคือการเชื่อมต่อกับเซิร์ฟเวอร์ GATT ในอุปกรณ์ หากต้องการเชื่อมต่อกับเซิร์ฟเวอร์ GATT
ในอุปกรณ์ BLE ให้ใช้วิธีconnectGatt()
เมธอดนี้ใช้พารามิเตอร์ 3 ตัว ได้แก่ ออบเจ็กต์
Context, autoConnect (บูลีน
ที่ระบุว่าจะเชื่อมต่อกับอุปกรณ์ BLE โดยอัตโนมัติทันทีที่
พร้อมใช้งานหรือไม่) และการอ้างอิงถึง
BluetoothGattCallback
var bluetoothGatt: BluetoothGatt? = null // ... bluetoothGatt = device.connectGatt(this, false, bluetoothGattCallback)
ซึ่งจะเชื่อมต่อกับเซิร์ฟเวอร์ GATT ที่โฮสต์โดยอุปกรณ์ BLE และส่งคืนอินสแตนซ์ BluetoothGatt ซึ่งคุณสามารถใช้เพื่อดำเนินการไคลเอ็นต์ GATT ได้ ผู้โทร (แอป Android)
คือไคลเอ็นต์ GATT โดย
BluetoothGattCallback จะใช้เพื่อแสดงผลลัพธ์แก่ไคลเอ็นต์ เช่น
สถานะการเชื่อมต่อ รวมถึงการดำเนินการอื่นๆ ของไคลเอ็นต์ GATT
ตั้งค่าบริการที่มีผลผูกพัน
ในตัวอย่างต่อไปนี้ แอป BLE จะมีกิจกรรม
(DeviceControlActivity) เพื่อเชื่อมต่อกับอุปกรณ์บลูทูธ แสดงข้อมูลอุปกรณ์
และแสดงบริการและลักษณะ GATT ที่อุปกรณ์รองรับ กิจกรรมนี้จะสื่อสารกับ Service ที่ชื่อ BluetoothLeService ตามข้อมูลที่ผู้ใช้ป้อน ซึ่งจะโต้ตอบกับอุปกรณ์ BLE ผ่าน BLE API การสื่อสารจะดำเนินการโดยใช้บริการที่เชื่อมโยงซึ่งช่วยให้กิจกรรมเชื่อมต่อกับ BluetoothLeService และเรียกใช้ฟังก์ชันเพื่อเชื่อมต่อกับอุปกรณ์ได้ BluetoothLeService ต้องมีการติดตั้งใช้งาน Binder ที่ให้สิทธิ์เข้าถึง
บริการสำหรับกิจกรรม
class BluetoothLeService : Service() { private val binder = LocalBinder() override fun onBind(intent: Intent): IBinder? { return binder } inner class LocalBinder : Binder() { fun getService(): BluetoothLeService { return this@BluetoothLeService } } }
กิจกรรมสามารถเริ่มบริการได้โดยใช้
bindService()
ส่ง Intent เพื่อเริ่ม
บริการ การติดตั้งใช้งาน ServiceConnection
เพื่อฟังเหตุการณ์การเชื่อมต่อและการยกเลิกการเชื่อมต่อ และแฟล็ก
เพื่อระบุตัวเลือกการเชื่อมต่อเพิ่มเติม
class DeviceControlActivity : AppCompatActivity() { private var bluetoothService: BluetoothLeService? = null // Code to manage Service lifecycle. private val serviceConnection: ServiceConnection = object : ServiceConnection { override fun onServiceConnected( componentName: ComponentName, service: IBinder ) { bluetoothService = (service as LocalBinder).getService() bluetoothService?.let { bluetooth -> // call functions on service to check connection and connect to devices } } override fun onServiceDisconnected(componentName: ComponentName) { bluetoothService = null } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.gatt_services_characteristics) val gattServiceIntent = Intent(this, BluetoothLeService::class.java) bindService(gattServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE) } }
ตั้งค่า BluetoothAdapter
เมื่อผูกบริการแล้ว บริการจะต้องเข้าถึงBluetoothAdapter โดยควร
ตรวจสอบว่าอะแดปเตอร์พร้อมใช้งานในอุปกรณ์ อ่านข้อมูลเพิ่มเติมเกี่ยวกับBluetoothAdapter ได้ที่ตั้งค่า
บลูทูธ ตัวอย่างต่อไปนี้จะรวมโค้ดการตั้งค่านี้ไว้ในฟังก์ชัน initialize() ซึ่งแสดงผลค่า Boolean ที่บ่งบอกถึงความสำเร็จ
private const val TAG = "BluetoothLeService" class BluetoothLeService : Service() { private var bluetoothAdapter: BluetoothAdapter? = null fun initialize(): Boolean { bluetoothAdapter = BluetoothAdapter.getDefaultAdapter() if (bluetoothAdapter == null) { Log.e(TAG, "Unable to obtain a BluetoothAdapter.") return false } return true } // ... }
กิจกรรมจะเรียกใช้ฟังก์ชันนี้ภายในServiceConnectionการติดตั้งใช้งาน
การจัดการค่าที่ส่งคืนเป็นเท็จจากฟังก์ชัน initialize() ขึ้นอยู่กับแอปพลิเคชันของคุณ คุณอาจแสดงข้อความแสดงข้อผิดพลาดแก่ผู้ใช้เพื่อระบุว่า
อุปกรณ์ปัจจุบันไม่รองรับการทำงานของบลูทูธ หรือปิดใช้ฟีเจอร์ใดๆ
ที่ต้องใช้บลูทูธในการทำงาน ในตัวอย่างต่อไปนี้
finish() จะเรียกใช้ในกิจกรรม
เพื่อส่งผู้ใช้กลับไปยังหน้าจอก่อนหน้า
class DeviceControlActivity : AppCompatActivity() { // Code to manage Service lifecycle. private val serviceConnection: ServiceConnection = object : ServiceConnection { override fun onServiceConnected( componentName: ComponentName, service: IBinder ) { bluetoothService = (service as LocalBinder).getService() bluetoothService?.let { bluetooth -> if (!bluetooth.initialize()) { Log.e(TAG, "Unable to initialize Bluetooth") finish() } // perform device connection } } override fun onServiceDisconnected(componentName: ComponentName) { bluetoothService = null } } // ... }
เชื่อมต่อกับอุปกรณ์
เมื่อเริ่มต้นอินสแตนซ์ BluetoothLeService แล้ว อินสแตนซ์จะเชื่อมต่อกับอุปกรณ์ BLE
ได้ กิจกรรมต้องส่งที่อยู่อุปกรณ์ไปยังบริการเพื่อให้บริการเริ่มการเชื่อมต่อได้ โดยบริการจะโทรหา
getRemoteDevice()
ในวันที่ BluetoothAdapter เพื่อเข้าถึงอุปกรณ์ หากอะแดปเตอร์ไม่พบ
อุปกรณ์ที่มีที่อยู่นั้น getRemoteDevice() จะส่ง
IllegalArgumentException
fun connect(address: String): Boolean { bluetoothAdapter?.let { adapter -> try { val device = adapter.getRemoteDevice(address) } catch (exception: IllegalArgumentException) { Log.w(TAG, "Device not found with provided address.") return false } // connect to the GATT server on the device return true } ?: run { Log.w(TAG, "BluetoothAdapter not initialized") return false } }
DeviceControlActivity จะเรียกใช้ฟังก์ชัน connect() นี้เมื่อเริ่มต้นบริการแล้ว
กิจกรรมต้องส่งที่อยู่อุปกรณ์ BLE ใน
ตัวอย่างต่อไปนี้ ระบบจะส่งที่อยู่อุปกรณ์ไปยังกิจกรรมเป็นส่วนเสริมของ Intent
// Code to manage Service lifecycle. private val serviceConnection: ServiceConnection = object : ServiceConnection { override fun onServiceConnected( componentName: ComponentName, service: IBinder ) { bluetoothService = (service as LocalBinder).getService() bluetoothService?.let { bluetooth -> if (!bluetooth.initialize()) { Log.e(TAG, "Unable to initialize Bluetooth") finish() } // perform device connection deviceAddress?.let { bluetooth.connect(it) } } } override fun onServiceDisconnected(componentName: ComponentName) { bluetoothService = null } }
ประกาศการเรียกกลับ GATT
เมื่อกิจกรรมบอกบริการว่าควรเชื่อมต่อกับอุปกรณ์ใดและบริการ
เชื่อมต่อกับอุปกรณ์แล้ว บริการจะต้องเชื่อมต่อกับเซิร์ฟเวอร์ GATT ใน
อุปกรณ์ BLE การเชื่อมต่อนี้ต้องใช้ BluetoothGattCallback เพื่อรับ
การแจ้งเตือนเกี่ยวกับสถานะการเชื่อมต่อ การค้นพบบริการ การอ่านลักษณะ
และการแจ้งเตือนลักษณะ
หัวข้อนี้จะเน้นที่การแจ้งเตือนสถานะการเชื่อมต่อ ดูวิธีค้นหาบริการ อ่านลักษณะ และขอการแจ้งเตือนลักษณะได้ที่โอนข้อมูล BLE
ฟังก์ชัน
onConnectionStateChange()
จะทริกเกอร์เมื่อการเชื่อมต่อกับเซิร์ฟเวอร์ GATT ของอุปกรณ์มีการเปลี่ยนแปลง
ในตัวอย่างต่อไปนี้ มีการกำหนดการเรียกกลับในคลาส Service เพื่อให้ใช้กับ BluetoothDevice ได้เมื่อบริการเชื่อมต่อกับคลาส
private val bluetoothGattCallback = object : BluetoothGattCallback() { override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) { if (newState == BluetoothProfile.STATE_CONNECTED) { // successfully connected to the GATT Server } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { // disconnected from the GATT Server } } }
เชื่อมต่อกับบริการ GATT
เมื่อประกาศ BluetoothGattCallback แล้ว บริการจะใช้ออบเจ็กต์
BluetoothDevice จากฟังก์ชัน connect() เพื่อเชื่อมต่อกับบริการ GATT
ในอุปกรณ์ได้
ใช้ฟังก์ชัน
connectGatt()
ซึ่งต้องมีออบเจ็กต์ Context, แฟล็กบูลีน autoConnect และ BluetoothGattCallback ในตัวอย่างนี้ แอปเชื่อมต่อกับอุปกรณ์ BLE โดยตรง ดังนั้นจึงมีการส่ง false สำหรับ autoConnect
นอกจากนี้ยังมีการเพิ่มพร็อพเพอร์ตี้ BluetoothGatt ด้วย ซึ่งช่วยให้บริการปิด
การเชื่อมต่อเมื่อไม่
จำเป็นอีกต่อไป
class BluetoothLeService : Service() { // ... private var bluetoothGatt: BluetoothGatt? = null // ... fun connect(address: String): Boolean { bluetoothAdapter?.let { adapter -> try { val device = adapter.getRemoteDevice(address) // connect to the GATT server on the device bluetoothGatt = device.connectGatt(this, false, bluetoothGattCallback) return true } catch (exception: IllegalArgumentException) { Log.w(TAG, "Device not found with provided address. Unable to connect.") return false } } ?: run { Log.w(TAG, "BluetoothAdapter not initialized") return false } } }
การอัปเดตการออกอากาศ
เมื่อเซิร์ฟเวอร์เชื่อมต่อหรือยกเลิกการเชื่อมต่อกับเซิร์ฟเวอร์ GATT เซิร์ฟเวอร์จะต้องแจ้ง กิจกรรมของสถานะใหม่ ซึ่งทำได้หลายวิธี ตัวอย่างต่อไปนี้ใช้ broadcasts เพื่อส่ง ข้อมูลจากบริการไปยังกิจกรรม
บริการจะประกาศฟังก์ชันเพื่อออกอากาศสถานะใหม่ ฟังก์ชันนี้รับสตริงการดำเนินการซึ่งส่งไปยังออบเจ็กต์ Intent ก่อนที่จะออกอากาศไปยังระบบ
private fun broadcastUpdate(action: String) { val intent = Intent(action) sendBroadcast(intent) }
เมื่อฟังก์ชันการออกอากาศพร้อมใช้งานแล้ว ฟังก์ชันนี้จะใช้ภายใน
BluetoothGattCallback เพื่อส่งข้อมูลเกี่ยวกับสถานะการเชื่อมต่อกับเซิร์ฟเวอร์
GATT ประกาศค่าคงที่และสถานะการเชื่อมต่อปัจจุบันของบริการ
ในบริการที่แสดงการดำเนินการ Intent
class BluetoothLeService : Service() { private var connectionState = STATE_DISCONNECTED private val bluetoothGattCallback = object : BluetoothGattCallback() { override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) { if (newState == BluetoothProfile.STATE_CONNECTED) { // successfully connected to the GATT Server connectionState = STATE_CONNECTED broadcastUpdate(ACTION_GATT_CONNECTED) } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { // disconnected from the GATT Server connectionState = STATE_DISCONNECTED broadcastUpdate(ACTION_GATT_DISCONNECTED) } } } // ... companion object { const val ACTION_GATT_CONNECTED = "com.example.bluetooth.le.ACTION_GATT_CONNECTED" const val ACTION_GATT_DISCONNECTED = "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED" private const val STATE_DISCONNECTED = 0 private const val STATE_CONNECTED = 2 } }
ฟังข้อมูลอัปเดตในกิจกรรม
เมื่อบริการออกอากาศการอัปเดตการเชื่อมต่อแล้ว กิจกรรมจะต้อง
ใช้ BroadcastReceiver
ลงทะเบียนตัวรับนี้เมื่อตั้งค่ากิจกรรม และยกเลิกการลงทะเบียนเมื่อ
กิจกรรมออกจากหน้าจอ การฟังเหตุการณ์จากบริการ
กิจกรรมจะอัปเดตอินเทอร์เฟซผู้ใช้ได้ตาม
สถานะการเชื่อมต่อปัจจุบันกับอุปกรณ์ BLE
class DeviceControlActivity : AppCompatActivity() { private val gattUpdateReceiver: BroadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { when (intent.action) { BluetoothLeService.ACTION_GATT_CONNECTED -> { connected = true updateConnectionState(R.string.connected) } BluetoothLeService.ACTION_GATT_DISCONNECTED -> { connected = false updateConnectionState(R.string.disconnected) } } } } override fun onResume() { super.onResume() registerReceiver(gattUpdateReceiver, makeGattUpdateIntentFilter()) bluetoothService?.let { service -> deviceAddress?.let { address -> val result = service.connect(address) Log.d(TAG, "Connect request result=$result") } } } override fun onPause() { super.onPause() unregisterReceiver(gattUpdateReceiver) } private fun makeGattUpdateIntentFilter(): IntentFilter { return IntentFilter().apply { addAction(BluetoothLeService.ACTION_GATT_CONNECTED) addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED) } } }
ในโอนข้อมูล BLE
ระบบจะใช้ BroadcastReceiver เพื่อสื่อสารการค้นพบบริการ
รวมถึงข้อมูลลักษณะจากอุปกรณ์ด้วย
ปิดการเชื่อมต่อ GATT
ขั้นตอนสำคัญอย่างหนึ่งเมื่อต้องจัดการกับการเชื่อมต่อบลูทูธคือการปิดการเชื่อมต่อเมื่อใช้งานเสร็จแล้ว โดยเรียกใช้close()
ฟังก์ชันในออบเจ็กต์ BluetoothGatt ในตัวอย่างต่อไปนี้ บริการ
จะมีการอ้างอิงถึง BluetoothGatt เมื่อกิจกรรมยกเลิกการเชื่อมโยงกับบริการ ระบบจะปิดการเชื่อมต่อเพื่อป้องกันไม่ให้แบตเตอรี่ของอุปกรณ์หมด
class BluetoothLeService : Service() { // ... override fun onUnbind(intent: Intent?): Boolean { close() return super.onUnbind(intent) } private fun close() { bluetoothGatt?.let { gatt -> gatt.close() bluetoothGatt = null } } }