日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
你不知道的前端藍(lán)牙應(yīng)用實(shí)踐-心率帶

本文為來(lái)自 字節(jié)教育-成人與創(chuàng)新前端團(tuán)隊(duì) 成員的文章,已授權(quán) ELab 發(fā)布。

徐聞網(wǎng)站制作公司哪家好,找成都創(chuàng)新互聯(lián)!從網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開(kāi)發(fā)、APP開(kāi)發(fā)、成都響應(yīng)式網(wǎng)站建設(shè)公司等網(wǎng)站項(xiàng)目制作,到程序開(kāi)發(fā),運(yùn)營(yíng)維護(hù)。成都創(chuàng)新互聯(lián)于2013年開(kāi)始到現(xiàn)在10年的時(shí)間,我們擁有了豐富的建站經(jīng)驗(yàn)和運(yùn)維經(jīng)驗(yàn),來(lái)保證我們的工作的順利進(jìn)行。專(zhuān)注于網(wǎng)站建設(shè)就選成都創(chuàng)新互聯(lián)

一、背景

最近開(kāi)啟了減肥計(jì)劃,購(gòu)入了一條心率帶,期望在使用劃船機(jī)過(guò)程中監(jiān)測(cè)心率情況。購(gòu)入后的情況如下:

心率帶不直接顯示數(shù)值,需要連接APP或相關(guān)設(shè)備使用。

官方APP僅實(shí)時(shí)顯示心率數(shù)據(jù),無(wú)法生成心率統(tǒng)計(jì)圖表。

通過(guò)咕咚APP連接心率帶,開(kāi)啟運(yùn)動(dòng)后可以監(jiān)測(cè)心率變化,但劃船機(jī)不在支持的運(yùn)動(dòng)范圍內(nèi)。

自己簡(jiǎn)單實(shí)現(xiàn)了一個(gè)劃船機(jī)節(jié)拍器的小程序。

于是萌生了在自己的節(jié)拍器小程序內(nèi)監(jiān)聽(tīng)心率數(shù)據(jù)的想法,即Taro小程序中的藍(lán)牙應(yīng)用實(shí)踐。

二、簡(jiǎn)單了解藍(lán)牙

對(duì)于概念類(lèi)的知識(shí)筆者畢竟不是專(zhuān)業(yè)的,感興趣的同學(xué)可以通過(guò)百科、搜索引擎等渠道進(jìn)行了解。這里僅簡(jiǎn)單介紹接下來(lái)會(huì)運(yùn)用到的一些知識(shí)。

中心設(shè)備/外圍設(shè)備

客戶端(中心設(shè)備):在本次實(shí)踐中為筆者的手機(jī)。

服務(wù)端(外圍設(shè)備):在本次實(shí)踐中為心率帶、耳機(jī)等設(shè)備。

BLE(Bluetooth Low Energy)

藍(lán)牙低能耗技術(shù),實(shí)現(xiàn)設(shè)備間的連接與通信。顧名思義,能耗與成本都更低,與之相對(duì)應(yīng)的則稱(chēng)為經(jīng)典藍(lán)牙?;诠P者的開(kāi)發(fā)目的,本文簡(jiǎn)單了解設(shè)備連接前(GAP)和連接后(GATT)所涉及的兩個(gè)協(xié)議。

(了解更多:藍(lán)牙低能耗——百度百科[1]、一文讀懂藍(lán)牙技術(shù)從 1.0 到 5.0 的前世今生[2])

GAP(Generic Access Profile)

主要用來(lái)控制設(shè)備連接和廣播。通常是由外圍設(shè)備主動(dòng)、間隔性地廣播設(shè)備信息,等待中心設(shè)備發(fā)現(xiàn)并建立連接。需要注意的是,這種連接方式是獨(dú)占的,在建立連接后,外圍設(shè)備將停止廣播。

另一方面,還存在僅向外廣播而不建立連接的iBeacon設(shè)備。

GATT(Generic Attribute Profile)

