精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

OpenHarmony BLE低功耗藍牙

系統 OpenHarmony
實現BLE藍牙設備與DAYU200設備之間數據交互,即中心設備接收外圍設備的通知數據。

??想了解更多關于開源的內容,請訪問:??

??51CTO 開源基礎軟件社區??

??https://ost.51cto.com??

目標:實現BLE藍牙設備與DAYU200設備之間數據交互,即中心設備接收外圍設備的通知數據,OpenHarmony社區提供了詳細的API文檔,可以移步到:??藍牙??

之前在HarmonyOS系統上實現了BLE藍牙的連接與數據傳輸,??《鴻蒙BLE藍牙通信開發總結》??,現在需要在OpenHarmony上也實現BLE藍牙的通信。

設備與環境

設備:BLE藍牙設備、DAYU200設備
系統:OpenHarmony 3.2 beta1
SDK:9

先看下效果

??查看視頻??

OpenHarmony BLE低功耗藍牙-開源基礎軟件社區

說在前面的話

如果你需要了解藍牙的基礎知識,可以查看??《鴻蒙BLE藍牙通信開發總結》??這篇文章的“藍牙介紹”部分。

前置步驟

創建項目

說明:創建項目的過程比較簡單,注意在選擇SDK 9的版本,使用Stage模型,如下:

OpenHarmony BLE低功耗藍牙-開源基礎軟件社區

OpenHarmony BLE低功耗藍牙-開源基礎軟件社區

業務邏輯梳理

1、權限問題,首先需要注冊藍牙相關權限。
2、搜索藍牙,應用啟動后可以手動的開啟和關閉藍牙掃描。
3、連接藍牙,根據藍牙的mac地址,調用connect進行連接。
4、遍歷藍牙特征,在藍牙連接成功后,獲取藍牙的服務特征,設置指定GATT特征通知。
5、通知數據,將數據通過藍牙服務中的通知屬性發送。
6、接受通知,中心設備通過characteristicChangedEvent接收通知數據,并顯示在屏幕上。
7、斷開藍牙,根據需要斷開連接的藍牙。
8、關閉藍牙,在應用退出,需要結束掃描,釋放資源。

開發實踐

1、申請權限

開發之前,通過API文檔可以指導,需要實現目標需要獲得以下權限:

  • ohos.permission.USE_BLUETOOTH // 允許應用查看藍牙的配置。
  • ohos.permission.DISCOVER_BLUETOOTH // 允許應用配置本地藍牙,查找遠端設備且與之配對連接。
  • ohos.permission.LOCATION // 允許應用獲取設備位置信息。
  • ohos.permission.MANAGE_BLUETOOTH // 允許應用配對藍牙設備,并對設備的電話簿或消息進行訪問。

以上權限中ohos.permission.MANAGE_BLUETOOTH級別是system_basic,此權限在應用打包簽名時需要在UnsgnedReleasedProfileTemplate.json文件中的acls字段下添加此權限,否則安裝時會出現:Failed due to grant request permissions failed,如下代碼:

"acls": {
"allowed-acls": [
"ohos.permission.MANAGE_BLUETOOTH"
]
}

如下圖:

OpenHarmony BLE低功耗藍牙-開源基礎軟件社區

應用開發時,將需要申請的權限在modele.json5文件中聲明,權限相關的說明可以查看:??應用權限列表??

"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、打開并搜索藍牙

打開藍牙:
  • 打開藍牙并監聽藍牙狀態
let result:boolean = bluetooth.enableBluetooth() // 開啟藍牙
  • 監聽藍牙狀態(開啟或關閉)
/***
* 添加監聽器
*/
addBlueListener() {
// 訂閱藍牙連接狀態改變事件。
let self = this
bluetooth.on('stateChange', (data) => {
logger.info(TAG, `enter on stateChange`)
// 藍牙打開
if (data === bluetooth.BluetoothState.STATE_ON) {
logger.info(TAG, `enter BluetoothState.STATE_ON`)
self.startBleScan()
}
// 藍牙關閉
if (data === bluetooth.BluetoothState.STATE_OFF) {
logger.info(TAG, `enter BluetoothState.STATE_OFF`)
self.disconnect()
self.stopBleScan()
bluetooth.stopBluetoothDiscovery() // 關閉藍牙掃描
self.mDeviceName = ''
self.mDeviceRower = ''
self.discoveryList = []
}
logger.info(TAG, `BluetoothState = ${JSON.stringify(data)}`)
})
}
bluetooth.getState():

點擊藍牙開關,進行開啟和關閉藍牙操作,程序啟動后會先自動檢測系統藍牙是否開啟,如果開啟則打開藍牙開關,默認狀態下關閉藍牙。藍牙被開啟后會進入藍牙掃描。目前主要針對BLE低功耗藍牙進行操作,所以這里只開啟BLE藍牙掃描,下面說到的藍牙相關操作,都是指BLE藍牙。

  • 獲取藍牙狀態

接口

說明

返回值

bluetooth.getState

獲取藍牙開關狀態。

BluetoothState 藍牙開關狀態。

import bluetooth from '@ohos.bluetooth';

let state = bluetooth.getState()
if (state === bluetooth.BluetoothState.STATE_ON) {
this.isOn = true
// 針對BLE藍牙
this.startBleScan() // 發現BLE藍牙
}
if (state === bluetooth.BluetoothState.STATE_OFF) {
this.isOn = false
}
啟動藍牙掃描,并注冊發現設備監聽器:

