withTimeout方法可以在搜寻设备时等待指定的秒数,如果30秒内未搜索到则取消搜索 

/**

* 超时控制函数

* @param {Promise} promise 回调函数

* @param {number} timeout 超时时间, 默认10s

*/

export function withTimeout(promise, timeout = 10000) {

let timeoutEvent = null

const logicPromise = new Promise((resolve, reject) => {

promise.then((data) => {

if (timeoutEvent) {

// 清理超时

clearTimeout(timeoutEvent)

timeoutEvent = null

}

resolve(data)

})

})

// 创建一个新的 Promise 对象,用于处理超时情况

const timeoutPromise = new Promise((resolve, reject) => {

timeoutEvent = setTimeout(() => {

reject(`执行超时`);

}, timeout);

});

// 将两个 Promise 对象(原始的 promise 和超时的 promise)合并为一个新的 Promise 对象

return Promise.race([logicPromise, timeoutPromise]);

}

计算数据校验和:

校验字节等于命令字节与所有数据字节之和的反码。求和按带进位加 (ADDC)方式计算,每个进位都被加到本次结果的最低位(LSB)。

举例:如命令字节=0x01;

数据=0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF,0x00,0x01;则校验字节0x01+0xF0+0xF1+0xF2+0xF3+0xF4+0xF5+0xF6+0xF7+0xF8+0xF9+0xFA

+0xFB+0xFC+0xFD+0xFE+0xFF+0x00+0x01 = 0x0F79;

0x0F+0x79 = 0x88;

校验字节 = 0xFF – 0x88 = 0x77。

getAddc(str) {

let itotal = 0,

len = str.length,

num = 0;

var tempTotal = "";

while (num < len) {

let s = str.substring(num, num + 2);

itotal += parseInt(s, 16);

num = num + 2;

if (itotal >= 256) {

itotal = parseInt((itotal - itotal % 256) / 256) + itotal % 256

}

}

itotal = 255 - itotal

return itotal.toString(16).padStart(2, '0')

}

vue页面代码  

js函数代码 