定義了兩個(gè)設(shè)備間的數(shù)據(jù)傳輸方式。GATT中有兩個(gè)關(guān)鍵概念:service(服務(wù))與characteristic(特征)。

Service就是一個(gè)獨(dú)立的邏輯項(xiàng),它包含一個(gè)或多個(gè)Characteristic。

Characteristic是GATT中最小的邏輯數(shù)據(jù)單元,其屬性包含properties,標(biāo)識(shí)該特性是否能夠被read、write、notify、indicate。notify與indicate的區(qū)別在于,indicate需要有回復(fù)才能發(fā)送下一個(gè)數(shù)據(jù)包。

每個(gè)服務(wù)和特性都具有唯一的UUID標(biāo)識(shí),其中部分是由Bluetooth SIG官方定義的, Assigned Numbers | Bluetooth? Technology Website[3],如設(shè)備名、心率數(shù)據(jù)等常用屬性都是官方定義來(lái)統(tǒng)一規(guī)范。此外UUID也可以由硬件工程師來(lái)自定義實(shí)現(xiàn)。

(了解更多:BLE相關(guān)協(xié)議(GAP&GATT)[4])

三、API簡(jiǎn)介

Taro中的藍(lán)牙API

Taro.openBluetoothAdapter(option) | Taro 文檔[5]

初始化藍(lán)牙模塊

Taro.openBluetoothAdapter({
success: function (res) {
console.log("藍(lán)牙環(huán)境已啟動(dòng):", res);
setInitStatus(true);
},
fail: function (err) {
if (err.errMsg.includes("fail already opened")) {
console.log("藍(lán)牙環(huán)境原先已啟動(dòng):", err);
setInitStatus(true);
} else {
console.log("藍(lán)牙環(huán)境啟動(dòng)失?。?, err);
setInitStatus(false);
}
},
});

Taro.getBluetoothDevices(option) | Taro 文檔[6]

獲取在藍(lán)牙模塊生效期間所有已發(fā)現(xiàn)的藍(lán)牙設(shè)備。包括已經(jīng)和本機(jī)處于連接狀態(tài)的設(shè)備。

discoverInterval = setInterval(() => {
Taro.getBluetoothDevices({
success: async function (res) {
const canConnectDevices = res.devices.filter(
(item) =>
// 信號(hào)強(qiáng)度大于-80
item.RSSI > -80 &&
// 含有設(shè)備名
!["未知設(shè)備", "MBeacon"].includes(item.name)
);
console.log("獲取藍(lán)牙設(shè)備列表成功:", canConnectDevices);

setDeviceList(() => canConnectDevices);
},
});
}, 2000);

也可以使用 Taro.onBluetoothDeviceFound(callback) | Taro 文檔[7]來(lái)發(fā)現(xiàn)設(shè)備。

Taro.createBLEConnection(option) | Taro 文檔[8]

連接低功耗藍(lán)牙設(shè)備。

若小程序在之前已有搜索過(guò)某個(gè)藍(lán)牙設(shè)備,并成功建立連接,可直接傳入之前搜索獲取的 deviceId 直接嘗試連接該設(shè)備,無(wú)需進(jìn)行搜索操作。

Taro.createBLEConnection({
deviceId,
success: function (res) {
console.log("設(shè)備連接成功", res);
setConnectDeviceId(deviceId);

Taro.onBLEConnectionStateChange(function (res) {
// 該方法回調(diào)中可以用于處理連接意外斷開(kāi)等異常情況
console.log(
`設(shè)備 ${res.deviceId}連接狀態(tài)發(fā)生變化: ${res.connected}`
);
if (!res.connected) {
setConnectDeviceId("");
}
});
},
});

Taro.getBLEDeviceServices(option) | Taro 文檔[9]

獲取藍(lán)牙設(shè)備所有服務(wù)(service)。

Taro.getBLEDeviceServices({
// 這里的 deviceId 需要已經(jīng)通過(guò) createBLEConnection 與對(duì)應(yīng)設(shè)備建立鏈接
deviceId,
success: function (res) {
console.log('device services:', res.services)
}
})

Taro.getBLEDeviceCharacteristics(option) | Taro 文檔[10]

獲取藍(lán)牙設(shè)備某個(gè)服務(wù)中所有特征值(characteristic)。

Taro.getBLEDeviceCharacteristics({
// 這里的 deviceId 需要已經(jīng)通過(guò) createBLEConnection 與對(duì)應(yīng)設(shè)備建立鏈接
deviceId,
// 這里的 serviceId 需要在 getBLEDeviceServices 接口中獲取
serviceId,
success: function (res) {
console.log('device getBLEDeviceCharacteristics:', res.characteristics)
}
})

Taro.readBLECharacteristicValue(option) | Taro 文檔[11]

讀取低功耗藍(lán)牙設(shè)備的特征值的二進(jìn)制數(shù)據(jù)值。注意:必須設(shè)備的特征值支持 read 才可以成功調(diào)用。

接口讀取到的信息需要在onBLECharacteristicValueChange 方法注冊(cè)的回調(diào)中獲取。

Taro.notifyBLECharacteristicValueChange(option) | Taro 文檔[12]

啟用低功耗藍(lán)牙設(shè)備特征值變化時(shí)的 notify 功能,訂閱特征值。注意:必須設(shè)備的特征值支持 notify 或者 indicate 才可以成功調(diào)用。

另外,必須先啟用 notifyBLECharacteristicValueChange 才能監(jiān)聽(tīng)到設(shè)備 characteristicValueChange 事件

Taro.onBLECharacteristicValueChange(callback) | Taro 文檔[13]

監(jiān)聽(tīng)低功耗藍(lán)牙設(shè)備的特征值變化事件。必須先啟用 notifyBLECharacteristicValueChange 接口才能接收到設(shè)備推送的 notification。

WEB藍(lán)牙API

此處貼一些資料,感興趣可自行閱讀

Web Bluetooth API - Web APIs | MDN[14]

通過(guò) JavaScript 與藍(lán)牙設(shè)備通信[15]

四、設(shè)備名稱(chēng)(Device Name)詳解

首先getBLEDeviceServices獲取到服務(wù)列表:

查詢資料

可知0x1800是我們需要的服務(wù)。getBLEDeviceCharacteristics獲取其特征列表:

查詢資料

可知0x2A00是我們需要的特征。此時(shí)可以看到read屬性為true,我們通過(guò)onBLECharacteristicValueChange和readBLECharacteristicValue讀一下數(shù)據(jù)看看。

Taro.onBLECharacteristicValueChange(function (characteristic) {
const buffer = characteristic.value
const unit8Array = new Uint8Array(buffer);
console.log("unit8Array: ", unit8Array);

// 轉(zhuǎn)字符串
const encodedString = String.fromCodePoint.apply(null, unit8Array);
console.log('設(shè)備名:', encodedString)
});

Taro.readBLECharacteristicValue({
deviceId,
serviceId: DeviceNameService,
characteristicId: DeviceNameCharacteristics,
});

得到輸出,某米耳機(jī):

大家應(yīng)該已經(jīng)發(fā)現(xiàn),給到的特征值其實(shí)是ArrayBuffer格式。

(了解更多:談?wù)凧S二進(jìn)制:File、Blob、FileReader、ArrayBuffer、Base64 - 掘金[16])

此時(shí)需要我們將其轉(zhuǎn)化為字符串。除了上面的方法外,還可以先轉(zhuǎn)16進(jìn)制,再轉(zhuǎn)字符串:

// 轉(zhuǎn)16進(jìn)制
const hexString = Array.prototype.map
.call(unit8Array, function (bit) {
return ("00" + bit.toString(16)).slice(-2);
})
.join("");
console.log("hexString: ", hexString);

// 16進(jìn)制轉(zhuǎn)字符串
const hex2String = (hexString:string) => {
if (hexString.length % 2) return "";
var tmp = "";
for (let i = 0; i < hexString.length; i += 2) {
tmp += "%" + hexString.charAt(i) + hexString.charAt(i + 1);
}
return decodeURI(tmp);
}

五、心率測(cè)量(Heart Rate Measurement)詳解

同樣,首先我們拿到了所需的服務(wù)和特征如下。

export const HEART_RATE_SERVICE_UUID =
"0000180D-0000-1000-8000-00805F9B34FB";
export const HEART_RATE_CHARACTERISTIC_UUID =
"00002A37-0000-1000-8000-00805F9B34FB";

在設(shè)備連接后,通過(guò)onBLECharacteristicValueChange和notifyBLECharacteristicValueChange訂閱特征值變化。

export const blueToothGetHeartRate = (
deviceId: string,
onHeartRateChange: (newHeartRate: number) => void
) => {
Taro.onBLECharacteristicValueChange(function (characteristic) {
const heartRateValue = getHeartRateValue(characteristic.value);
onHeartRateChange(heartRateValue);
});
Taro.notifyBLECharacteristicValueChange({
state: true, // 啟用 notify 功能
deviceId,
serviceId: HEART_RATE_SERVICE_UUID,
characteristicId: HEART_RATE_CHARACTERISTIC_UUID,
});
};

此時(shí)我們已經(jīng)能夠獲取到心率帶發(fā)送的心率數(shù)據(jù)如下。

但是此時(shí)如果按照上文解析設(shè)備名稱(chēng)的方式,將其轉(zhuǎn)化為字符串,將得到一串亂碼。

所以需要根據(jù)協(xié)議文檔(https://www.bluetooth.com/specifications/specs/heart-rate-service-1-0/)來(lái)解讀這幾個(gè)數(shù)據(jù)。

標(biāo)志字段:

  • 二進(jìn)制:10110、十進(jìn)制:22、十六進(jìn)制:16
  • 其中位0為心率格式位,決定心率數(shù)據(jù)是uint8(位0 === 0)還是unit16(位0 === 1)

  • 位1、位2為傳感器接觸狀態(tài)位。11表示支持皮膚接觸檢測(cè)且接觸正常。
  • 位3為能量消耗狀態(tài)位,0表示能量消耗字段不存在.
  • 位4為RR-Interval狀態(tài)位,1表示存在一個(gè)或多個(gè) RR-Interval 值。

心率數(shù)值:

  • 二進(jìn)制:1100101、十進(jìn)制:101、十六進(jìn)制:65
  • 當(dāng)心率小于255時(shí),unit8足以。但如果需要支持更高的心率值(某些動(dòng)物),則需要為unit16

  • 此時(shí)我們根據(jù)標(biāo)志字段0,已經(jīng)能夠獲取到需要的心率值:101。那么還剩下兩個(gè)字段代表什么意思呢?

RR-intreval 心率間隔:

  • 從前面的標(biāo)志字段分析中,我們發(fā)現(xiàn)該設(shè)備不支持能量消耗狀態(tài)但支持RR-interval。且RR-interval可能是一位也可能是多位。那么我們?cè)撛趺醋x取這個(gè)數(shù)字呢?
  • 首先我們顧名思義,心率間隔就是兩次心跳間的間隔時(shí)長(zhǎng),從上面的心率值推算:
  • 60*1000/101 ≈594ms
  • 接下來(lái)看這兩個(gè)數(shù)字。
  • 二進(jìn)制:1010010、十進(jìn)制:82、十六進(jìn)制:52
  • 二進(jìn)制:10、十進(jìn)制:2、十六進(jìn)制:02
  • 嘗試2*256+82 = 594,謎團(tuán)解開(kāi)了~

六、總結(jié)與疑惑

至此整個(gè)藍(lán)牙心率設(shè)備數(shù)據(jù)獲取的實(shí)現(xiàn)就完成了,可以在使用節(jié)拍器的同時(shí)監(jiān)控自身的心率數(shù)據(jù)了。

整體來(lái)說(shuō)整個(gè)開(kāi)發(fā)過(guò)程還是比較簡(jiǎn)單的,畢竟API文檔描述的非常清晰,主要時(shí)間耗費(fèi)在解讀心率數(shù)據(jù)這個(gè)過(guò)程,后來(lái)知道應(yīng)該從協(xié)議文檔出發(fā)解讀就好說(shuō)了。

由于是在業(yè)余時(shí)間實(shí)現(xiàn)的上述能力,開(kāi)發(fā)過(guò)程中存在不少疑惑沒(méi)來(lái)得及研究。這里先拋兩個(gè)問(wèn)題出來(lái),希望了解相關(guān)知識(shí)的同學(xué)能夠不吝賜教。

  • 在arraybuffer轉(zhuǎn)16進(jìn)制過(guò)程中("00" + bit.toString(16)).slice(-2);?,為什么要先"00" +?,然后再.slice(-2)??直接bit.toString(16)是否可行?
  • 針對(duì)某米耳機(jī),筆者暫時(shí)沒(méi)有在服務(wù)和特征中找到電量信息相關(guān)的數(shù)據(jù),那么手機(jī)設(shè)備又是如何獲取到耳機(jī)電量的呢?

猜測(cè)電池服務(wù)和特性分別是0x180F Battery service 和 0x2A19 Battery Level ,但上圖耳機(jī)返回的service列表中沒(méi)找到該服務(wù)。

本文作者正在等待你的幫助。

參考資料

[1]藍(lán)牙低能耗——百度百科: https://baike.baidu.com/item/%E8%93%9D%E7%89%99%E4%BD%8E%E8%83%BD%E8%80%97

[2]一文讀懂藍(lán)牙技術(shù)從 1.0 到 5.0 的前世今生: https://zhuanlan.zhihu.com/p/37717509

[3]Assigned Numbers | Bluetooth? Technology Website: https://www.bluetooth.com/specifications/assigned-numbers/

[4]BLE相關(guān)協(xié)議(GAP&GATT): https://www.jianshu.com/p/62eb2f5407c9

[5]Taro.openBluetoothAdapter(option) | Taro 文檔: https://docs.taro.zone/docs/apis/device/bluetooth/openBluetoothAdapter

[6]Taro.getBluetoothDevices(option) | Taro 文檔: https://docs.taro.zone/docs/apis/device/bluetooth/getBluetoothDevices

[7]Taro.onBluetoothDeviceFound(callback) | Taro 文檔: https://docs.taro.zone/docs/apis/device/bluetooth/onBluetoothDeviceFound

[8]Taro.createBLEConnection(option) | Taro 文檔: https://docs.taro.zone/docs/apis/device/bluetooth-ble/createBLEConnection

[9]Taro.getBLEDeviceServices(option) | Taro 文檔: https://docs.taro.zone/docs/apis/device/bluetooth-ble/getBLEDeviceServices

[10]Taro.getBLEDeviceCharacteristics(option) | Taro 文檔: https://docs.taro.zone/docs/apis/device/bluetooth-ble/getBLEDeviceCharacteristics

[11]Taro.readBLECharacteristicValue(option) | Taro 文檔: https://docs.taro.zone/docs/apis/device/bluetooth-ble/readBLECharacteristicValue

[12]Taro.notifyBLECharacteristicValueChange(option) | Taro 文檔: https://docs.taro.zone/docs/apis/device/bluetooth-ble/notifyBLECharacteristicValueChange

[13]Taro.onBLECharacteristicValueChange(callback) | Taro 文檔: https://docs.taro.zone/docs/apis/device/bluetooth-ble/onBLECharacteristicValueChange

[14]Web Bluetooth API - Web APIs | MDN: https://developer.mozilla.org/en-US/docs/Web/API/Web_Bluetooth_API

[15]通過(guò) JavaScript 與藍(lán)牙設(shè)備通信: https://web.dev/i18n/zh/bluetooth/

[16]談?wù)凧S二進(jìn)制:File、Blob、FileReader、ArrayBuffer、Base64 - 掘金: https://juejin.cn/post/7148254347401363463#heading-9


文章標(biāo)題:你不知道的前端藍(lán)牙應(yīng)用實(shí)踐-心率帶
鏈接URL:http://m.5511xx.com/article/ccossss.html