啟動BLE藍牙掃描,并注冊“BLEDeviceFiind”藍牙監聽器,用于接收掃描到藍牙,為了方便調試我這里只將需要的藍牙設備過濾出來。掃描出來的藍牙狀態默認為:未連接

參數名

類型

必填

說明

filters

Array<ScanFilter>


表示掃描結果過濾策略集合,如果不使用過濾的方式,該參數設置為null。

options

ScanOptions


表示掃描的參數配置,可選參數。

參數名

類型

必填

說明

type

string


填寫"BLEDeviceFind"字符串,表示BLE設備發現事件。

callback

Callback<Array<ScanResult>>


表示回調函數的入參,發現的設備集合。回調函數由用戶創建通過該接口注冊。

/**
* 開啟BLE藍牙掃描
* @param data
*/
startBleScan() {
logger.info(TAG, `startBleScan`)
bluetooth.BLE.on('BLEDeviceFind', this.onBLEDeviceFind)
bluetooth.BLE.startBLEScan([{
deviceId: DEVICE_MAC
}])
}
onBLEDeviceFind = (scanResult: Array<bluetooth.ScanResult>) => {
let self = this
// logger.info(TAG, `BLE scan device find result= ${JSON.stringify(scanResult)}`)
if (typeof (scanResult) === 'undefined' || scanResult.length <= 0) {
return
}
for (let result of scanResult) {
let temp: string = result.deviceId
// 只過濾需要的設備
if (DEVICE_MAC !== temp) {
break
}
if (!self.isExistDevice(self.discoveryList, temp)) {
self.createBleBlueInfo(temp, result.rssi).then((info) => {
logger.info(TAG, `BLE scan device find,add = ${JSON.stringify(info)}`)
// 連續兩次發送相同的廣播,時間間隔5ms,過濾極短時間發現的設備
let curTime: number = Date.now()
let diff: number = curTime - self.mLastFindDeviceTime
logger.info(TAG, `BLE scan device find,Time diff = ${diff} curTime=${curTime} mLastFindDeviceTime=${self.mLastFindDeviceTime}`)
if (diff > 5) {
logger.info(TAG, `BLE scan device find,Time is less than 5 ms, so back`)
self.discoveryList.push(info)
}
this.mLastFindDeviceTime = curTime
});
} else {
let curBlueInfo: baseInfo = self.getBlueInfoByDeviceId(self.discoveryList, temp)
if (curBlueInfo !== null) {
curBlueInfo.deviceRssi = result.rssi
logger.info(TAG, `BLE scan device find,update rssi = ${curBlueInfo.deviceRssi}`)
let index: number = self.getIndexByDeviceId(self.discoveryList, temp)
if (index >= 0) {
self.discoveryList.splice(index, 1)
logger.info(TAG, `BLE scan device find,delete index= ${index}`)
self.discoveryList.push(curBlueInfo)
logger.info(TAG, `BLE scan device find,add new info = ${JSON.stringify(curBlueInfo)}`)
}
}
}
}
}
拋出問題:

問題1:在bluetooth.BLE.startBLEScan()接口中傳遞需要過濾的deviceId,但是無效,問題已向社區反饋,如果有興趣可以關注相關 ??issues??

3、連接藍牙

點擊列表中的藍牙信息,根據當前的狀態發起藍牙連接,涉及的接口:??GattClientDevice.connect()??。

返回值:

類型

說明

boolean

連接操作成功返回true,操作失敗返回false。

this.mGattClientDevice = bluetooth.BLE.createGattClientDevice(deviceId)
let connectStatus = this.mGattClientDevice.connect()
logger.info(TAG, `BLE Device connect = ${connectStatus}`)

4、獲取藍牙服務,遍歷藍牙特征

通過GattClientDevice.on(type: “BLEConnectionStateChange”, callback: Callback<BLEConnectChangedState>)注冊藍牙連接狀態變化監聽器,獲取藍牙連接狀態,當藍牙連接成功,則通過GattClientDevice.getServices() 獲取藍牙支持的服務,這里提醒一句,獲取服務需要耗時3秒左右,通過藍牙服務設置readCharacteristicValue()、writeCharacteristicValue()、setNotifyCharacteristicChanged()、on(‘BLECharacteristicChange’) 來完成對藍牙的讀、寫、監聽特征值變化的操作。

  • 通過GattClientDevice.getServices()