export class Fragrance {

// 读UUID:0000FEC1-0000-1000-8000-XXXXX

#appointNotifyCharacteristicId = '0000FEC1-0000-1000-8000-XXXXX'

// 写UUID:0000FEC1-0000-1000-8000-XXXXX

#appointWriteCharacteristicId = '0000FEC1-0000-1000-8000-XXXXX'

// 指定服务ID

#appointServiceId = '0000FEE7-0000-1000-8000-XXXXX'

// 指定设备ID

#appointDeviceId = '60E962AD-434B-F6EF-D82C-XXXXX'

// 读UUID:0000fec1-0000-1000-8000-XXXXX

#notifyCharacteristicId = '0000FEC1-0000-1000-8000-XXXXX'

// 写UUID:0000fec1-0000-1000-8000-XXXXX

#writeCharacteristicId = '0000FEC1-0000-1000-8000-XXXXX'

//

#serviceId = '0000FEE7-0000-1000-8000-XXXXX'

// 设备ID

#deviceId = ''

#deviceReturnData = ''

#connected = false // 连接状态标志位

isConnected() {

return this.#connected

}

/**

* 关闭蓝牙连接, 关闭蓝牙模块

*/

async close() {

try {

this.#connected = false

// 关闭蓝牙连接

await uni.closeBLEConnection({

deviceId: this.#deviceId

})

// 关闭蓝牙模块

await uni.closeBluetoothAdapter()

} catch (e) {

// 这里错误 不处理

console.error('关闭蓝牙错误', e)

}

}

// 开始搜索蓝牙uni.startBluetoothDevicesDiscovery(),使用uni.onBluetoothDeviceFound 注册搜索到的设备,

async ScanDevice() {

var that = this

return new Promise((resolve, reject) => {

console.log("开始搜索")

try {

uni.startBluetoothDevicesDiscovery({

interval: 0, // 上报设备的间隔。0 表示找到新设备立即上报,其他数值根据传入的间隔上报

allowDuplicatesKey: true,

// services: ['0000FEE7'], // 只搜索主服务 UUID 为 0000FEE7 的设备

// 扫描模式,越高扫描越快,也越耗电。仅安卓微信客户端 7.0.12 及以上支持。

// low:低,medium:中,high:高

powerLevel: 'high',

success(res) {

uni.onBluetoothDeviceFound(function(devices) {

var d = {}

if (typeof devices == Array) {

d = devices[0]

} else {

d = devices.devices[0]

}

// console.log('onBluetoothDeviceFound-', d.name)

// 找到名称 KLOVEF-AirFragranceSystem 的设备是代表已搜索到

if (d.name == 'KLOVEF-AirFragranceSystem') {

// console.log('onBluetoothDeviceFound-', devices)

that.#deviceId = d.deviceId

resolve(d.deviceId)

///console.log("this.#deviceId-aaa", that.#deviceId)

// uni.stopBluetoothDevicesDiscovery({

// success(res) {

// console.log('关闭成功')

// },

// fail(res) {

// console.log('关闭失败' + res.errMsg)

// }

// })

}

})

},

fail(res) {

console.log("搜索失败", res.errMsg)

}

})

} catch (e) {

console.log("结束搜索-111")

console.error('ScanDevice' + e)

}

console.log("结束搜索-222")

})

}

HexToConfig(HexStr) {

if (HexStr.length != 40) { //校验数据长度

return null

}

if (!this.checkAddc(HexStr)) { //校验ADDC

return null

}

var FragranceType = parseInt(HexStr.substring(2, 4), 16)

var FragranceMode = parseInt(HexStr.substring(4, 6), 16) + 1

//返回和写入时一致的数据,旧返回数据 (关闭状态=0x00, 打开状态=0x01)

//目标返回数据 (未控制=0x00, 打开香氛=0x01, 关闭香氛=0x02)

var FragranceWorkState = parseInt(HexStr.substring(6, 8), 16)

if (FragranceWorkState != 1) {

FragranceWorkState = 2

}

//返回和写入时一致的数据,旧返回数据 等离子工作状态(关闭状态=0x00, 打开状态=0x01)

//目标返回数据 (未控制=0x00, 打开等离子=0x01, 关闭等离子=0x02)

var IPCWorkState = parseInt(HexStr.substring(8, 10), 16)

if (IPCWorkState != 1) {

IPCWorkState = 2

}

var CurrentFragranceWorkChannel = parseInt(HexStr.substring(10, 12), 16) + 1

var FirstFragranceType = parseInt(HexStr.substring(12, 14), 16)

var SecondFragranceType = parseInt(HexStr.substring(14, 16), 16)

var FirstFragranceResidue = parseInt(HexStr.substring(16, 18), 16)

//console.log("FirstFragranceResidue",HexStr.substring(16, 18), 16)

var SecondFragranceResidue = parseInt(HexStr.substring(18, 20), 16)

var AirQualityLevel = parseInt(HexStr.substring(20, 22), 16)

//返回和写入时一致的数据,旧返回数据 等离子工作状态((关闭状态=0x00, 打开状态=0x01)

//目标返回数据 (未控制=0x00, 打开语音控制=0x01, 关闭语音控制=0x02)

var VoiceControlState = parseInt(HexStr.substring(22, 24), 16)

if (VoiceControlState != 1) {

VoiceControlState = 2

}

//这里为了方便前端显示,转换成索引

var LightFragranceModeStopTime = parseInt(HexStr.substring(24, 26), 16) / 2 - 1

var LightFragranceModeWorkTime = parseInt(HexStr.substring(26, 28), 16) / 2 - 1

var MiddleFragranceModeStopTime = parseInt(HexStr.substring(28, 30), 16) / 2 - 1

var MiddleFragranceModeWorkTime = parseInt(HexStr.substring(30, 32), 16) / 2 - 1

var StrongFragranceModeStopTime = parseInt(HexStr.substring(32, 34), 16) / 2 - 1

var StrongFragranceModeWorkTime = parseInt(HexStr.substring(34, 36), 16) / 2 - 1

return {

//香氛机型(单香机型=0x01, 两香机型=0x02)

FragranceType: FragranceType,

//当前香氛模式(自动模式=0x00,淡香模式=0x01,中香模式=0x02,浓香模式=0x03);

FragranceMode: FragranceMode,

//香氛工作状态(关闭状态=0x00, 打开状态=0x01)

FragranceWorkState: FragranceWorkState,

//等离子工作状态(关闭状态=0x00, 打开状态=0x01)。

IPCWorkState: IPCWorkState,

//当前香氛工作通道(1号香氛=0x00, 2号香氛=0x01)

CurrentFragranceWorkChannel: CurrentFragranceWorkChannel,

//Byte6= 1号香氛类型(未配置=0x00,青茶香氛=0x01,鸢尾香氛=0x02,茉莉香氛=0x03,

//森林香氛=0x04,蓝风铃香氛=0x05,水蜜桃香氛=0x06,海洋香氛=0x07,浪漫香氛=0x08,魅力香氛=0x09)

FirstFragranceType: FirstFragranceType,

//2号香氛类型(未配置=0x00,青茶香氛=0x01,鸢尾香氛=0x02,茉莉香氛=0x03,

//森林香氛=0x04,蓝风铃香氛=0x05,水蜜桃香氛=0x06,海洋香氛=0x07,浪漫香氛=0x08,魅力香氛=0x09)

SecondFragranceType: SecondFragranceType,

//1号香氛余量(0~100)

FirstFragranceResidue: FirstFragranceResidue,

//2号香氛余量(0~100)

SecondFragranceResidue: SecondFragranceResidue,

//空气质量等级(未配置=0x00,优=0x01, 良=0x02, 中=0x03, 差=0x04)

AirQualityLevel: AirQualityLevel,

//语音控制使能状态(关闭状态=0x00, 打开状态=0x01)

VoiceControlState: VoiceControlState,

//淡香模式停止时间反馈(秒)

LightFragranceModeStopTime: LightFragranceModeStopTime,

//淡香模式工作时间反馈(秒)

LightFragranceModeWorkTime: LightFragranceModeWorkTime,

//中香模式停止时间反馈(秒)

MiddleFragranceModeStopTime: MiddleFragranceModeStopTime,

//中香模式工作时间反馈(秒)

MiddleFragranceModeWorkTime: MiddleFragranceModeWorkTime,

//浓香模式停止时间反馈(秒)

StrongFragranceModeStopTime: StrongFragranceModeStopTime,

//浓香模式工作时间反馈(秒)

StrongFragranceModeWorkTime: StrongFragranceModeWorkTime,

}

}

/**

* 开始通知

*/

startNotice() {

console.log("startNotice-监听数据")

var that = this;

uni.notifyBLECharacteristicValueChange({

state: true, // 启用 notify 功能

// 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接

deviceId: that.#deviceId,

// 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取

serviceId: that.#serviceId,

// 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取

characteristicId: that.#writeCharacteristicId,

success(res) {

//接收蓝牙返回消息

uni.onBLECharacteristicValueChange((sjRes) => {

// 此时可以拿到蓝牙设备返回来的数据是一个ArrayBuffer类型数据,

//所以需要通过一个方法转换成字符串

that.#deviceReturnData = that.ab2hex(sjRes.value) //10.0

console.log("startNotice-that.#deviceReturnData", that.#deviceReturnData)

})

},

fail(err) {

// uni.showToast({

// title: '通知失败',

// icon: 'error',

// duration: 2000

// })

console.log("startNotice", err)

}

})

}

//手机写入开关控制数据

async WriteSwitchControlData(f) {

var hexStr = "02" + f.FragranceMode.toString().padStart(2, '0') + f.CurrentFragranceWorkChannel.toString()

.padStart(2, '0') + f.FragranceWorkState.toString().padStart(2, '0') + f.IPCWorkState.toString()

.padStart(2, '0') + f.VoiceControlState.toString().padStart(2, '0')

//console.log("hexStr", hexStr)

hexStr = hexStr + "00000000000000"

hexStr = this.addAddc(hexStr)

var isOK = await this.SetFragranceInfo(hexStr)

//console.log("手机写入香氛设置数据", isOK)

return isOK

}

//手机写入香氛设置数据

async WriteFragranceSetData(f) {

var hexStr = "03" + f.LightFragranceModeStopTime.toString(16).padStart(2, '0') + f

.LightFragranceModeWorkTime.toString(16).padStart(2, '0') + f.MiddleFragranceModeStopTime.toString(16)

.padStart(2, '0') + f.MiddleFragranceModeWorkTime.toString(16).padStart(2, '0') + f

.StrongFragranceModeStopTime.toString(16).padStart(2, '0') + f.StrongFragranceModeWorkTime.toString(16)

.padStart(2, '0')

hexStr = hexStr + "000000000000"

hexStr = this.addAddc(hexStr)

var isOK = await this.SetFragranceInfo(hexStr)

//console.log("手机写入香氛设置数据", isOK)

return await this.SetFragranceInfo(hexStr)

}

//设置香氛数据

async SetFragranceInfo(hexStr) {

//console.log("增加addc 后 SetFragranceInfo-hexStr", hexStr)

var that = this

var buffer = that.string2buffer(hexStr); //9.0

return new Promise((resolve, reject) => {

try {

uni.writeBLECharacteristicValue({

// 这里的 deviceId 需要在 getBluetoothDevices 或 onBluetoothDeviceFound 接口中获取

deviceId: that.#deviceId,

// 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取

serviceId: that.#serviceId,

// 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取

characteristicId: that.#writeCharacteristicId,

// 这里的 value 是ArrayBuffer类型

value: buffer,

success(res) {

//that.startNotice()

//console.log('SetFragranceInfo success', res)

if (res.errCode == 0) {

//console.log("返回写入成功")

resolve(true)

} else {

resolve(false)

}

},

fail(res) {

console.log('writeBLECharacteristicValue fail', res.errMsg)

console.log("返回写入失败")

//return false

resolve(false)

},

complete(res) {

console.log('writeBLECharacteristicValue ', JSON.stringify(res))

}

})

} catch (e) {

console.error('uni.onBLECharacteristicValueChange' + e)

}

})

}

//设置香氛数据

async SetFragranceInfoByCode(code) {

var that = this

//向蓝牙设备发送一个0x00的16进制数据

//打开香氛

var hexStr = "02" + code + "0101010300000000000000"

hexStr = this.addAddc(hexStr)

//console.log(code + "写入开关控制数据-hexStr", hexStr)

var buffer = that.string2buffer(hexStr); //9.0

//console.log("开始发送数据-写入开关控制数据")

uni.writeBLECharacteristicValue({

// 这里的 deviceId 需要在 getBluetoothDevices 或 onBluetoothDeviceFound 接口中获取

deviceId: that.#deviceId,

// 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取

serviceId: that.#serviceId,

// 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取

characteristicId: that.#writeCharacteristicId,

// 这里的 value 是ArrayBuffer类型

value: buffer,

success(res) {

//that.startNotice()

console.log('SetFragranceInfo success', res.errMsg)

},

fail(res) {

console.log('writeBLECharacteristicValue fail', res.errMsg)

},

complete(res) {

console.log('writeBLECharacteristicValue ', JSON.stringify(res))

}

})

}

//增加ADDC校验

addAddc(str) {

return str + this.getAddc(str)

}

getAddc(str) {

let itotal = 0,

len = str.length,

num = 0;

var tempTotal = "";

while (num < len) {

let s = str.substring(num, num + 2);

itotal += parseInt(s, 16);

num = num + 2;

if (itotal >= 256) {

itotal = parseInt((itotal - itotal % 256) / 256) + itotal % 256

}

}

itotal = 255 - itotal

return itotal.toString(16).padStart(2, '0')

}

checkAddc(str) {

if (str.length != 40) {

return false

}

var data = str.substring(0, 38)

var allData = this.addAddc(data)

return str == allData

}

//读取香氛设备信息

async ReadFragranceInfo() {

var that = this

// 向蓝牙设备发送一个0x00的16进制数据

var buffer = this.string2buffer('01000000000000000000000000FE'); //9.0

console.log("this.#deviceId", this.#deviceId)

console.log("this.#serviceId", this.#serviceId)

console.log("this.#writeCharacteristicId", this.#writeCharacteristicId)

var that = this

uni.writeBLECharacteristicValue({

// 这里的 deviceId 需要在 getBluetoothDevices 或 onBluetoothDeviceFound 接口中获取

deviceId: that.#deviceId,

// 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取

serviceId: that.#serviceId,

// 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取

characteristicId: that.#writeCharacteristicId,

// 这里的 value 是ArrayBuffer类型

value: buffer,

success(res) {

that.startNotice()

},

fail(res) {

console.log('writeBLECharacteristicValue fail', res.errMsg)

},

complete(res) {

console.log('writeBLECharacteristicValue ', JSON.stringify(res))

}

})

}

getReturnDataStr() {

let returnData = this.#deviceReturnData

return returnData

}

getReturnData() {

//console.log("that.#deviceReturnData-999", this.#deviceReturnData)

var config = this.HexToConfig(this.#deviceReturnData)

//console.log("config", config)

return config

}

// ArrayBuffer转16进度字符串示例

ab2hex(buffer) {

if (!buffer) return ""

const hexArr = Array.prototype.map.call(

new Uint8Array(buffer),

function(bit) {

return ('00' + bit.toString(16)).slice(-2)

}

)

return hexArr.join('')

}

/**

* 将字符串转换成ArrayBufer

*/

string2buffer(str) {

let val = ""

if (!str) return;

let length = str.length;

let index = 0;

let array = []

while (index < length) {

array.push(str.substring(index, index + 2));

index = index + 2;

}

val = array.join(",");

// 将16进制转化为ArrayBuffer

return new Uint8Array(val.match(/[\da-f]{2}/gi).map(function(h) {

return parseInt(h, 16)

})).buffer

}

/**

* 连接低功耗蓝牙设备

* @param {string} deviceId 设备ID

* @param {number} timeout 蓝牙连接超时时间, 单位ms, 默认3000ms(3秒)

* @param {number} mut 每次传输的数据大小, 默认244bytes(C385协议上就是默认每次传一帧数据, 总大小为244byte)

* @param {number} retry 错误重试, 默认2次

* @param {number} first 是否首次进入,如果是则重置 #deviceId

*/

async BLEConnect(deviceId, timeout = 3000, mtu = 244, retry = 3, first = true) {

console.log("开始连接蓝牙" + retry)

if (retry < 0) {

// 重试次数已达到, 执行回调反馈给调用端

return [false, 0]

}

try {

//1. 检查适配器是否启动 uni.getBluetoothAdapterState(),没有启动则启动蓝牙适配器 uni.openBluetoothAdapter

// this.openBluetoothAdapter()

if (first) {

this.#deviceId = ''

}

const [bluetooth, state] = await uni.openBluetoothAdapter()

console.log("bluetooth", bluetooth)

console.log("state", state)

if (bluetooth) {

//console.log("typeof(bluetooth)",bluetooth)

if (bluetooth.errno == 103 || bluetooth.errMsg.indexOf("fail auth deny") != -1) {

return [false, 103] //103需要打开蓝牙权限

}

return [false, bluetooth.errCode] //10001需要打开蓝牙

}

const [adapter, adapterState] = await uni.getBluetoothAdapterState()

console.log("adapter", adapter)

console.log("adapterState", adapterState)

if (typeof(adapterState) == "undefined") {

//return showModal()

}

if (!adapterState.discovering && retry == 0) {

return [false, 10002] //未开启定位权限

}

if (!adapterState.available) {

return [false, 10001] //蓝牙未打开

}

if (first || this.#deviceId == '') { // 如果没有搜到过设备则重新进入搜索,如果已经搜到过就无需搜索了

try {

const deviceId = await withTimeout(this.ScanDevice(), 3000)

console.log('deviceId', deviceId)

} catch (e) {

console.log('ScanDevice', err)

return [false, 100]

}

uni.stopBluetoothDevicesDiscovery({

success(res) {

console.log('关闭成功')

},

fail(res) {

console.log('关闭失败' + res.errMsg)

}

})

// await this.ScanDevice()

//console.log("this.#deviceId",this.#deviceId)

if (this.#deviceId == "" && retry < 0) { //需要打开设备

return [false, 100]

}

if (this.#deviceId == "") {

console.log('deviceId not value')

retry = retry - 1

return that.BLEConnect(this.#deviceId, timeout, mtu, retry)

}

}

const [createErr, createSccess] = await uni.createBLEConnection({

deviceId: this.#deviceId,

timeout

})

//console.log('createBLEConnection', createErr, createSccess)

if (createErr) {

try {

// 需要成对出现,如果连接错误也需要退出

await uni.closeBLEConnection({

deviceId: this.#deviceId

})

} catch {}

// 连接错误

console.log("连接错误", createErr)

if (createErr.errCode == 10012 && retry == 0) {

return [false, createErr.errCode] //10012,需要打开设备

} else if (createErr.errCode != -1) {

retry = retry - 1

return await this.BLEConnect(this.#deviceId, timeout + 2000, mtu, retry, false)

}

}

var that = this

//setTimeout(() => {

var isok = await this.getBLEDeviceServices()

console.log("isok-111", isok)

if (!isok) {

retry = retry - 1

return await this.BLEConnect(deviceId, timeout, mtu, retry, false)

}

isok = await this.getBLEDeviceCharacteristics()

console.log("isok-222", isok)

if (!isok) {

retry = retry - 1

return await this.BLEConnect(deviceId, timeout, mtu, retry, false)

}

return [true, 0]

} catch (e) {

console.log("重试", e)

// 重试

retry = retry - 1

//console.log("连接蓝牙失败-catch-333")

return await this.BLEConnect(deviceId, timeout, mtu, retry, false)

}

return [false, 0]

}

async getBLEDeviceServices() {

console.log("开始执行-getBLEDeviceServices")

var that = this

return new Promise((resolve, reject) => {

uni.getBLEDeviceServices({

deviceId: that.#deviceId,

success(res) {

// 5.启用notify

//console.log('getBLEDeviceServices', res)

// this.#connected = true

resolve(true)

console.log("开始执行-getBLEDeviceServices-执行成功")

// console.log("getBLEDeviceServices-res", res)

// if (res.errno == 0 && res.services.length > 0) {

// //console.log('getBLEDeviceServices-成功')

// if (res.services[0].uuid != that.#appointServiceId) {

// resolve(false)

// }

// that.#serviceId = res.services[0].uuid

// // that.#connected = true

// // that.startNotice()

// // console.log("蓝牙连接成功")

// resolve(true)

// console.log("开始执行-getBLEDeviceServices-执行成功")

// //})

// } else {

// resolve(false)

// }

},

fail(err) {

resolve(false)

}

})

})

}

async getBLEDeviceCharacteristics() {

var that = this

console.log("开始执行-getBLEDeviceCharacteristics")

return new Promise((resolve, reject) => {

uni.getBLEDeviceCharacteristics({

deviceId: that.#deviceId,

serviceId: that.#serviceId,

success(res) {

//console.log("getBLEDeviceCharacteristics-res", res)

if (res.characteristics.length > 0) {

// console.log("连接蓝牙成功-222")

// console.log("res.characteristics", res.characteristics)

if (res.characteristics[0].uuid != that

.#appointNotifyCharacteristicId) {

resolve(false)

}

that.#notifyCharacteristicId = res.characteristics[0].uuid

that.#writeCharacteristicId = res.characteristics[0].uuid

that.#connected = true

console.log("开始执行-成功-getBLEDeviceCharacteristics")

that.startNotice()

// console.log("this.#deviceId-999", that.#deviceId)

// console.log("this.#writeCharacteristicId ", that

// .#writeCharacteristicId)

console.log("蓝牙连接成功")

resolve(true)

}

},

fail() {

resolve(false)

}

})

})

}

/**

* 发送数据

* @param {ArrayBuffer} bytes

*/

async #sendData(bytes) {

await uni.writeBLECharacteristicValue({

deviceId: this.#deviceId, // 蓝牙设备 id

serviceId: this.#serviceId, // 蓝牙特征值对应服务的 uuid

characteristicId: this.#writeCharacteristicId, // 蓝牙特征值的 uuid

value: bytes, // 这里的value是ArrayBuffer类型

fail: (err) => {

console.log('writeBLECharacteristicValue', err)

},

})

}

// notify 接收消息回调函数

#sendDataCallback = null

// notify 接收消息回调函数消息队列(防止漏接收消息)

#sendDataCallbackMsgQueue = null

/**

* 监听低功耗蓝牙设备的特征值变化事件。必须先启用 notifyBLECharacteristicValueChange 接口才能接收到设备推送的 notification。

* @param {Function} callback 回调函数

*/

onBLEConnectionStateChange(callback) {

uni.onBLEConnectionStateChange((res) => {

// 该方法回调中可以用于处理连接意外断开等异常情况

// console.log(`设备 ${res.deviceId} 状态已经改变, connected: ${res.connected}`)

this.#connected = res.connected // 更新连接状态

callback(res)

})

}

}

/**

* 16进制字符串转16进制 ArrayBuffer

* @param {string} hexString 16进制字符串

*/

function string2HexArray(hexString) {

if (!hexString) return;

let length = hexString.length;

let index = 0;

let array = []

while (index < length) {

array.push(hexString.substring(index, index + 2));

index = index + 2;

}

const val = array.join(",");

return new Uint8Array(val.match(/[\da-f]{2}/gi).map((h) => parseInt(h, 16))).buffer

}

/**

* ArrayBuffer转16进度字符串示例

* @param {Array} buffer 字节数组

* @return {string} 转换后的字符串

*/

export function ab2hex(buffer) {

const hexArr = Array.prototype.map.call(

new Uint8Array(buffer),

(bit) => {

return ('00' + bit.toString(16)).slice(-2)

}

)

return hexArr.join('')

}

/**

* 判断是不是要连接的蓝牙设备 是不是以 OTA- 开头

* @param {string} deviceName 设备名称

*/

export function isOTADevice(deviceName) {

const prefix = "OTA-"

return deviceName.toUpperCase().startsWith(prefix)

}

function crc16(str) {

const data = Buffer.from(str, "hex")

let crcValue = 0xFFFF;

for (let i = 0; i < data.length; i++) {

crcValue ^= data[i] & 0xFFFF

for (let j = 0; j < 8; j++) {

if (crcValue & 0x0001) {

crcValue >>= 1

crcValue ^= 0xA001

} else {

crcValue >>= 1

}

}

}

crcValue = crcValue.toString(16).toUpperCase().padStart(4, '0')

return crcValue.substring(0, 2) + crcValue.substring(2, 4)

}

/**

* 字符串转ASCII码函数

* @param {string} str

*/

function toASCII(str) {

return str.split('').map(char => char.charCodeAt(0)).join('');

}

/**

* 十六进制字符串转ASCII码

* @param {string} hexStr

*/

function hexToASCII(hexStr) {

let result = '';

for (let i = 0; i < hexStr.length; i += 2) {

let hex = hexStr.substr(i, 2);

result += String.fromCharCode(parseInt(hex, 16));

}

return result;

}

/**

* 格式化长度

* @param {number} fileLen

*/

function formatLen(fileLen) {

return fileLen.toLocaleString('en-US', {

minimumIntegerDigits: 6,

useGrouping: false

}).padStart(6, '0');

}

/**

* 搜索监听C385升级设备

* @param {Function} notifier 回调函数

* @param {Function} callback 回调函数

* @param {Function} cancelCallback 取消回调函数

*/

export async function listenUpdateDevice(notifier, callback, cancelCallback = null) {

const showModal = () => {

uni.showModal({

title: '提示!',

content: '初始化蓝牙失败,请打开本机蓝牙!',

showCancel: true,

cancelText: "取消",

confirmText: "确定",

success: (res) => {

if (res.confirm) {

// 用户点击了确定

return listenUpdateDevice(notifier, callback, cancelCallback)

} else if (res.cancel) {

// 用户点击了取消

if (cancelCallback) {

cancelCallback()

}

return

}

}

});

}

try {

// 1.初始化蓝牙模块

const res = await uni.openBluetoothAdapter()

// 2.获取本机蓝牙适配器状态(正常状态下是一定能获取的)

let [_, state] = await uni.getBluetoothAdapterState()

if (!state) {

return listenUpdateDevice(notifier, callback, cancelCallback)

}

// console.log(state)

if (!state.available) {

return showModal()

}

// 3.开启搜寻附近的蓝牙外围设备

await uni.startBluetoothDevicesDiscovery()

// 通知已经开始搜索设备了

notifier()

// 4.开始监听搜索设备

uni.onBluetoothDeviceFound(async found => {

const d = found.devices[0]

if (isOTADevice(d.name) && d.connectable) {

const device = {

name: d.name, // 名称

id: d.advertisServiceUUIDs.length > 0 ? d.advertisServiceUUIDs[0] : '', // 设备ID

MAC: d.deviceId, // MAC地址

advertisData: ab2hex(d.advertisData).trim(), // 广播数据

RSSI: d.RSSI, // RSSI

connectable: d.connectable // 是否能连接

}

callback(device)

// 搜索到了要升级的设备旧关闭(startBluetoothDevicesDiscovery会很耗电, 所以搜索到蓝牙设备后要及时关毕)

// await uni.stopBluetoothDevicesDiscovery()

}

})

} catch (e) {

console.error('listenUpdateDevice错误', e)

return showModal()

}

}

/**

* 关闭搜索监听C385升级设备

* @param {Function} callback 回调函数

*/

export async function closeListenUpdateDevice() {

// 搜索到了要升级的设备旧关闭(startBluetoothDevicesDiscovery会很耗电, 所以搜索到蓝牙设备后要及时关毕)

await uni.stopBluetoothDevicesDiscovery()

}

/**

* ASCII 转 ArrayBuffer

* @param {string} str ASCII 字符串

*/

function textEncode(str) {

const bytes = [];

for (let i = 0; i < str.length; i++) {

let charcode = str.charCodeAt(i);

if (charcode < 0x80) {

bytes.push(charcode);

} else if (charcode < 0x800) {

bytes.push(0xC0 | (charcode >> 6),

0x80 | (charcode & 0x3F));

} else {

bytes.push(0xE0 | (charcode >> 12),

0x80 | ((charcode >> 6) & 0x3F),

0x80 | (charcode & 0x3F));

}

}

return new Uint8Array(bytes).buffer;

}

/**

* 超时控制函数

* @param {Promise} promise 回调函数

* @param {number} timeout 超时时间, 默认10s

*/

export function withTimeout(promise, timeout = 10000) {

let timeoutEvent = null

const logicPromise = new Promise((resolve, reject) => {

promise.then((data) => {

if (timeoutEvent) {

// 清理超时

clearTimeout(timeoutEvent)

timeoutEvent = null

}

resolve(data)

})

})

// 创建一个新的 Promise 对象,用于处理超时情况

const timeoutPromise = new Promise((resolve, reject) => {

timeoutEvent = setTimeout(() => {

reject(`执行超时`);

}, timeout);

});

// 将两个 Promise 对象(原始的 promise 和超时的 promise)合并为一个新的 Promise 对象

return Promise.race([logicPromise, timeoutPromise]);

}

参考阅读

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: