吐槽:
先說一下心路歷程,因為個人開發的一個APP,需要連接藍牙模塊進行設備控制和雙向的資料通信,所以嘗試用uni-app開發一個手機程式對購買的藍牙模塊進行連接,emm.......怎么說呢,理論上程序都是通的,但坑還是太多了,今天程序跑通了,特來總結一下,說明下,代碼太長了,所以我準備分段說明展示,完整代碼到時候我上傳到github上,地址最后我寫在評論里哈,進入正題...........
1.藍牙通信整體流程

上圖一共九個步驟就是創建uni-app/微信小程式連接藍牙設備并進行通信的基本步驟,具體每個模塊是怎么回事,請繼續閱讀,也可以直接轉向官網查看,
微信小程式:
wx.readBLECharacteristicValue(Object object) | 微信開放檔案 (qq.com)
uni-app:
https://uniapp.dcloud.io/api/system/ble?id=getbledeviceservices
說明:uni和小程式在API介面上基本是一毛一樣的,所以在開發的時候可以互相參考著看下,因為微信小程式開發工具掃碼真機測驗速度快,當然uni自家的HbuilderX自帶的真機運行基座也不錯,就我本人來講,我是兩個一起參考的,需要注意的就是語法的細微差別,因為uni是基于Vue開發的,用的是Vue的寫法,而小程式并不是(其實都差不多)
2.打開藍牙配接器狀態openBluetoothAdapter
//開啟藍牙配接器初始化藍牙模塊
openBluetoothAdapter() {
//重繪藍牙設備
this.devices=[]
uni.openBluetoothAdapter({
success: (res) => {
console.log("開啟藍牙配接器成功(openBluetoothAdapter success)", res);
this.startBluetoothDevicesDiscovery();
uni.showToast({
title: "開始掃描設備",
icon: "success",
});
},
fail: (res) => {
uni.showToast({
title: "請開啟藍牙",
icon: "none",
});
if (res.errCode === 10001) {
uni.onBluetoothAdapterStateChange(function (res) {
//監聽藍牙配接器是否打開,若打開則自動搜索藍牙設備(onBluetoothAdapterStateChange)
if (res.available) {
this.startBluetoothDevicesDiscovery();
}
});
}
},
});
},
當用戶打開了藍牙的時候就會進入下一步查找藍牙設備,如果用戶沒有打開藍牙,可以通過onBluetoothAdapterStateChange進行判斷提示,打開后進入設備搜索,
這里說明下,不論是微信小程式還是uni-app,呼叫方式都是這種(uni.某某/wx.某某),其都包含有三個回呼函式,success,fail,complete,所以都可以直接使用就好了