// BLE藍牙連接成功,獲取當前BLE藍牙的服務
this.mGattClientDevice.getServices().then((result: Array<bluetooth.GattService>) => {
logger.info(TAG, `BLE Device getServices successfully`)
for (let blueService of result) {
logger.info(TAG, `BLE Device blue ${connectDeviceId} Service uuid=${blueService.serviceUuid}`)
if (SERVICE_UUID === blueService.serviceUuid) {
let curCharacteristics = blueService.characteristics
for (let characteristic of curCharacteristics) {
logger.info(TAG, `BLE Device characteristic= ${JSON.stringify(characteristic)}`)
if (NOTIFY_UUID === characteristic.characteristicUuid) {
self.mNotifyCharacteristic = characteristic
self.blueSetNotifyCharacteristicChanged()
} else if (WRITE_UUID === characteristic.characteristicUuid) {
self.mWriteCharacteristic = characteristic
logger.info(TAG, `BLE Device blue send : 獲得WriteCharacteristic `)
// 延遲500ms再向藍牙設備發送獲取設備信息消息,防止未注冊消息通知導致無法收到
setTimeout(() => {
self.getDeviceInfo()
}, 100)
}
}
}
// todo 使用ReadCharacteristic 時會導致WriteCharacteristic、NotifyCharacteristic失敗,這里先注釋
// else if (SERVICE_BATTERY_UUID === blueService.serviceUuid) {
// // 監聽BLE設備主動發送的消息
// let curCharacteristics = blueService.characteristics
// for (let characteristic of curCharacteristics) {
// logger.info(TAG, `BLE Device characteristic2= ${JSON.stringify(characteristic)}`)
// if (LEVEL_BATTERY_UUID === characteristic.characteristicUuid) {
// self.mReadCharacteristic = characteristic
// self.blueSetReadCharacteristic()
// }
// }
// }
}
});
} else if (connectState === bluetooth.ProfileConnectionState.STATE_DISCONNECTED) {
// 斷開連接
this.refreshBleConnect(connectDeviceId, STATE.DISCONNECT)
this.mDeviceName = ''
this.mDeviceRower = ''
} else if (connectState === bluetooth.ProfileConnectionState.STATE_CONNECTING) {
// 連接中
this.refreshBleConnect(connectDeviceId, STATE.CONNECTING)
this.mDeviceName = ''
this.mDeviceRower = ''
}
})

5、向低功耗藍牙設備寫入特定的特征值

通過步驟4可以獲取到BLECharacteristic,調用:GattClientDevice.writeCharacteristicValue() 就可以向低功耗藍牙設備寫入特定的特征值。

blueWriteCharacteristicValue(str: string) {
if (this.mWriteCharacteristic === null) {
return
}
let strUpper: string = str.toUpperCase()
let temp: string = HexUtil.stringToHex(strUpper)
let dataPacket: string = HexUtil.getWriteDataPacket(temp)
let data: Uint8Array = HexUtil.hexStringToBytes2(dataPacket)
this.mWriteCharacteristic.characteristicValue = data.buffer
let success: boolean = this.mGattClientDevice.writeCharacteristicValue(this.mWriteCharacteristic)
logger.info(TAG, `BLE Device WriteCharacteristicValue success =${success}`)
}

6、接受通知

向服務端發送設置通知此特征值請求:setNotifyCharacteristicChanged(characteristic: BLECharacteristic, enable: boolean)。

blueSetNotifyCharacteristicChanged() {
logger.info(TAG, `BLE Device SetNotifyCharacteristicChanged`)
let success1: boolean = this.mGattClientDevice.setNotifyCharacteristicChanged(this.mNotifyCharacteristic, true)
logger.info(TAG, `BLE Device SetNotifyCharacteristicChanged success1=${success1}`)
logger.info(TAG, `BLE Device blue send : 注冊通知監聽器 success1 =${success1}`)
if (success1) {
this.onBLECharacteristicChange()
let descriptor: bluetooth.BLEDescriptor = this.getDescriptor(this.mNotifyCharacteristic, this.mNotifyCharacteristic.characteristicUuid);
if (descriptor != null) {
descriptor.descriptorValue = new Uint8Array(this.ENABLE_NOTIFICATION_VALUE).buffer
let success2: boolean = this.mGattClientDevice.writeDescriptorValue(descriptor)
logger.info(TAG, `BLE Device SetNotifyCharacteristicChanged success2=${success2}`)
} else {
logger.info(TAG, `BLE Device SetNotifyCharacteristicChanged descriptor is null`)
}
}
}
  • 訂閱藍牙低功耗設備的特征值變化事件:GattClientDevice.on(type: “BLECharacteristicChange”, callback: Callback<BLECharacteristic>)。
/**
*訂閱藍牙低功耗設備的特征值變化事件。
* 需要先調用setNotifyCharacteristicChanged接口才能接收server端的通知。
* @param deviceId
*/
onBLECharacteristicChange() {
this.mGattClientDevice.on('BLECharacteristicChange', (data: bluetooth.BLECharacteristic) => {
let serviceUuid = data.serviceUuid;
let characteristicUuid = data.characteristicUuid;
logger.info(TAG, `BLE Device data on BLECharacteristicChange ${serviceUuid}, ${characteristicUuid}`)
let characteristicValue:ArrayBuffer = data.characteristicValue
if (characteristicValue !== null && typeof(characteristicValue) === 'undefined') {
return
}
let valueTemp = HexUtil.ab2hex(characteristicValue);
let value = HexUtil.filterValue(valueTemp)
let temp: string = HexUtil.hexToString(value)
let tempStrs: string[] = temp.split(',')
let count: number = tempStrs.length
let curStr: string = tempStrs[count - 1] + '\n'
// 解析設備基礎信息
this.analysisDeviceInfo(curStr)
// 解析設備電量信息
this.analysisRower(curStr)
if (this.filterResult(curStr)) {
return
}
this.mScanStr = curStr + '' + this.mScanStr
})
}
拋出問題:

問題1:監聽多服務通道特征通知會導致異常,相關??issues??。

7、斷開藍牙

藍牙連接成功后,點擊藍牙列表中的藍牙信息,彈窗窗口提示用戶需要斷開藍牙,點擊"確定"則斷開藍牙,涉及的接口:GattClientDevice.disconnect()。

  • 提示彈窗
AlertDialog.show({
title: $r('app.string.disconnect'),
message: '此操作將會斷開您與以下設備的連接:' + ((typeof (blue.deviceName) !== 'undefined') ? blue.deviceName : blue.deviceId),
primaryButton: {
value: $r('app.string.cancel'),
action: () => {
}
},
secondaryButton: {
value: $r('app.string.confirm'),
action: () => {
this.disconnectBlue(blue)
}
}
})
  • 斷開藍牙
/**
* 斷開藍牙
* @param blue
*/
disconnectBlue(blue: baseInfo) {
logger.info(TAG, `disconnectBlue info = ${JSON.stringify(blue)}`)
if (blue === null || typeof (this.mGattClientDevice) === 'undefined') {
return
}
let deviceId: string = blue.deviceId
let ret = this.mGattClientDevice.disconnect()
logger.info(TAG, `BLE Device ${deviceId} disconnect Status = ${ret}`)
if (ret === true) {
this.removeBlueDevice(deviceId)
this.mGattClientDevice = null
} else {
this.showToast('斷開藍牙失敗')
}
}

8、關閉藍牙

關閉藍牙后,會通知再2.1中藍牙關閉狀態的回調

let result:boolean = bluetooth.disableBluetooth()

到此BLE低功耗藍牙的整體流程就介紹完畢,如果有什么問題,可以在評論區留言。

問題與思考

1、BLE藍牙創建加密通信通道時需要進行綁定,目前SDK9的版本上還不支持,只能使用不綁定的方式進行通信。相關 ??issues??。

補充代碼

UI

build() {
Column() {
Stack({ alignContent: Alignment.Top }) {
Image($r('app.media.top_bg'))
.objectFit(ImageFit.Cover)
Row() {
Text(this.mDeviceName)
.width('30%')
.fontColor('#71fbfd')
.align(Alignment.Center)
.textAlign(TextAlign.Center)
.fontSize(32)
.margin({
bottom: '15%'
})
Image($r('app.media.project_hr22'))
.objectFit(ImageFit.Contain)
.width('30%')
.onClick(() => {
this.onClickDevice()
})
Text(`電量 ${this.mDeviceRower}%`)
.width('30%')
.margin({
top: '20%'
})
.fontColor('#71fbfd')
.align(Alignment.Center)
.textAlign(TextAlign.Center)
.fontSize(24)
}
.alignItems(VerticalAlign.Center)
.justifyContent(FlexAlign.Center)
.width('100%')
.height('100%')
}
.width('100%')
.height('40%')

Divider()
.vertical(false)
.color('#fc3811')
.strokeWidth(5)

Column() {
Row() {
Column() {
Text($r('app.string.bluetooth'))
.fontSize(30)
.alignSelf(ItemAlign.Start)
if (true === this.isOn) {
Row() {
Text($r('app.string.discovery'))
.fontSize(20)
.alignSelf(ItemAlign.Start)
.margin({
top: '10'
})

LoadingProgress()
.color(Color.Grey)
.width(30)
.height(30)
.align(Alignment.Center)
.margin({
left: 10
})
}
.height(40)
}
}.margin({ top: 20, bottom: 20 })

Blank() // 空白填充

Column() {
Toggle({ type: ToggleType.Switch, isOn: this.isOn })
.selectedColor('#ff2982ea')
.key('toggleBtn')
.onChange((isOn: boolean) => {
if (isOn) {
this.isOn = true
let result:boolean = bluetooth.enableBluetooth() // 開啟藍牙
logger.info(TAG, `enable Bluetooth ${result}`)
} else {
this.isOn = false
let result:boolean = bluetooth.disableBluetooth()
logger.info(TAG, `disable Bluetooth ${result}`)
}
})
}
}.width('100%')

if (this.isOn) {
Divider()
.width('100%')
.vertical(false)
.color('#a3a4a7')
.strokeWidth(1)
.margin({
bottom: '1%'
})
}
Column() {
ForEach(this.discoveryList, (item: baseInfo) => {
Row() {
Image(item.state === STATE.CONNECTED ? $r('app.media.blue_connect') :
(item.state === STATE.CONNECTING ? $r('app.media.blue_connecting') : $r('app.media.blue_disconnect')))
.width(45)
.height(45)
.objectFit(ImageFit.Cover)
.margin({
right: '1%'
})
Column() {
Text(item.deviceName)
.width('100%')
.fontSize(22)
.margin({
top: '2%',
bottom: '2%'
})
Row() {
Text(item.deviceId)
.fontSize(16)
Text(item.deviceRssi.toString())
.fontSize(16)
.margin({
left: '5%'
})
}
.width('100%')
.margin({
bottom: '2%'
})
}
}
.justifyContent(FlexAlign.Start)
.alignSelf(ItemAlign.Start)
.width('100%')
.key('pairedDevice')
.onClick(() => {
this.onClickBlueItem(item)
})
})
}
}
.width('90%')
.padding({ top: 10, left: 30, right: 30, bottom: 10 })
.margin({ top: 20, bottom: 20 })
.backgroundColor(Color.White)
.borderRadius(20)
.borderWidth(1)
.borderColor('#a3a4a7')
Column() {
Scroll(this.scroller) {
Column() {
Text(this.mScanStr)
.width('100%')
.fontSize(18)
.lineHeight(30)
.align(Alignment.Start)
}
.width('100%')
.padding({
left: '20',
right: '20'
})
.alignSelf(ItemAlign.Start)
.justifyContent(FlexAlign.Start)
}
.scrollable(ScrollDirection.Vertical)
.scrollBarColor(Color.Gray)
.scrollBar(BarState.Auto)
.scrollBarWidth(10)
.width('90%')
.height('20%')
}
.padding({
left: '20',
right: '20',
top: '20',
bottom: '20'
})
.margin({
bottom: '20'
})
.border({
width: 1,
color: '#a3a4a7'
})
.borderRadius(30)
.backgroundColor(Color.White)


Button('清除')
.width('90%')
.height(60)
.backgroundColor('#fc3811')
.fontSize(24)
.onClick(() => {
this.mScanStr = ''
})
}
.width('100%')
.height('100%')
.backgroundColor('#dcdcdc')
}

