
??想了解更多關(guān)于開(kāi)源的內(nèi)容,請(qǐng)?jiān)L問(wèn):??
??51CTO 開(kāi)源基礎(chǔ)軟件社區(qū)??
??https://ost.51cto.com??
OpenHarmony BLE藍(lán)牙設(shè)備連接
1、簡(jiǎn)介
OpenHarmony藍(lán)牙模塊提供了基礎(chǔ)的傳統(tǒng)藍(lán)牙能力以及BLE的掃描、廣播等功能,這里將介紹如何通過(guò)OpenHarmony提供的@ohos.bluetooth (藍(lán)牙接口)打開(kāi)當(dāng)前設(shè)備的藍(lán)牙,關(guān)閉藍(lán)牙,以及連接BLE藍(lán)牙設(shè)備。
2、設(shè)備與環(huán)境
- 設(shè)備:九聯(lián)s905l3a機(jī)頂盒、開(kāi)鴻智谷學(xué)生卡BLE藍(lán)牙設(shè)備
- 系統(tǒng):OpenHarmony 3.2 beta2
- SDK:9
演示視頻:??OpenHarmony BLE藍(lán)牙設(shè)備連接??
3、邏輯流程

首先機(jī)頂盒在開(kāi)始的時(shí)候獲取藍(lán)牙相關(guān)權(quán)限,然后通過(guò)OpenHarmony提供的藍(lán)牙接口打開(kāi)藍(lán)牙,接著訂閱發(fā)現(xiàn)BLE設(shè)備發(fā)現(xiàn)事件,然后通過(guò)OpenHarmony提供的藍(lán)牙接口開(kāi)啟BLE設(shè)備掃描,當(dāng)發(fā)現(xiàn)到了BLE藍(lán)牙設(shè)備后,進(jìn)行上報(bào),BLE設(shè)備發(fā)現(xiàn)事件觸發(fā),獲取到來(lái)自BLE設(shè)備的廣播信息包,然后進(jìn)行BLE藍(lán)牙連接。
4、實(shí)現(xiàn)過(guò)程
(1)獲取藍(lán)牙相關(guān)權(quán)限
在使用藍(lán)牙接口之前,首先要讓設(shè)備獲取一下權(quán)限:
- ohos.permission.USE_BLUETOOTH // 允許應(yīng)用查看藍(lán)牙的配置。
- ohos.permission.DISCOVER_BLUETOOTH // 允許應(yīng)用配置本地藍(lán)牙,查找遠(yuǎn)端設(shè)備且與之配對(duì)連接。
- ohos.permission.LOCATION // 允許應(yīng)用獲取設(shè)備位置信息。
- ohos.permission.MANAGE_BLUETOOTH // 允許應(yīng)用配對(duì)藍(lán)牙設(shè)備,并對(duì)設(shè)備的電話簿或消息進(jìn)行訪問(wèn)。
打開(kāi)DevEco Studio 3.1.0.200,創(chuàng)建新的Stage項(xiàng)目,在項(xiàng)目中的module.json文件中添加相關(guān)權(quán)限:
"requestPermissions": [
{
"name": "ohos.permission.USE_BLUETOOTH",
"reason": "$string:grant_use_bluetooth",
"usedScene": {
"abilities": [
"MainAbility"
],
"when": "inuse"
}
},
{
"name": "ohos.permission.DISCOVER_BLUETOOTH",
"reason": "$string:grant_discovery_bluetooth",
"usedScene": {
"abilities": [
"MainAbility"
],
"when": "inuse"
}
},
{
"name": "ohos.permission.LOCATION",
"reason": "$string:grant_location",
"usedScene": {
"abilities": [
"MainAbility"
],
"when": "inuse"
}
},
{
"name": "ohos.permission.MANAGE_BLUETOOTH",
"reason": "$string:grant_manage_bluetooth",
"usedScene": {
"abilities": [
"MainAbility"
],
"when": "inuse"
}
}
]
(2)打開(kāi)設(shè)備的藍(lán)牙
首先,通過(guò)調(diào)用 bluetooth.getState() 藍(lán)牙接口來(lái)獲取當(dāng)前設(shè)備藍(lán)牙是否打開(kāi),并設(shè)置藍(lán)牙開(kāi)關(guān)的標(biāo)識(shí)位 isOn。
async aboutToAppear() {
// 等待獲取藍(lán)牙權(quán)限
await globalThis.abilityContext.requestPermissionsFromUser(['ohos.permission.USE_BLUETOOTH', 'ohos.permission.DISCOVER_BLUETOOTH', 'ohos.permission.LOCATION', 'ohos.permission.MANAGE_BLUETOOTH'])
logger.info(TAG, `獲取權(quán)限 grantPermission,requestPermissionsFromUser,PermissionRequestResult`)
// 獲取藍(lán)牙狀態(tài)
let state = bluetooth.getState()
// 判斷當(dāng)前設(shè)備藍(lán)牙是否打開(kāi)
if (state === bluetooth.BluetoothState.STATE_ON) {
this.isOn = true
}
if (state === bluetooth.BluetoothState.STATE_OFF) {
this.isOn = false
}
}
如果當(dāng)前設(shè)備藍(lán)牙未打開(kāi),則通過(guò)調(diào)用 bluetooth.enableBluetooth() 藍(lán)牙接口來(lái)打開(kāi)藍(lán)牙。
// 打開(kāi)藍(lán)牙函數(shù)
initBluetooth() {
this.enable = bluetooth.enableBluetooth()
// 判斷藍(lán)牙是否成功打開(kāi)
if(this.enable==true){
prompt.showToast({
message: 'Open bluetooth ' + this.enable,
duration: 2000,
});
}
}
(3)注冊(cè)發(fā)現(xiàn)BLE設(shè)備監(jiān)聽(tīng)器
在設(shè)備打開(kāi)藍(lán)牙之后,通過(guò)調(diào)用 bluetooth.BLE.on('BLEDeviceFind') 藍(lán)牙接口來(lái)訂閱BLE設(shè)備發(fā)現(xiàn)上報(bào)事件。該接口參數(shù)如下:

通過(guò)注冊(cè)發(fā)現(xiàn)BLE設(shè)備監(jiān)聽(tīng)器,可以得到發(fā)現(xiàn)設(shè)備的集合,BLE設(shè)備的廣播包、地址、信號(hào)強(qiáng)度rssi,在這里發(fā)現(xiàn)獲取連接BLE設(shè)備名字的接口 getDeviceName 無(wú)法成功調(diào)用,所以自己通過(guò)解析廣播包來(lái)獲取設(shè)備名字。
// 訂閱BLE設(shè)備發(fā)現(xiàn)上報(bào)事件
// 獲取到的data包括BLE設(shè)備的廣播包、地址、信號(hào)強(qiáng)度rssi
bluetooth.BLE.on('BLEDeviceFind', (data) => {
logger.info(TAG, `enter on bluetoothBLEDeviceFind`)
logger.info("rgytl 開(kāi)始掃描設(shè)備地址! 1")
if (data !== null && data.length > 0) {
logger.info("rgytl 開(kāi)始掃描設(shè)備地址! 2")
if (this.discoveryBleList.indexOf(data[0]) === -1) {
// 把發(fā)現(xiàn)的設(shè)備地址存入列表
this.discoveryBleList.push(data[0].deviceId)
logger.info("rgytl ---- discoveryBleList = "+JSON.stringify(this.discoveryBleList))
// 讀取廣播包,解析廣播包,得到設(shè)備名字,并存入設(shè)備列表
var i = 0;
var x = data[0].data[i]
var y = data[0].data[i + 1]
while(y!=0x09 && i+x+2<data[0].data.byteLength){
i = i+x+1
x = data[0].data[i]
y = data[0].data[i+1]
}
let arr = data[0].data.slice(i+2,i+x+1)
var BLEName = ""
for(let j=0;j<arr.byteLength;j++){
BLEName+=String.fromCharCode(arr[j])
}
logger.info("rgytl ---- discoveryBleList = "+BLEName)
// 把通過(guò)廣播包解析的BLE設(shè)備名字存入設(shè)備名字列表
this.BleInfo.push(BLEName)
// 把發(fā)現(xiàn)的BLE設(shè)備信號(hào)存入設(shè)備信號(hào)強(qiáng)度列表
this.BleRssi.push(data[0].rssi)
logger.info("rgytl ---- discoveryBleList = "+data[0].rssi)
}
logger.info(TAG, `開(kāi)啟掃描 discoveryBleList = ${JSON.stringify(this.discoveryBleList)}`)
}
})
(4)開(kāi)啟BLE設(shè)備掃描
在完成訂閱BLE設(shè)備發(fā)現(xiàn)上報(bào)事件后,通過(guò)調(diào)用 bluetooth.BLE.startBLEScan 接口去開(kāi)啟BLE設(shè)備掃描,通過(guò)該接口,可以對(duì)掃描BLE設(shè)備進(jìn)行過(guò)濾,可以過(guò)濾的參數(shù)有:BLE設(shè)備的地址、名字、以及服務(wù)的UUID等。

在這里,我設(shè)置只掃描包含我BLE設(shè)備名字的BLE設(shè)備,這樣子就不會(huì)說(shuō)掃描到一大堆其他的BLE設(shè)備,影響使用,只需要開(kāi)啟一次掃描和訂閱一次BLE設(shè)備發(fā)現(xiàn)上報(bào)事件就可以了,使用的時(shí)候只要沒(méi)有關(guān)閉,就不需要重復(fù)調(diào)用。
// 設(shè)置藍(lán)牙BLE掃描模式(根據(jù)名字掃描)
bluetooth.BLE.startBLEScan(
[{
deviceId: null,
name: "ble slave test",
serviceUuid: null
}],
{
interval: 0,
dutyMode: bluetooth.ScanDuty.SCAN_MODE_LOW_POWER,
matchMode: bluetooth.MatchMode.MATCH_MODE_AGGRESSIVE,
}
)
(5)連接BLE設(shè)備
在掃描到BLE設(shè)備之后,可以通過(guò) on(‘BLEConnectionStateChange’) 來(lái)訂閱獲取BLE設(shè)備的連接狀態(tài)變化事件,在使用該接口之前,要先通過(guò) bluetooth.BLE.createGattClientDevice('XX:XX:XX:XX:XX:XX') 接口創(chuàng)建一個(gè)可使用的GattClientDevice實(shí)例。

// 訂閱BEL狀態(tài)變化
if(this.BleOnflag){
// 只創(chuàng)建一個(gè)GattClient對(duì)象
this.BleOnflag = false
this.BLEDevice = bluetooth.BLE.createGattClientDevice(item);
// 訂閱獲取BLE設(shè)備的連接狀態(tài)變化事件
this.BLEDevice.on('BLEConnectionStateChange', (data) => {
console.log('bluetooth connectState state changed');
let connectState = data.state;
// 根據(jù)不通的連接狀態(tài),提示不同的信息
if(JSON.stringify(connectState) == 0){
logger.info(`connectState = ${JSON.stringify(connectState)},斷開(kāi)連接`)
prompt.showToast({
message: '斷開(kāi)連接',
duration: 2000,
});
} else if(JSON.stringify(connectState) == 2){
logger.info(`connectState = ${JSON.stringify(connectState)},連接成功`)
prompt.showToast({
message: '連接成功',
duration: 2000,
});
} else if(JSON.stringify(connectState) == 1){
logger.info(`connectState = ${JSON.stringify(connectState)},正在連接`)
} else {
logger.info(`connectState = ${JSON.stringify(connectState)},正在斷連`)
}
logger.info(`connectState = ${JSON.stringify(connectState)}`);
})
}
在前面通過(guò) bluetooth.BLE.createGattClientDevice(item) 創(chuàng)建一個(gè)GattClientDevice實(shí)例 BLEDevice 后,我們可以通過(guò)該實(shí)例去調(diào)用 connect() 方法連接BLE設(shè)備。注意,GattClientDevice實(shí)例 只需要?jiǎng)?chuàng)建一個(gè)就可以。

// 連接藍(lán)牙
let BLEConnect = this.BLEDevice.connect()
// 如果連接成功,則把BLE設(shè)備存入連接成功列表
if(BLEConnect){
this.deviceBleList.push(item)
}
(6)結(jié)尾處理
當(dāng)不連接BLE設(shè)備的時(shí)候,要記得關(guān)閉BLE設(shè)備掃描,取消訂閱設(shè)備發(fā)現(xiàn)事件。
取消BLE設(shè)備連接,通過(guò)之前創(chuàng)建的GattClientDevice實(shí)例 BLEDevice 調(diào)用 disconnect() 方法斷開(kāi)連接BLE設(shè)備。
Button("斷開(kāi)")
.alignSelf(ItemAlign.Center)
.onClick(() => {
AlertDialog.show({
title: $r('app.string.disconnect'),
message: '此操作將會(huì)斷開(kāi)該設(shè)備的連接',
primaryButton: {
value: $r('app.string.cancel'),
action: () => {
}
},
secondaryButton: {
value: $r('app.string.confirm'),
action: () => {
// 斷開(kāi)連接BLE設(shè)備
let BLEdisConnect = this.BLEDevice.disconnect()
if(BLEdisConnect){
logger.info(`connectState BLEdisConnect = ${JSON.stringify(BLEdisConnect)},斷開(kāi)連接`)
// 移出BLE設(shè)備連接列表
this.deviceBleList.pop(item)
}
}
}
})
})
在斷開(kāi)連接、關(guān)閉藍(lán)牙之后,可以通過(guò) off(‘connectStateChange’) 取消訂閱BLE連接狀態(tài)變化事件、bluetooth.BLE.stopBLEScan 停止BLE掃描、以及 bluetooth.BLE.off(‘BLEDeviceFind’) 取消訂閱BLE設(shè)備發(fā)現(xiàn)上報(bào)事件,最后通過(guò) bluetooth.disableBluetooth() 關(guān)閉藍(lán)牙。
.onChange((isOn: boolean) => {
if (isOn) {
this.isOn = true
this.initBluetooth()
} else {
this.isOn = false
bluetooth.BLE.off('BLEDeviceFind',()=>{
logger.info("rgytl 取消BLE設(shè)備發(fā)現(xiàn)訂閱!")
})
bluetooth.BLE.stopBLEScan()
this.disable = bluetooth.disableBluetooth()
this.discoveryList = []
this.BleInfo = []
this.BleRssi = []
if(this.disable==true){
prompt.showToast({
message: 'Close bluetooth ' + this.disable,
duration: 2000,
});
}
}
})
5、參考文檔
??OpenAtom OpenHarmony 藍(lán)牙??
??應(yīng)用權(quán)限列表??
??OpenHarmony Gitee 藍(lán)牙??
文章相關(guān)附件可以點(diǎn)擊下面的原文鏈接前往下載
??https://ost.51cto.com/resource/2700??
??想了解更多關(guān)于開(kāi)源的內(nèi)容,請(qǐng)?jiān)L問(wèn):??
??51CTO 開(kāi)源基礎(chǔ)軟件社區(qū)??
??https://ost.51cto.com??