ขั้นตอนแรกในการโต้ตอบกับอุปกรณ์ BLE ก็คือการเชื่อมต่ออุปกรณ์นั้น เพิ่มเติม
ให้เชื่อมต่อกับเซิร์ฟเวอร์ GATT ในอุปกรณ์ วิธีเชื่อมต่อกับ GATT
ในอุปกรณ์ BLE ให้ใช้
connectGatt()
เมธอดนี้มีพารามิเตอร์ 3 ตัว ได้แก่
Context
ออบเจ็กต์ autoConnect
(บูลีน
ซึ่งระบุว่าจะเชื่อมต่อกับอุปกรณ์ BLE โดยอัตโนมัติในทันทีหรือไม่
ใหม่) และการอ้างอิงถึง
BluetoothGattCallback
:
var bluetoothGatt: BluetoothGatt? = null
...
bluetoothGatt = device.connectGatt(this, false, gattCallback)
bluetoothGatt = device.connectGatt(this, false, gattCallback);
ซึ่งจะเชื่อมต่อกับเซิร์ฟเวอร์ 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
}
}
}
class BluetoothLeService extends Service {
private Binder binder = new LocalBinder();
@Nullable
@Override
public IBinder onBind(Intent intent) {
return binder;
}
class LocalBinder extends Binder {
public BluetoothLeService getService() {
return BluetoothLeService.this;
}
}
}
กิจกรรมสามารถเริ่มบริการโดยใช้
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)
}
}
class DeviceControlActivity extends AppCompatActivity {
private BluetoothLeService bluetoothService;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
bluetoothService = ((LocalBinder) service).getService();
if (bluetoothService != null) {
// call functions on service to check connection and connect to devices
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
bluetoothService = null;
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.gatt_services_characteristics);
Intent gattServiceIntent = new Intent(this, BluetoothLeService.class);
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
}
...
}
class BluetoothLeService extends Service {
public static final String TAG = "BluetoothLeService";
private BluetoothAdapter bluetoothAdapter;
public boolean initialize() {
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
}
}
...
}
class DeviceControlsActivity extends AppCompatActivity {
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
bluetoothService = ((LocalBinder) service).getService();
if (bluetoothService != null) {
if (!bluetoothService.initialize()) {
Log.e(TAG, "Unable to initialize Bluetooth");
finish();
}
// perform device connection
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
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
} ?: run {
Log.w(TAG, "BluetoothAdapter not initialized")
return false
}
}
public boolean connect(final String address) {
if (bluetoothAdapter == null || address == null) {
Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
return false;
}
try {
final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address);
} catch (IllegalArgumentException exception) {
Log.w(TAG, "Device not found with provided address.");
return false;
}
// connect to the GATT server on the device
}
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
bluetooth.connect(deviceAddress)
}
}
override fun onServiceDisconnected(componentName: ComponentName) {
bluetoothService = null
}
}
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
bluetoothService = ((LocalBinder) service).getService();
if (bluetoothService != null) {
if (!bluetoothService.initialize()) {
Log.e(TAG, "Unable to initialize Bluetooth");
finish();
}
// perform device connection
bluetoothService.connect(deviceAddress);
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
bluetoothService = null;
}
};
ประกาศ Callback ของ GATT
เมื่อกิจกรรมแจ้งให้บริการทราบว่าต้องเชื่อมต่อกับอุปกรณ์ใดและบริการใด
เชื่อมต่อกับอุปกรณ์ บริการจะต้องเชื่อมต่อกับเซิร์ฟเวอร์ GATT บนอุปกรณ์
อุปกรณ์ BLE การเชื่อมต่อนี้ต้องใช้ BluetoothGattCallback
เพื่อรับ
การแจ้งเตือนเกี่ยวกับสถานะการเชื่อมต่อ การค้นพบบริการ ลักษณะเฉพาะ
การอ่าน และการแจ้งเตือนลักษณะเฉพาะ
หัวข้อนี้มุ่งเน้นไปที่การแจ้งเตือนสถานะการเชื่อมต่อ ดูโอน BLE ข้อมูลเกี่ยวกับวิธีดำเนินการ การค้นพบบริการ การอ่านลักษณะเฉพาะ และลักษณะของคำขอ การแจ้งเตือน
onConnectionStateChange()
จะทำงานเมื่อการเชื่อมต่อกับเซิร์ฟเวอร์ GATT ของอุปกรณ์มีการเปลี่ยนแปลง
ในตัวอย่างต่อไปนี้ มีการกำหนด Callback ในคลาส 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
}
}
}
private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
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
}
}
}
class BluetoothLeService extends Service {
...
private BluetoothGatt bluetoothGatt;
...
public boolean connect(final String address) {
if (bluetoothAdapter == null || address == null) {
Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
return false;
}
try {
final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address);
// connect to the GATT server on the device
bluetoothGatt = device.connectGatt(this, false, bluetoothGattCallback);
return true;
} catch (IllegalArgumentException exception) {
Log.w(TAG, "Device not found with provided address. Unable to connect.");
return false;
}
}
}
ข้อมูลอัปเดตเกี่ยวกับการออกอากาศ
เมื่อเซิร์ฟเวอร์เชื่อมต่อหรือยกเลิกการเชื่อมต่อจากเซิร์ฟเวอร์ GATT เซิร์ฟเวอร์จะต้องแจ้งเตือน กิจกรรมของรัฐใหม่ ซึ่งสามารถทำได้หลายวิธี ตัวอย่างต่อไปนี้ใช้ประกาศเพื่อส่ง ตั้งแต่บริการไปจนถึงกิจกรรม
บริการจะประกาศฟังก์ชันเพื่อประกาศสถานะใหม่ ฟังก์ชันนี้ใช้เวลา
ในสตริงการดำเนินการซึ่งส่งผ่านไปยังออบเจ็กต์ Intent
ก่อนออกอากาศ
เข้ากับระบบ
private fun broadcastUpdate(action: String) {
val intent = Intent(action)
sendBroadcast(intent)
}
private void broadcastUpdate(final String action) {
final Intent intent = new Intent(action);
sendBroadcast(intent);
}
เมื่อมีฟังก์ชันออกอากาศแล้ว ระบบจะนำไปใช้ภายใน
BluetoothGattCallback
เพื่อส่งข้อมูลเกี่ยวกับสถานะการเชื่อมต่อกับ
เซิร์ฟเวอร์ GATT มีการประกาศค่าคงที่และสถานะการเชื่อมต่อปัจจุบันของบริการ
ในบริการที่นำเสนอการดำเนินการ Intent
class BluetoothLeService : Service() {
private var connectionState = STATE_DISCONNECTED
private val bluetoothGattCallback: 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
}
}
class BluetoothLeService extends Service {
public final static String ACTION_GATT_CONNECTED =
"com.example.bluetooth.le.ACTION_GATT_CONNECTED";
public final static String ACTION_GATT_DISCONNECTED =
"com.example.bluetooth.le.ACTION_GATT_DISCONNECTED";
private static final int STATE_DISCONNECTED = 0;
private static final int STATE_CONNECTED = 2;
private int connectionState;
...
private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
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);
}
}
};
…
}
ฟังข้อมูลอัปเดตในกิจกรรม
เมื่อบริการเผยแพร่การอัปเดตการเชื่อมต่อ กิจกรรมจะต้อง
ใช้ 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())
if (bluetoothService != null) {
val result = bluetoothService!!.connect(deviceAddress)
Log.d(DeviceControlsActivity.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)
}
}
}
class DeviceControlsActivity extends AppCompatActivity {
...
private final BroadcastReceiver gattUpdateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
connected = true;
updateConnectionState(R.string.connected);
} else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
connected = false;
updateConnectionState(R.string.disconnected);
}
}
};
@Override
protected void onResume() {
super.onResume();
registerReceiver(gattUpdateReceiver, makeGattUpdateIntentFilter());
if (bluetoothService != null) {
final boolean result = bluetoothService.connect(deviceAddress);
Log.d(TAG, "Connect request result=" + result);
}
}
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(gattUpdateReceiver);
}
private static IntentFilter makeGattUpdateIntentFilter() {
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED);
intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED);
return intentFilter;
}
}
ในส่วนโอนข้อมูล 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
}
}
}
class BluetoothLeService extends Service {
...
@Override
public boolean onUnbind(Intent intent) {
close();
return super.onUnbind(intent);
}
private void close() {
if (bluetoothGatt == null) {
Return;
}
bluetoothGatt.close();
bluetoothGatt = null;
}
}