數據轉換工具:HexUtil

import TextUtils from './TextUtils'

type char = string;
type byte = number;

export default class HexUtil {
private static readonly DIGITS_LOWER: char[] = ['0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
private static readonly DIGITS_UPPER: char[] = ['0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'];
private static readonly UPDATE_HEAD: string = 'xxxxxxxx';
private static readonly WRITE_PACKET_DATE_START: string = "xxxxxxxxx"; //發送數據固定起始數據域格式
public static readonly WRITE_PACKET_DATE_END: string = "xxxx"; // 發送數據固定結束數據域格式
public static readonly NOTIFY_DATE_START: string = "xxxxxxxx"; // 通知數據頭
public static readonly NOTIFY_DATE_END: string = "xxxxxxxxx"; // 通知數據尾
public static encodeHex(data: byte[], toLowerCase: boolean = true): char[] {
return HexUtil.encodeHexInner(data, toLowerCase ? HexUtil.DIGITS_LOWER : HexUtil.DIGITS_UPPER);
}
protected static encodeHexInner(data: byte[], toDigits: char[]): char[] {
if (!data)
return null;
let l: number = data.length;
let out: char[] = new Array(l << 1);
let index:number = 0
for(let item of data) {
index += 1
out[index] = toDigits[(0xF0 & item) >>> 4]
index += 1
out[index] = toDigits[0x0F & item]
}
return out;
}
protected static encodeHexInner2(data: Uint8Array, toDigits: char[]): char[] {
if (!data)
return null;
let l: number = data.length;
let out: char[] = new Array(l << 1);
let index:number = 0
for(let item of data) {
index += 1
out[index] = toDigits[(0xF0 & item) >>> 4]
index += 1
out[index] = toDigits[0x0F & item]
}
return out;
}
private static byteToString(data: char[]): string {
let str = '';
for(let item of data) {
str += item
}
return str;
}
public static encodeHexStr(data: byte[], toLowerCase: boolean = true): string{
return HexUtil.encodeHexStrInner(data, toLowerCase ? HexUtil.DIGITS_LOWER : HexUtil.DIGITS_UPPER);
}
protected static encodeHexStrInner(data: byte[], toDigits: char[]): string {
return HexUtil.byteToString(HexUtil.encodeHexInner(data, toDigits));
}
public static encodeHexStr2(data: Uint8Array, toLowerCase: boolean = true): string{
return HexUtil.encodeHexStrInner2(data, toLowerCase ? HexUtil.DIGITS_LOWER : HexUtil.DIGITS_UPPER);
}
protected static encodeHexStrInner2(data: Uint8Array, toDigits: char[]): string {
return HexUtil.byteToString(HexUtil.encodeHexInner2(data, toDigits));
}
public static formatHexString(data: Uint8Array, addSpace: boolean = false): string {
if (!data || data.length < 1)
return null;
let sb: string = '';
for (let item of data) {
let hex: String = (item & 0xFF).toString(16);
if (hex.length == 1) {
hex = '0' + hex;
}
sb = sb + hex;
if (addSpace)
sb = sb + " ";
}
return sb;
}
public static decodeHex(data: char[]): byte[] {
let len: number = data.length;

if ((len & 0x01) != 0) {
throw new Error("Odd number of characters.");
}
let out: byte[] = new Array(len >> 1);
let i:number = 0
let j:number = 0
while(j < len) {
let f : number = HexUtil.toDigit(data[j], j) << 4
j += 1
f = f | HexUtil.toDigit(data[j], j)
j += 1
out[i] = (f & 0xFF)
}
return out;
}
protected static toDigit(ch: char, index: number): number {
let digit: number = HexUtil.charToByte(ch.toUpperCase()); //Character.digit(ch, 16);
if (digit == -1) {
throw new Error("Illegal hexadecimal character " + ch
+ " at index " + index);
}
return digit;
}
public static hexStringToBytes(hexString: string): Uint8Array {
if (TextUtils.isEmpty(hexString)) {
return null;
}
hexString = hexString.trim();
hexString = hexString.toUpperCase();
let length: number = hexString.length / 2;
let hexChars: char[] = TextUtils.toCharArray(hexString);
let d: byte[] = new Array(length);
let index = 0
while (index < length) {
let pos = index * 2;
d[index] = (HexUtil.charToByte(hexChars[pos]) << 4 | HexUtil.charToByte(hexChars[pos + 1]));
index += 1
}
return new Uint8Array(d);
}
public static hexStringToBytes2(hexString: string): Uint8Array {
if (TextUtils.isEmpty(hexString)) {
return null;
}
hexString = hexString.trim();
hexString = hexString.toUpperCase();
let length: number = hexString.length / 2;
let hexChars: char[] = TextUtils.toCharArray(hexString);
let d: byte[] = new Array(length);
let index = 0
while (index < length) {
let pos = index * 2;
d[index] = (HexUtil.charToByte(hexChars[pos]) << 4 | HexUtil.charToByte(hexChars[pos + 1]));
index += 1
}
return new Uint8Array(d);
}
public static charToByte(c: char): byte {
return "0123456789ABCDEF".indexOf(c);
}
public static extractData(data: Uint8Array, position: number): String {
return HexUtil.formatHexString(new Uint8Array([data[position]]));
}
public static getWriteDataPacket(hexString: string): string {
if (TextUtils.isEmpty(hexString) || hexString.length % 2 !== 0) {
return ''
}
let dataField: string = ''
if (hexString.startsWith(HexUtil.UPDATE_HEAD)) {
dataField = hexString.replace(HexUtil.UPDATE_HEAD, '')
} else {
dataField = HexUtil.WRITE_PACKET_DATE_START.concat(hexString, HexUtil.WRITE_PACKET_DATE_END)
}
return dataField
}
public static stringToHex(s: string): string {
let str: string = ''
let len: number = s.length
let index: number = 0
while (index < len) {
let ch: number = s.charCodeAt(index)
let s4: string = ch.toString(16)
str = str + s4
index += 1
}
return str
}
public static hexToString(data:string):string {
let val : string = ''
let arr:string[] = data.split(',')
let index:number = 0
while(index < arr.length) {
val += String.fromCharCode(parseInt(arr[index], 16))
index += 1
}
let b:string = decodeURIComponent(val)
console.log('hexToString b' + b)
return b
}
public static ab2hex(buffer:ArrayBuffer):string {
var hexArr = Array.prototype.map.call(
new Uint8Array(buffer),
function (bit) {
return ('00' + bit.toString(16)).slice(-2)
}
)
return hexArr.join(',');
}
/**
* 過濾通知消息頭和消息尾
* @param data
*/
public static filterValue(data:string) : string {
if (data === null) {
return ''
}
return data.replace(this.NOTIFY_DATE_START, '').replace(this.NOTIFY_DATE_END, '')
}
}

字符工具:TextUtils

export default class TextUtils{
public static isEmpty(text: string): boolean {
return text == null || text == undefined || text.length==0;
}
public static toCharArray(text: string): Array<string> {
let arr: string[] = new Array(text.length);
let index = 0
while(index < text.length) {
arr[index] = text.charAt(index);
index += 1
}
return arr;
}
}

??想了解更多關于開源的內容,請訪問:??

??51CTO 開源基礎軟件社區??

??https://ost.51cto.com??。

責任編輯:jianghua 來源: 51CTO開源基礎軟件社區
相關推薦

2023-04-17 16:10:14

鴻蒙藍牙

2024-04-12 15:52:42

藍牙

2021-04-13 15:11:19

物聯網LoRa和BLEIoT

2022-05-18 14:21:18

漏洞藍牙低功耗藍牙

2023-09-20 11:46:54

藍牙物聯網IOT

2023-05-24 10:39:30

Windows 11微軟

2015-11-19 09:58:47

藍牙ZigBee低功耗廣域網絡

2009-01-05 18:35:57

服務器產品CPU

2021-10-30 07:55:00

BLE 藍牙開發

2023-05-25 06:52:50

2020-06-22 10:21:44

物聯網藍牙技術

2015-09-22 11:04:24

藍牙4.0開發

2012-11-09 10:16:22

ARM低功耗Intel

2010-01-06 18:15:15

amd皓龍綠色

2012-08-08 10:33:16

低功耗服務器

2023-06-29 16:11:19

物聯網LPWA網絡

2023-07-16 22:48:59

2013-01-10 10:15:22

低功耗CPU虛擬化

2023-03-08 21:30:33

2022-01-25 16:54:14

BLE操作系統鴻蒙
點贊
收藏

51CTO技術棧公眾號

在线视频第一页| 少妇大叫太大太粗太爽了a片小说| 久久99国产综合精品免费| 免费看成人哺乳视频网站| 色噜噜狠狠成人中文综合| 欧美大香线蕉线伊人久久国产精品| 最近中文字幕免费在线观看| 亚洲乱码在线| 精品一区二区亚洲| 日本人69视频| 成年女人在线看片| 国产精品色哟哟| 成人h视频在线观看| 黄色免费av网站| 亚洲先锋影音| 国产亚洲欧美另类中文| 性生交大片免费看l| 大胆人体一区二区| 一区二区三区欧美视频| 欧美一卡2卡3卡4卡无卡免费观看水多多| 91久久精品无码一区二区| 激情亚洲网站| 久久激情视频久久| 国产免费一区二区三区网站免费| 麻豆久久一区| 在线日韩国产精品| 91精品国产91久久久久麻豆 主演| 国产资源在线播放| 成人免费毛片片v| 91欧美激情另类亚洲| 波多野结衣 久久| 在线播放一区| 九九久久国产精品| 国产又色又爽又高潮免费| 亚洲日本三级| 日韩av在线电影网| 中文字幕制服丝袜| 国产日韩一区二区三免费高清| 日本精品一区二区三区高清| 欧美精品自拍视频| 青春草视频在线| 亚洲人成亚洲人成在线观看图片| 欧美日韩电影一区二区三区| 亚洲 另类 春色 国产| 国产九色精品成人porny| 国产精品欧美久久久| 国产黄网在线观看| 日韩一级在线| 韩国日本不卡在线| 日韩欧美国产亚洲| 伊人久久大香线蕉综合热线| 欧美大片网站在线观看| 美国黄色小视频| 欧美一区二区三区另类| 久久综合国产精品台湾中文娱乐网| 极品久久久久久久| 日韩精品免费| 日韩小视频在线观看| 国产一区二区三区视频播放| 欧美系列电影免费观看| 中文字幕精品网| 精品亚洲乱码一区二区| 亚洲国产不卡| 欧美日韩成人精品| 福利一区二区三区四区| 夜夜嗨网站十八久久| 91精品国产高清久久久久久91 | 国产黄色免费大片| 国产一区二区0| 国产精品av一区| 欧美一区二区黄片| 久久久九九九九| 亚洲国产成人不卡| 69成人在线| 亚洲.国产.中文慕字在线| 日韩中文字幕三区| 精品国产欧美日韩一区二区三区| 欧美日韩二区三区| 亚洲欧美激情一区二区三区| 欧美一级一片| 曰本色欧美视频在线| 51精品免费网站| 黄色综合网站| 日韩av电影院| 亚洲一区二区三区网站| 成人免费看黄yyy456| 久久久久久久久久久久久久久久av| 国产在线自天天| 亚洲乱码国产乱码精品精可以看| 无码人妻少妇伦在线电影| 色婷婷综合久久久中字幕精品久久| 欧美裸体一区二区三区| 无码一区二区精品| 97久久视频| 2019亚洲日韩新视频| 怡春院在线视频| 成人精品国产免费网站| 天堂av一区二区| 男人天堂亚洲天堂| 欧洲在线/亚洲| 国产精品熟妇一区二区三区四区| 免费看成人吃奶视频在线| 美女福利精品视频| 日日夜夜狠狠操| 国产91在线观看丝袜| 色999日韩自偷自拍美女| 中文字幕伦理免费在线视频| 欧美午夜片欧美片在线观看| 一卡二卡三卡四卡五卡| 超碰成人久久| 欧美野外猛男的大粗鳮| hs视频在线观看| 国产精品私人影院| 国产成人a亚洲精v品无码| 日韩一区二区三区色| 中文字幕亚洲色图| 色屁屁影院www国产高清麻豆| 激情久久久久久久久久久久久久久久| 精品国产综合| 色呦呦在线资源| 欧美日韩一区二区三区四区 | 99re只有精品| 欧美激情自拍偷拍| 国产aaa一级片| 激情av综合| 久久久久久久久久婷婷| 国产视频手机在线观看| 亚洲国产精品精华液ab| 久久综合色视频| av综合网址| 欧美噜噜久久久xxx| 国产又大又黑又粗| 国产精品美女视频| 成年网站在线播放| 狠狠操综合网| 欧美一级视频在线观看| 熟妇人妻系列aⅴ无码专区友真希| 亚洲欧洲精品天堂一级| 最新中文字幕免费视频| 国模精品一区| 国产精品看片资源| 岛国视频免费在线观看| 欧美午夜精品久久久久久人妖| 在线精品一区二区三区| 亚洲久久成人| 国产原创精品| 国产www视频在线观看| 欧美第一区第二区| 国产在线一二区| 成人激情文学综合网| 日韩中文字幕在线免费| 欧美一区二区三区红桃小说| 国语自产精品视频在线看一大j8 | av动漫在线看| 日韩美脚连裤袜丝袜在线| 97在线免费观看视频| 人妻无码中文字幕| 亚洲成人自拍网| 少妇搡bbbb搡bbb搡打电话| 欧美亚韩一区| 久久99精品久久久久久秒播放器| 多野结衣av一区| 日韩成人小视频| 四虎影院在线免费播放| 国产亚洲福利社区一区| 天天爽天天爽夜夜爽| 欧美手机视频| 成人网在线视频| 欧美人与牲禽动交com| 欧美成人女星排行榜| 日本三级视频在线| 久久久国际精品| 奇米影视四色在线| 亚洲色图二区| 狠狠久久综合婷婷不卡| 欧美momandson| 日日狠狠久久偷偷四色综合免费 | 国产不卡视频在线观看| 久久久久久久久久久99| 久久91成人| 91亚洲精品在线| а√在线中文在线新版| 自拍偷拍亚洲一区| 国模私拍视频在线| 欧洲亚洲国产日韩| 欧美日韩在线观看免费| 久久综合久久综合久久| 国产无色aaa| 一本色道久久| 艳母动漫在线免费观看| 加勒比视频一区| 国产精品视频一区二区三区四| 18加网站在线| 一区二区三区在线播放欧美| 亚洲免费一级片| 欧美自拍偷拍一区| 国产亚洲精久久久久久无码77777| 91免费视频网| 午夜不卡福利视频| 久久国产一二区| 日韩精品手机在线观看| 国产伦一区二区三区| 97伦理在线四区| 91国内外精品自在线播放| 欧美精品一区二区三区国产精品| 欧美在线观看在线观看| 日韩一区和二区| 无码日韩精品一区二区| 亚洲在线视频网站| 潘金莲一级黄色片| 久久人人97超碰com| 久久发布国产伦子伦精品| 日韩国产精品91| 蜜桃传媒一区二区三区| 亚洲乱码在线| 亚洲AV无码成人精品一区| 亚洲免费观看高清完整版在线观| 7777精品伊久久久大香线蕉语言| 国产高清不卡| 97免费中文视频在线观看| 黄色小网站在线观看| 一区二区三区视频在线| 毛片网站在线| 日韩电视剧免费观看网站| 亚洲精品喷潮一区二区三区 | 一级特黄性色生活片| 亚洲理论在线| 2019日韩中文字幕mv| 欧美高清视频在线观看mv| 奇米影视首页 狠狠色丁香婷婷久久综合| 日韩一区二区三区色| 91免费的视频在线播放| 高清欧美日韩| 国产精品视频自拍| 精品国产欧美日韩一区二区三区| 国产91露脸中文字幕在线| 日韩伦理在线| 欧美一级电影久久| 欧美xxxhd| 国产91精品久| 惠美惠精品网| 国产成人精彩在线视频九色| 欧美aa视频| 国产精品老牛影院在线观看| 农村妇女一区二区| 国产伊人精品在线| 91久久青草| 91探花福利精品国产自产在线| 欧美一区=区三区| 国产在线精品播放| 国产精品一区二区精品视频观看| 91久久在线播放| 91蜜桃臀久久一区二区| 国产九色精品| 图片婷婷一区| 天堂va久久久噜噜噜久久va| 国产精品久久久久久久免费观看| 成年人免费观看的视频| 亚洲字幕久久| 三上悠亚久久精品| 国产精品日本| 我要看一级黄色大片| 国产精品一区在线观看你懂的| 欧美成人精品一区二区综合免费| 9l国产精品久久久久麻豆| 午夜在线观看一区| 综合久久给合久久狠狠狠97色| 一区二区视频免费看| 午夜影院在线观看欧美| 日本黄色一级视频| 欧美妇女性影城| 黄色a在线观看| 亚洲欧美中文在线视频| 日本蜜桃在线观看| 高清在线视频日韩欧美| 肉色欧美久久久久久久免费看| 91精品国产综合久久久久久久久| 亚洲福利合集| 欧美日韩系列| 女同性一区二区三区人了人一| 欧美二区在线视频| 麻豆成人免费电影| 91丨porny丨对白| 日本一区二区综合亚洲| 日韩欧美中文字幕视频| 色综合中文综合网| 国产999久久久| 亚洲欧美精品suv| 成人直播在线| 日韩免费在线看| 97久久综合区小说区图片区| 日本一区视频在线| 在线成人av| 免费成人黄色大片| 久久久久亚洲综合| 好吊色视频在线观看| 欧洲色大大久久| 熟妇高潮一区二区三区| 久久综合国产精品台湾中文娱乐网| 精品捆绑调教一区二区三区| 91免费综合在线| 激情五月色综合国产精品| 国产精品www在线观看| 久久精品72免费观看| 精品人妻一区二区三区视频| 亚洲精选视频免费看| 欧美性猛交xxxx乱大交hd | 成人午夜黄色影院| 台湾亚洲精品一区二区tv| 国产日韩欧美大片| 美国十次了思思久久精品导航| 日本护士做爰视频| 一区二区三区**美女毛片| 在线免费看av片| 亚洲人成在线观看网站高清| 999av小视频在线| 91传媒免费看| 欧美激情国产在线| 久久综合伊人77777麻豆最新章节| 99免费精品在线观看| 麻豆成人在线视频| 日韩欧美资源站| 黄色成年人视频在线观看| 国产精品福利久久久| 亚洲系列另类av| 久久免费视频3| 99精品欧美一区二区蜜桃免费| 欧美日韩免费做爰视频| 欧美久久久久中文字幕| 色欧美激情视频在线| 国产精品久久久久久久久粉嫩av| 小嫩嫩12欧美| 69堂免费视频| 91美女片黄在线观看91美女| 日韩av黄色片| 日韩精品日韩在线观看| 蜜桃麻豆影像在线观看| 精品一区二区国产| 国产精品普通话对白| 日本免费福利视频| 色综合一区二区| 免费理论片在线观看播放老| 国产z一区二区三区| 国产精品亚洲人成在99www| 久久久久免费精品| 国产日产精品一区| 夜夜嗨aⅴ一区二区三区| 爽爽爽爽爽爽爽成人免费观看| 美女视频一区| 超碰超碰超碰超碰超碰| 国产成人av一区二区三区在线| 久久午夜鲁丝片午夜精品| 精品99999| 手机在线观看av| 欧美三日本三级少妇三99| 日韩精品亚洲一区| 极品色av影院| 欧美xingq一区二区| 国产极品在线观看| 日韩.欧美.亚洲| 激情综合色播激情啊| 免费一级a毛片夜夜看 | 亚洲中文字幕一区| 色哟哟国产精品| 欧美日韩欧美| 成人动漫视频在线观看免费| 99精品国产99久久久久久福利| 极品人妻一区二区三区| 欧美私人免费视频| 综合图区亚洲| 久久大片网站| 美女网站色91| 久久久久久久国产视频| 日韩精品在线观| 激情小说亚洲| 免费av手机在线观看| 亚洲国产高清aⅴ视频| www.日本在线观看| 欧美在线中文字幕| 久久久人成影片免费观看| 黄色网址在线视频| 欧美视频一区二区| 国产盗摄一区二区| 亚洲精品tv久久久久久久久| 国产69精品久久99不卡| 天天射天天干天天| 欧美日韩福利在线观看| 国产精品羞羞答答在线观看| 人妻精油按摩bd高清中文字幕| 色中色一区二区| 欧洲成人综合网| 亚洲mv在线看| 成a人片亚洲日本久久| 在线观看免费视频一区| 欧美黄色片视频| 久久影视一区| 成人免费av片| 精品日韩在线一区| 久久人体av| 无码人妻精品一区二区三区在线|