3.開始搜尋附近的藍牙外圍設備startBluetoothDevicesDiscovery
此操作比較耗費系統資源,請在搜索并連接到設備后呼叫 stopBluetoothDevicesDiscovery 方法停止搜索,后期熟練了也可以直接寫上對應的設備id,直接連他就ok了,
//開啟藍牙設備搜索
startBluetoothDevicesDiscovery() {
// 關閉藍牙配接器的時候將其打開
if (this._discoveryStarted) {
return;
}
this._discoveryStarted = true;
uni.startBluetoothDevicesDiscovery({
allowDuplicatesKey: true,
success: (res) => {
console.log( "開始搜尋藍牙設備成功(startBluetoothDevicesDiscovery success)", res);
uni.showLoading({
title: "正在搜索設備",
});
this.onBluetoothDeviceFound();
},
});
},
4. 監聽尋找到新設備的事件onBluetoothDeviceFound
//監聽尋找到新設備的事件
onBluetoothDeviceFound() {
uni.onBluetoothDeviceFound((res)=>{
if(res){
uni.hideLoading();
}
res.devices.forEach(device=>{
//過濾掉沒有名字的設備
if (!device.name && !device.localName) {
return
};
//這么操作是為了去除重復
const foundDevices = this.devices//將資料中的陣列復制一份,利用動態系結方式,不斷復制最新的陣列
const idx = this.inArray(foundDevices, 'deviceId', device.deviceId)
if (idx === -1) {
this.devices.push(device);//陣列里沒有的的就向里面添加資料,保證沒有重復[uni寫法]
}
})
console.log(this.devices);
});
},
這里要注意的是要對搜索到的設備進行去重復操作,因為藍牙搜索貌似是這樣的,你只要沒關閉它,他就一直搜索他,會出現大量的重復設備,這些設備你只需要向你的設備陣列中放入一個就行了,
5.連接低功耗藍牙設備createBLEConnection
官方目前只有低功耗藍牙設備的介面,就是BLE藍牙,和傳統手機藍牙有區別,所以你搜索的時候是找不到開著藍牙的其他手機的,目前我了解的是智能家居設備,小米手環,華為手表等這類的是用的低功耗藍牙設備,也可以購買BLE低功耗藍牙模塊進行測驗,總之低功耗藍牙設備將來的使用會更廣,通過藍牙模塊也可以集成進其他硬體設備中進行控制,
我這個代碼是做在了button按鈕上,當用戶點擊某個設備想要連接時,這個設備的資訊就傳入引數e中,界面圖最后展示,
createBLEConnection(e) {
const ds = e.currentTarget.dataset
this.deviceId = ds.deviceId
//將設備名稱也傳遞給全域變數
this.deviceName = ds.name
uni.createBLEConnection({
deviceId:this.deviceId,
success: (res) => {
this.connected=true,
console.log('連接時獲取設備id',this.deviceId);
setTimeout(() => {
this.getBLEDeviceServices(this.deviceId);
}, 1000);
},
fail:(err)=>{
uni.showToast({
title:'建立連接失敗',
icon:'none'
})
return
}
})
this.stopBluetoothDevicesDiscovery()//此時停止藍牙搜索
},
這里面有一個巨坑:在uni-app中呼叫getBLEDeviceServices的時候,不要直接調,直接調是沒有任何服務的!!!!!!!!!!!!!!!!一定要設定時間間隔,延遲呼叫,這個問題在微信小程式中沒有,在uni中一定要延遲呼叫,延遲呼叫,延遲呼叫,延遲呼叫,延遲呼叫,延遲呼叫,延遲呼叫,延遲呼叫,延遲呼叫,
6.獲取藍牙設備所有服務getBLEDeviceServices
這里需要說明下,剛開始我也是對各種id云里霧里,其實就三個,簡單的說:每個設備都有一個唯一的設備id(deviceId),每個設備中又有不同的服務,他們通過服務id區分(serviceId),有的是只讀的,有的是只寫的,有的是可讀可寫,有的可以監聽,有的不可以,,,,,所以你要搞清楚那個服務是你要的,要不然后面所有操作都對了就是不行,每個服務id中又有不同的特征值id(characteristicId)也就是uuid,uuid(universally unique Identifier)通用唯一識別碼,用來標識藍牙設備所提供的服務,比如(音頻傳輸、串口通信、列印服務、傳真服務、網路服務、檔案傳輸服務、資訊同步服務等),下面就是我當時做的時候的一個截圖,

可以看到,我這個服務中所有的特征值id/uuid只有2和3支持讀寫,但是也不能用,因為他們的notify與indicate都是false,這意味著到時候傳遞資料時候,你寫的程式無法獲取資料的變化情況,說白了就是沒法通信,所以你得從新換一個服務id,之后再看他里面的特征值id情況,

