554 lines
17 KiB
JavaScript
554 lines
17 KiB
JavaScript
/**
|
||
* 初始化蓝牙模块
|
||
*
|
||
* @return {Object} 调用结果
|
||
*/
|
||
function openBluetoothAdapter() {
|
||
return new Promise((resolve, reject) => {
|
||
wx.openBluetoothAdapter({
|
||
success(res) {
|
||
resolve(res)
|
||
},
|
||
fail(res) {
|
||
reject(res)
|
||
}
|
||
})
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 关闭蓝牙模块
|
||
*
|
||
* @return {Object} 调用结果
|
||
*/
|
||
function closeBluetoothAdapter() {
|
||
return new Promise((resolve, reject) => {
|
||
wx.closeBluetoothAdapter({
|
||
complete(res) {
|
||
resolve(res)
|
||
}
|
||
})
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 监听蓝牙状态
|
||
*
|
||
* @param {function} onBluetoothStateChange 函数对象,监听蓝牙状态变化的回调函数
|
||
* @return void
|
||
*/
|
||
function onBluetoothAdapterStateChange(onBluetoothStateChange) {
|
||
return new Promise((resolve) => {
|
||
wx.onBluetoothAdapterStateChange(function(res) {
|
||
// console.log('onBluetoothAdapterStateChange, now is', res)
|
||
onBluetoothStateChange(res)
|
||
})
|
||
resolve()
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 启动搜索蓝牙设备功能
|
||
*
|
||
* @param {string} services 蓝牙服务
|
||
* @return void
|
||
*/
|
||
function startBluetoothDevicesDiscovery(services) {
|
||
return new Promise((resolve, reject) => {
|
||
wx.startBluetoothDevicesDiscovery({
|
||
services: services || [],
|
||
allowDuplicatesKey: false,
|
||
success(res) {
|
||
console.log("wx.startBluetoothDevicesDiscovery success")
|
||
resolve(res)
|
||
},
|
||
fail(res) {
|
||
console.log("wx.startBluetoothDevicesDiscovery fail")
|
||
reject(res)
|
||
}
|
||
})
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 关闭搜索蓝牙设备功能
|
||
*
|
||
* @return void
|
||
*/
|
||
function stopBluetoothDevicesDiscovery() {
|
||
return new Promise((resolve, reject) => {
|
||
wx.stopBluetoothDevicesDiscovery({
|
||
complete(res) {
|
||
console.log("wx.stopBluetoothDevicesDiscovery success")
|
||
resolve()
|
||
}
|
||
})
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 断开与低功耗蓝牙设备的连接
|
||
*
|
||
* @param {string} deviceId 蓝牙设备ID
|
||
* @return void
|
||
*/
|
||
function closeBLEConnection(deviceId) {
|
||
return new Promise((resolve, reject) => {
|
||
wx.closeBLEConnection({
|
||
deviceId,
|
||
success(res) {
|
||
console.log(`closeBLEConnection ${deviceId} success`, res)
|
||
},
|
||
fail(res) {
|
||
console.log(`closeBLEConnection ${deviceId} fail`, res)
|
||
},
|
||
complete(res) {
|
||
resolve()
|
||
}
|
||
})
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 连接低功耗蓝牙设备
|
||
*
|
||
* @param {string} deviceId 蓝牙设备ID
|
||
* @return {Object} 调用结果
|
||
*/
|
||
function createBLEConnection(deviceId) {
|
||
return new Promise((resolve, reject) => {
|
||
wx.createBLEConnection({
|
||
deviceId: deviceId,
|
||
timeout: 5000, // TO_CHECK: 设置连接蓝牙超时时间
|
||
success: function(res) {
|
||
console.log(`createBLEConnection ${deviceId} success`, res)
|
||
resolve(res)
|
||
},
|
||
fail: function(res) {
|
||
console.log(`createBLEConnection ${deviceId} fail`, res);
|
||
reject(new Error(res.errMsg || 'wx.createBLEConnection fail'))
|
||
}
|
||
})
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 监听低功耗蓝牙连接状态的改变事件
|
||
*
|
||
* @param {function} onStateChange 函数对象,连接状态变化的回调函数
|
||
* @return void
|
||
*/
|
||
function onBLEConnectionStateChange(onStateChange) {
|
||
return new Promise((resolve) => {
|
||
wx.onBLEConnectionStateChange(function(res) { // 该方法回调中可以用于处理连接意外断开等异常情况
|
||
// console.log(`onBLEConnectionStateChange device ${res.deviceId} state has changed, connected: ${res.connected}`)
|
||
onStateChange(res)
|
||
})
|
||
resolve()
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 获取蓝牙设备所有服务
|
||
*
|
||
* @param {string} deviceId 蓝牙设备ID
|
||
* @return {Object} 调用结果
|
||
*/
|
||
function getBLEDeviceServices(deviceId) {
|
||
return new Promise((resolve, reject) => {
|
||
wx.getBLEDeviceServices({
|
||
deviceId: deviceId,
|
||
success: function(res) {
|
||
console.log(`wx.getBLEDeviceServices ${deviceId} success:`, res.services)
|
||
resolve(res.services)
|
||
},
|
||
fail(res) { // 获取蓝牙服务失败
|
||
console.log(`wx.getBLEDeviceServices ${deviceId} fail:`, res)
|
||
reject(res.errMsg || 'wx.getBLEDeviceServices fail')
|
||
}
|
||
})
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 获取蓝牙设备某个服务中所有特征值
|
||
*
|
||
* @param {string} deviceId 蓝牙设备ID
|
||
* @param {string} serviceId 蓝牙服务ID
|
||
* @return {Object} 调用结果
|
||
*/
|
||
function getBLEDeviceCharacteristics(deviceId, serviceId) {
|
||
return new Promise((resolve, reject) => {
|
||
wx.getBLEDeviceCharacteristics({
|
||
deviceId: deviceId,
|
||
serviceId: serviceId,
|
||
success(res) {
|
||
console.log(`wx.getBLEDeviceCharacteristics ${deviceId} ${serviceId} success:`, res.characteristics)
|
||
resolve(res.characteristics)
|
||
},
|
||
fail(res) { // 读取蓝牙特征值失败
|
||
console.log(`wx.getBLEDeviceCharacteristics ${deviceId} ${serviceId} fail`, res)
|
||
reject(res.errMsg || 'wx.getBLEDeviceCharacteristics fail')
|
||
},
|
||
})
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 启用低功耗蓝牙设备特征值变化时的 notify 功能,订阅特征值
|
||
* 注:必须设备的特征值支持 notify 或者 indicate 才可以成功调用
|
||
*
|
||
* @param {string} deviceId 蓝牙设备ID
|
||
* @param {string} serviceId 蓝牙服务ID
|
||
* @param {string} notifyCharacteristicId 蓝牙特征值ID
|
||
* @return {Object} 调用结果
|
||
*/
|
||
function notifyBLECharacteristicValueChanged(deviceId, serviceId, notifyCharacteristicId) {
|
||
return new Promise((resolve, reject) => {
|
||
wx.notifyBLECharacteristicValueChanged({
|
||
deviceId: deviceId,
|
||
serviceId: serviceId,
|
||
characteristicId: notifyCharacteristicId,
|
||
state: true,
|
||
success(res) {
|
||
console.log(`wx.notifyBLECharacteristicValueChanged ${deviceId} ${serviceId} ${notifyCharacteristicId} success`);
|
||
resolve(true)
|
||
},
|
||
fail(err) {
|
||
console.log(`wx.notifyBLECharacteristicValueChanged ${deviceId} ${serviceId} ${notifyCharacteristicId} fail`, err);
|
||
reject("启用蓝牙特征值notify功能失败")
|
||
}
|
||
})
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 监听低功耗蓝牙设备的特征值变化事件
|
||
* 注:必须先启用 notifyBLECharacteristicValueChange 接口才能接收到设备推送的 notification
|
||
*
|
||
* @param {function} onMessage 函数对象,读数据的回调函数
|
||
* @return void
|
||
*/
|
||
function onBLECharacteristicValueChange(onMessage) {
|
||
var receivedData = null
|
||
return new Promise((resolve, reject) => {
|
||
wx.onBLECharacteristicValueChange(function(res) {
|
||
//{value: ArrayBuffer, deviceId: "D8:00:D2:4F:24:17", serviceId: "ba11f08c-5f14-0b0d-1080-007cbe238851-0x600000460240", characteristicId: "0000cd04-0000-1000-8000-00805f9b34fb-0x60800069fb80"}
|
||
onMessage(res.value)
|
||
})
|
||
resolve()
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 向低功耗蓝牙设备特征值中写入二进制数据
|
||
* 注:必须设备的特征值支持 write 才可以成功调用
|
||
*
|
||
* @param {string} deviceId 蓝牙设备ID
|
||
* @param {string} serviceId 蓝牙服务ID
|
||
* @param {string} writeCharacteristicId 蓝牙特征值ID
|
||
* @param {string} payload 数据,必须是ArrayBuffer类型
|
||
* @return {Object} 调用结果
|
||
*/
|
||
function writeBLECharacteristicValue(deviceId, serviceId, writeCharacteristicId, payload) {
|
||
return new Promise((resolve, reject) => {
|
||
wx.writeBLECharacteristicValue({
|
||
deviceId: deviceId,
|
||
serviceId: serviceId,
|
||
characteristicId: writeCharacteristicId,
|
||
value: payload, // parameter.value should be ArrayBuffer
|
||
fail(res) {
|
||
console.log(`writeBLECharacteristicValue fail: ${deviceId} ${serviceId} ${writeCharacteristicId}`, res)
|
||
reject(res)
|
||
},
|
||
success(res) {
|
||
resolve(res)
|
||
}
|
||
})
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 读取低功耗蓝牙设备的特征值的二进制数据值。
|
||
* 注:必须设备的特征值支持 read 才可以成功调用; 接口读取到的信息需要在 onBLECharacteristicValueChange 方法注册的回调中获取
|
||
*
|
||
* @param {string} deviceId 蓝牙设备ID
|
||
* @param {string} serviceId 蓝牙服务ID
|
||
* @param {string} readCharacteristicId 蓝牙特征值ID
|
||
* @return {Object} 调用结果
|
||
*/
|
||
function readBLECharacteristicValue(deviceId, serviceId, readCharacteristicId) {
|
||
return new Promise((resolve, reject) => {
|
||
wx.readBLECharacteristicValue({
|
||
deviceId: deviceId,
|
||
serviceId: serviceId,
|
||
characteristicId: readCharacteristicId,
|
||
fail(res) {
|
||
console.log(`readBLECharacteristicValue fail: ${deviceId} ${serviceId} ${readCharacteristicId}`, res)
|
||
reject(res)
|
||
},
|
||
success(res) {
|
||
resolve(res)
|
||
}
|
||
})
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 连接设备
|
||
*
|
||
* @param {string} deviceId 蓝牙设备ID
|
||
* @return {Object} 设备连接信息
|
||
*/
|
||
function connectDevice(deviceId) {
|
||
return closeBLEConnection(deviceId).then(() => {
|
||
// 断开重连需要间隔1s,否则会出现假连接成功
|
||
// 还有一种方式是 getConnectedBluetoothDevices,对比已连接的蓝牙,如果已连接直接跳到设备页面
|
||
return createBLEConnection(deviceId)
|
||
}).then(() => {
|
||
// 读取 BLE services
|
||
return getBLEDeviceServices(deviceId)
|
||
}).then(([deviceId, services]) => {
|
||
// 读取 BLE Characteristics
|
||
for (let i = 0; i < services.length; i++) {
|
||
if (services[i].isPrimary) { // 确定对应服务。这里使用第一个primary服务
|
||
let serviceId = services[i].uuid
|
||
return getBLEDeviceCharacteristics(deviceId, serviceId)
|
||
}
|
||
}
|
||
return Promise.reject(new Error('未找到蓝牙主服务'))
|
||
}).then(
|
||
// 必须先启用 notifyBLECharacteristicValueChange 才能监听到设备 onBLECharacteristicValueChange 事件
|
||
([deviceId, serviceId, characteristics]) => {
|
||
let notifyCharacteristicId = null
|
||
let writeCharacteristicId = null
|
||
let readCharacteristicId = null
|
||
characteristics.forEach(function(item, index) {
|
||
if (item.properties.notify == true) {
|
||
notifyCharacteristicId = item.uuid
|
||
notifyBLECharacteristicValueChanged(deviceId, serviceId, item.uuid)
|
||
}
|
||
if (item.properties.write == true) {
|
||
writeCharacteristicId = item.uuid
|
||
}
|
||
if (item.properties.read == true) {
|
||
readCharacteristicId = item.uuid
|
||
}
|
||
})
|
||
let device = {
|
||
connected: true,
|
||
deviceId: deviceId,
|
||
serviceId: serviceId,
|
||
notifyCharacteristicId: notifyCharacteristicId,
|
||
writeCharacteristicId: writeCharacteristicId,
|
||
readCharacteristicId: readCharacteristicId,
|
||
}
|
||
return Promise.resolve(device)
|
||
}
|
||
)
|
||
}
|
||
|
||
/**
|
||
* 根据名称搜索设备
|
||
*
|
||
* @param {string} name 蓝牙设备名称
|
||
* @param {string} timeout 搜索超时时间,单位ms
|
||
* @param {array} services 蓝牙设备的服务ID
|
||
* @return {string} deviceId 蓝牙设备ID
|
||
*/
|
||
function searchDeviceName(name, timeout, services = ["6E400001-B5A3-F393-E0A9-E50E24DCCA9E"]) {
|
||
console.log("search begin", name)
|
||
let timer = null
|
||
let searchTimeout = new Promise((resolve, reject) => {
|
||
timer = setTimeout(function() {
|
||
console.log("searchDeviceName timeout", name)
|
||
stopBluetoothDevicesDiscovery()
|
||
reject(null)
|
||
}, timeout);
|
||
})
|
||
|
||
let search = Promise.resolve()
|
||
.then(() => {
|
||
return closeBluetoothAdapter()
|
||
})
|
||
.then(() => {
|
||
return openBluetoothAdapter()
|
||
})
|
||
.then(() => {
|
||
return startBluetoothDevicesDiscovery(services)
|
||
})
|
||
.then(() => {
|
||
return new Promise((resolve) => {
|
||
console.log("wx.onBluetoothDeviceFound start")
|
||
wx.onBluetoothDeviceFound(function(res) {
|
||
let foundName = res.devices[0].localName // TODO: localName or name?
|
||
if (foundName && foundName == name) {
|
||
console.log("searchDeviceName found:", name, res);
|
||
wx.offBluetoothDeviceFound(this)
|
||
clearTimeout(timer)
|
||
stopBluetoothDevicesDiscovery()
|
||
resolve(res.devices[0].deviceId)
|
||
}
|
||
})
|
||
})
|
||
})
|
||
|
||
return Promise.race([search, searchTimeout])
|
||
}
|
||
|
||
async function searchDevices(name, timeout, services = ["6E400001-B5A3-F393-E0A9-E50E24DCCA9E"]) {
|
||
console.log("searchDevices", name)
|
||
let timer = null
|
||
let searchTimeout = new Promise((resolve, reject) => {
|
||
timer = setTimeout(function() {
|
||
console.log("searchDevices timeout", name)
|
||
stopBluetoothDevicesDiscovery()
|
||
reject(null)
|
||
}, timeout);
|
||
})
|
||
|
||
let search = Promise.resolve()
|
||
.then(() => {
|
||
return closeBluetoothAdapter()
|
||
})
|
||
.then(() => {
|
||
return openBluetoothAdapter()
|
||
})
|
||
.then(() => {
|
||
return startBluetoothDevicesDiscovery(services)
|
||
})
|
||
.then(() => {
|
||
return new Promise((resolve) => {
|
||
console.log("wx.onBluetoothDeviceFound start")
|
||
wx.onBluetoothDeviceFound(function(res) {
|
||
let foundName = res.devices[0].localName // TODO: localName or name?
|
||
if (foundName && foundName == name) {
|
||
console.log("searchDeviceName found:", name, res);
|
||
wx.offBluetoothDeviceFound(this)
|
||
clearTimeout(timer)
|
||
stopBluetoothDevicesDiscovery()
|
||
resolve(res.devices[0].deviceId)
|
||
}
|
||
})
|
||
})
|
||
})
|
||
|
||
return Promise.race([search, searchTimeout])
|
||
}
|
||
|
||
// function searchAndConnect(name, timeout, services = ["6E400001-B5A3-F393-E0A9-E50E24DCCA9E"]) {
|
||
function searchAndConnect(name, timeout, services = []) {
|
||
return searchDeviceName(name, timeout, services).then(res => {
|
||
let devicesList = res
|
||
if (devicesList.length == 0) {
|
||
return Promise.reject(false)
|
||
}
|
||
let deviceId = devicesList[0].deviceId
|
||
return connectDevice(deviceId)
|
||
})
|
||
}
|
||
|
||
// 扫描设备
|
||
function scanDevicesForSometime(services, ms) {
|
||
startBluetoothDevicesDiscovery(services).then(res => {
|
||
console.log("wx.startBluetoothDevicesDiscovery success", res)
|
||
})
|
||
setTimeout(function() {
|
||
wx.stopBluetoothDevicesDiscovery({
|
||
success(res) {
|
||
console.log("wx.stopBluetoothDevicesDiscovery success", res)
|
||
},
|
||
fail(e) {
|
||
console.log("wx.stopBluetoothDevicesDiscovery fail", res)
|
||
}
|
||
})
|
||
}, ms);
|
||
}
|
||
|
||
// 扫描设备
|
||
function connectFailAnalysis(deviceId) {
|
||
console.log("scanDevices ...")
|
||
let that = this
|
||
let found = false
|
||
let discovery = Promise.resolve().then(
|
||
() => {
|
||
return openBluetoothAdapter()
|
||
}
|
||
).then(
|
||
() => {
|
||
return startBluetoothDevicesDiscovery([])
|
||
// return startBluetoothDevicesDiscovery(["6E400001-B5A3-F393-E0A9-E50E24DCCA9E"])
|
||
}
|
||
).then(
|
||
() => {
|
||
let devicesList = []
|
||
return new Promise((resolve) => {
|
||
wx.onBluetoothDeviceFound(function(res) {
|
||
let name = res.devices[0].name
|
||
if (name != "") {
|
||
console.log("onBluetoothDeviceFound:", name, res);
|
||
devicesList.push(res.devices[0])
|
||
let foundDeviceId = res.devices[0].deviceId
|
||
console.log("found", foundDeviceId, deviceId)
|
||
// 尝试连接,读取等
|
||
if (foundDeviceId = deviceId) {
|
||
found = true
|
||
}
|
||
}
|
||
})
|
||
resolve()
|
||
})
|
||
}
|
||
)
|
||
|
||
let discoveryTimeout = new Promise(resolve => {
|
||
setTimeout(resolve, 10000);
|
||
}).then(res => {
|
||
console.log("discoveryTimeout")
|
||
return stopBluetoothDevicesDiscovery()
|
||
}).then(res => {
|
||
return new Promise(res => {
|
||
console.log("xxxx", found)
|
||
if (found) {
|
||
console.log("found", deviceId)
|
||
resolve(true)
|
||
} else {
|
||
console.log("not found", deviceId)
|
||
reject(false)
|
||
}
|
||
})
|
||
})
|
||
|
||
return Promise.race([discovery, discoveryTimeout])
|
||
}
|
||
|
||
module.exports = {
|
||
// 蓝牙适配器
|
||
closeBluetoothAdapter: closeBluetoothAdapter,
|
||
openBluetoothAdapter: openBluetoothAdapter,
|
||
onBluetoothAdapterStateChange: onBluetoothAdapterStateChange,
|
||
startBluetoothDevicesDiscovery: startBluetoothDevicesDiscovery,
|
||
stopBluetoothDevicesDiscovery: stopBluetoothDevicesDiscovery,
|
||
|
||
// BLE连接
|
||
closeBLEConnection: closeBLEConnection,
|
||
createBLEConnection: createBLEConnection,
|
||
onBLEConnectionStateChange: onBLEConnectionStateChange,
|
||
|
||
// BLE服务和特征值
|
||
getBLEDeviceServices: getBLEDeviceServices,
|
||
getBLEDeviceCharacteristics: getBLEDeviceCharacteristics,
|
||
notifyBLECharacteristicValueChanged: notifyBLECharacteristicValueChanged,
|
||
onBLECharacteristicValueChange: onBLECharacteristicValueChange,
|
||
|
||
// BLE读写
|
||
writeBLECharacteristicValue: writeBLECharacteristicValue,
|
||
readBLECharacteristicValue: readBLECharacteristicValue,
|
||
|
||
// wrapper
|
||
scanDevicesForSometime: scanDevicesForSometime,
|
||
searchDeviceName: searchDeviceName,
|
||
connectDevice: connectDevice,
|
||
searchAndConnect: searchAndConnect,
|
||
connectFailAnalysis: connectFailAnalysis,
|
||
} |