這個是我候選的一個服務id中的特征值id,發現他的notify是true的,證明可以用這個進行通信,notify和indicate只要有一個為true就行,不用都為true,
getBLEDeviceServices(deviceId) {
uni.getBLEDeviceServices({
deviceId:deviceId,
success: (res) => {
//serviceId固定死了
this.getBLEDeviceCharacteristics(deviceId, this.serviceId)
},
fail:(err)=>{
uni.showToast({
title:'獲取服務失敗',
icon:'none'
})
return
}
})
},
這里我的服務id我寫死了 所以就不在上res中找了,如果不想寫死,可以上res中找,找到后存起來供下面使用,
7.獲取藍牙設備某個服務中所有特征值(characteristic)getBLEDeviceCharacteristics
這里面特征值id我也是之前console出來找到后直接寫死了,就不要每次獲取了,當監聽到notify或者indicate為true是就要啟動notifyBLECharacteristicValueChange,
啟用低功耗藍牙設備特征值變化時的 notify 功能,訂閱特征值,注意:必須設備的特征值支持 notify 或者 indicate 才可以成功呼叫,
另外,必須先啟用 notifyBLECharacteristicValueChange 才能監聽到設備 characteristicValueChange 事件
uni.getBLEDeviceCharacteristics({
//設備id與服務id必須要給,特征值id先不用獲取,直接寫死
deviceId,
serviceId,
success: (res) => {
if(res.characteristics[0].properties.read)
{
console.log('該特征值可讀');
uni.readBLECharacteristicValue({
deviceId,
serviceId,
characteristicId:this.characteristicId,
});
}
if(res.characteristics[0].properties.write)
{
console.log('該特征值可寫');
this.canWrite=true;
//呼叫寫
this.writeBLECharacteristicValue()
}
//確保對應服務id下的特征值id具備監聽資料變化的特性
if (res.characteristics[0].properties.notify || res.characteristics[2].properties.indicate) {
uni.notifyBLECharacteristicValueChange({
deviceId,
serviceId,
characteristicId: this.characteristicId,
state: true,//是否啟用notify通知
success:(res)=>{
console.log('通知啟用(notifyBLECharacteristicValueChange)',res);
}
})
}
},
fail(res) {
console.error('獲取藍牙設備特征值失敗(getBLEDeviceCharacteristics)', res)
}
})
這里面我在判斷出設備可讀的時候就呼叫了readBLECharacteristicValue,
讀取低功耗藍牙設備的特征值的二進制資料值,注意:必須設備的特征值支持 read 才可以成功呼叫
8.監聽低功耗藍牙設備的特征值變化事件onBLECharacteristicValueChange
監聽低功耗藍牙設備的特征值變化事件,必須先啟用 notifyBLECharacteristicValueChange 介面才能接收到設備推送的 notification,
這樣設備發送資料給手機,手機才會獲得,傳遞的值就是value(硬體設備向手機傳遞資料),
uni.onBLECharacteristicValueChange((characteristic) => {
//記錄手機接受的資料
this.myDataMeasure.push(this.ab2hex(characteristic.value));
//記錄目前通信的物件(和誰通信,特征值是多少,初始值00)
const idx = this.inArray(this.chs, 'uuid', characteristic.characteristicId)
const data = {}
if (idx === -1) {
this.chs.push({
uuid: characteristic.characteristicId,
value: this.ab2hex(characteristic.value)
})
} else {
this.chs[idx] = {
uuid: characteristic.characteristicId,
value: this.ab2hex(characteristic.value)
}
}
})
9.寫入藍牙特征值writeBLECharacteristicValue
這是手機向硬體設備進行寫入寫入二進制資料,注意:必須設備的特征值支持 write 才可以成功呼叫
writeBLECharacteristicValue() {
//向藍牙設備發送一個0x00的16進制資料
let buffer = new ArrayBuffer(1)
//可以自定義復合格式的視圖
let dataView = new DataView(buffer)
dataView.setUint8(0, this.sendData)
uni.writeBLECharacteristicValue({
deviceId: this.deviceId,
serviceId: this.serviceId,
characteristicId: this.characteristicId,
value: buffer,
complete:()=>{
//buffer本身是arraybuffer(arraybuffer要轉換為16進制)
console.log('十六進制',this.ab2hex(buffer));
let sixNumber=this.ab2hex(buffer);
console.log('十進制',this.hex2int(sixNumber));
}
})
},
這里我對手機發送的資料進行了一個進制轉換,到目前為止就可以實作手機與藍牙設備的通信了,當然自己實際操作肯定不能這么順利,各種問題還是會有,目前我把我遇到的坑都寫了出來,后續也歡迎補充,
10.最終效果展示


轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/234325.html
標籤:其他
上一篇:Error: L6406E: No space in execution regions with .ANY selector matching 原因及解決辦法
