402 lines
9.8 KiB
JavaScript
402 lines
9.8 KiB
JavaScript
|
function BLEDevice(arg) {
|
|||
|
this.deviceId = "";
|
|||
|
this.serviceId = "";
|
|||
|
this.debug = arg ? (arg['debug'] || false) : false;
|
|||
|
this.connectTimeout = arg ? (arg['connectTimeout'] || 10000) : 10000;
|
|||
|
this.onBLECharacteristicValueChange = {}
|
|||
|
};
|
|||
|
|
|||
|
/**
|
|||
|
* 日志输出
|
|||
|
*
|
|||
|
* @param {msg} String
|
|||
|
* 输出的信息
|
|||
|
* @param {Array} args
|
|||
|
* 辅助输出信息
|
|||
|
*/
|
|||
|
BLEDevice.prototype.log = function (msg, ...args) {
|
|||
|
if (this.debug) {
|
|||
|
console.log(msg, ...args)
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
BLEDevice.prototype.setStateCallBack = function (cb) {
|
|||
|
wx.onBluetoothAdapterStateChange((res) => {
|
|||
|
cb(res);
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
/**
|
|||
|
* 初始化蓝牙模块
|
|||
|
*
|
|||
|
* @return
|
|||
|
* 返回蓝牙模块Promise
|
|||
|
*/
|
|||
|
BLEDevice.prototype.openBluetoothAdapter = function() {
|
|||
|
return new Promise((resolve, reject) => {
|
|||
|
wx.openBluetoothAdapter({
|
|||
|
success: (res) => {
|
|||
|
this.log("openBluetoothAdapter", res);
|
|||
|
resolve(res);
|
|||
|
},
|
|||
|
fail: (res) => {
|
|||
|
this.log("openBluetoothAdapter failed", res)
|
|||
|
reject(res);
|
|||
|
}
|
|||
|
})
|
|||
|
})
|
|||
|
};
|
|||
|
|
|||
|
/**
|
|||
|
* 关闭蓝牙模块
|
|||
|
*/
|
|||
|
BLEDevice.prototype.closeBluetoothAdapter = function() {
|
|||
|
wx.closeBluetoothAdapter();
|
|||
|
};
|
|||
|
|
|||
|
/**
|
|||
|
* 获取本机蓝牙适配器状态
|
|||
|
*/
|
|||
|
BLEDevice.prototype.getBluetoothAdapterState = function () {
|
|||
|
wx.getBluetoothAdapterState({
|
|||
|
success: (res) => {
|
|||
|
if (res.discovering) {
|
|||
|
this.onBluetoothDeviceFound()
|
|||
|
} else if (res.available) {
|
|||
|
this.startBluetoothDevicesDiscovery(state.servicesId, state.key);
|
|||
|
}
|
|||
|
}
|
|||
|
})
|
|||
|
};
|
|||
|
|
|||
|
/**
|
|||
|
* 开始搜寻附近的蓝牙外围设备
|
|||
|
*
|
|||
|
* @param {String} servicesId
|
|||
|
* 要搜索的蓝牙设备主服务的uuid列表
|
|||
|
* @param {Function} cb
|
|||
|
* 发现设备后的回调
|
|||
|
* 若发现成功,参数success为true,foundDevices:发现的设备列表
|
|||
|
* 若发现失败,参数success为false
|
|||
|
* @return
|
|||
|
* 返回搜索蓝牙设备Promise
|
|||
|
*/
|
|||
|
BLEDevice.prototype.startBluetoothDevicesDiscovery = function (servicesId, cb) {
|
|||
|
let foundDevices = [];
|
|||
|
wx.onBluetoothDeviceFound((res) => {
|
|||
|
res.devices.forEach(device => {
|
|||
|
if (!device.name && !device.localName) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
const idx = inArray(foundDevices, 'deviceId', device.deviceId)
|
|||
|
|
|||
|
if (idx === -1) {
|
|||
|
foundDevices.push(device)
|
|||
|
} else {
|
|||
|
foundDevices[idx] = device
|
|||
|
}
|
|||
|
this.log("onBluetoothDeviceFound", foundDevices);
|
|||
|
|
|||
|
cb({ success: true, foundDevices: foundDevices});
|
|||
|
})
|
|||
|
})
|
|||
|
|
|||
|
wx.startBluetoothDevicesDiscovery({
|
|||
|
services: servicesId,
|
|||
|
allowDuplicatesKey: true,
|
|||
|
success: (res) => {
|
|||
|
this.log("startBluetoothDevicesDiscovery", res);
|
|||
|
},
|
|||
|
fail: (res) => {
|
|||
|
cb({ success: false });
|
|||
|
}
|
|||
|
})
|
|||
|
};
|
|||
|
|
|||
|
/**
|
|||
|
* 停止搜寻附近的蓝牙外围设备
|
|||
|
*/
|
|||
|
BLEDevice.prototype.stopBluetoothDevicesDiscovery = function () {
|
|||
|
wx.stopBluetoothDevicesDiscovery();
|
|||
|
};
|
|||
|
|
|||
|
/**
|
|||
|
* 连接低功耗蓝牙设备
|
|||
|
*
|
|||
|
* @param {String} deviceId
|
|||
|
* 连接设备的ID
|
|||
|
*
|
|||
|
* @return
|
|||
|
* 返回设备连接的Promise
|
|||
|
*/
|
|||
|
BLEDevice.prototype.createBLEConnection = function (deviceId) {
|
|||
|
this.deviceId = deviceId;
|
|||
|
return new Promise((resolve, reject) => {
|
|||
|
wx.createBLEConnection({
|
|||
|
deviceId: this.deviceId,
|
|||
|
success: (res) => {
|
|||
|
this.log("createBLEConnection", res)
|
|||
|
this.getBLEDeviceServices()
|
|||
|
.then(() => {
|
|||
|
resolve();
|
|||
|
}).catch((res) => {
|
|||
|
reject(res);
|
|||
|
this.closeBLEConnection();
|
|||
|
})
|
|||
|
},
|
|||
|
fail: (res) => {
|
|||
|
this.log("createBLEConnection failed", res)
|
|||
|
reject(res)
|
|||
|
}
|
|||
|
})
|
|||
|
this.stopBluetoothDevicesDiscovery();
|
|||
|
|
|||
|
setTimeout(() => {
|
|||
|
reject('Connect timeout');
|
|||
|
this.closeBLEConnection();
|
|||
|
}, this.connectTimeout);
|
|||
|
})
|
|||
|
};
|
|||
|
|
|||
|
/**
|
|||
|
* 断开与低功耗蓝牙设备的连接
|
|||
|
*/
|
|||
|
BLEDevice.prototype.closeBLEConnection = function () {
|
|||
|
wx.closeBLEConnection({
|
|||
|
deviceId: this.deviceId,
|
|||
|
})
|
|||
|
};
|
|||
|
|
|||
|
/**
|
|||
|
* 获取蓝牙设备的所有服务
|
|||
|
*
|
|||
|
* @param {String} findServicesUUID
|
|||
|
* 要查找的主服务uuid
|
|||
|
*
|
|||
|
* @param {Number} findServicesIndex
|
|||
|
* 开始查找的位置
|
|||
|
*
|
|||
|
* @return
|
|||
|
* 返回获取服务的Promise
|
|||
|
*/
|
|||
|
BLEDevice.prototype.getBLEDeviceServices = function () {
|
|||
|
return new Promise((resolve, reject) => {
|
|||
|
wx.getBLEDeviceServices({
|
|||
|
deviceId: this.deviceId,
|
|||
|
success: (res) => {
|
|||
|
let total = res.services.length
|
|||
|
for (let i = 0; i < res.services.length; i++) {
|
|||
|
this.log("getBLEDeviceServices", res.services[i]);
|
|||
|
this.getBLEDeviceCharacteristics(res.services[i].uuid)
|
|||
|
.then(() => {
|
|||
|
total --;
|
|||
|
if (total === 0) {
|
|||
|
resolve();
|
|||
|
}
|
|||
|
})
|
|||
|
.catch(res => {
|
|||
|
reject(res);
|
|||
|
})
|
|||
|
}
|
|||
|
},
|
|||
|
fail: (res) => {
|
|||
|
reject(res);
|
|||
|
}
|
|||
|
})
|
|||
|
})
|
|||
|
};
|
|||
|
|
|||
|
/**
|
|||
|
* 获取蓝牙设备某个服务中所有特征值
|
|||
|
*
|
|||
|
* @param {String} serviceId
|
|||
|
* 要查找特征的service uuid
|
|||
|
*
|
|||
|
* @return
|
|||
|
* 返回服务特征值的Promise
|
|||
|
*/
|
|||
|
BLEDevice.prototype.getBLEDeviceCharacteristics = function (serviceId) {
|
|||
|
|
|||
|
return new Promise((resolve, reject) => {
|
|||
|
wx.getBLEDeviceCharacteristics({
|
|||
|
deviceId: this.deviceId,
|
|||
|
serviceId: serviceId,
|
|||
|
success: (res) => {
|
|||
|
resolve(res);
|
|||
|
|
|||
|
for (let i = 0; i < res.characteristics.length; i++) {
|
|||
|
this.log("getBLEDeviceCharacteristics", i, res.characteristics[i]);
|
|||
|
let item = res.characteristics[i];
|
|||
|
if (item.properties.read) {
|
|||
|
this.log("可读:", res.characteristics[i]);
|
|||
|
}
|
|||
|
if (item.properties.write) {
|
|||
|
this.log("可写:", res.characteristics[i]);
|
|||
|
}
|
|||
|
if (item.properties.notify || item.properties.indicate) {
|
|||
|
this.log("可订阅", res.characteristics[i]);
|
|||
|
}
|
|||
|
}
|
|||
|
},
|
|||
|
fail: (res) => {
|
|||
|
reject(res);
|
|||
|
},
|
|||
|
complete: (res) => {
|
|||
|
}
|
|||
|
})
|
|||
|
})
|
|||
|
};
|
|||
|
|
|||
|
/**
|
|||
|
* 读取低功耗蓝牙设备的特征值的二进制数据值
|
|||
|
*
|
|||
|
* @param {String} serviceId
|
|||
|
* 蓝牙可订阅服务的uuid
|
|||
|
*
|
|||
|
* @param {String} readCharacteristicId
|
|||
|
* 蓝牙可读特征值的uuid
|
|||
|
*
|
|||
|
* @param {Function} callback
|
|||
|
* 特征变化的回调函数
|
|||
|
*/
|
|||
|
BLEDevice.prototype.readBLECharacteristicValue = function (serviceId, readCharacteristicId, callback) {
|
|||
|
wx.onBLECharacteristicValueChange((characteristic) => {
|
|||
|
this.log("onBLECharacteristicValueChange", characteristic.value)
|
|||
|
let cb = this.onBLECharacteristicValueChange[readCharacteristicId]
|
|||
|
if (cb) {
|
|||
|
cb(characteristic)
|
|||
|
}
|
|||
|
})
|
|||
|
|
|||
|
return new Promise((resolve, reject) => {
|
|||
|
wx.readBLECharacteristicValue({
|
|||
|
deviceId: this.deviceId,
|
|||
|
serviceId: serviceId,
|
|||
|
characteristicId: readCharacteristicId,
|
|||
|
success: (res) => {
|
|||
|
resolve(res)
|
|||
|
this.log('readBLECharacteristicValue', res);
|
|||
|
this.onBLECharacteristicValueChange[readCharacteristicId] = callback;
|
|||
|
},
|
|||
|
fail: (res) => {
|
|||
|
reject(res)
|
|||
|
}
|
|||
|
})
|
|||
|
})
|
|||
|
};
|
|||
|
|
|||
|
/**
|
|||
|
* 向低功耗蓝牙设备特征值中写入二进制数据
|
|||
|
*
|
|||
|
* @param {String} writeCharacteristicId
|
|||
|
* 蓝牙可写特征值的uuid
|
|||
|
*
|
|||
|
* @param {ArrayBuffer} buffer
|
|||
|
* 特征值对应的二进制值
|
|||
|
*/
|
|||
|
BLEDevice.prototype.writeBLECharacteristicValue = function (serviceId, writeCharacteristicId, buffer) {
|
|||
|
// // 向蓝牙设备发送一个0x00的16进制数据
|
|||
|
this.log("writeBLECharacteristicValue buffer", buffer);
|
|||
|
wx.writeBLECharacteristicValue({
|
|||
|
deviceId: this.deviceId,
|
|||
|
serviceId: serviceId,
|
|||
|
characteristicId: writeCharacteristicId,
|
|||
|
value: buffer,
|
|||
|
success: (res) => {
|
|||
|
this.log('writeBLECharacteristicValue', res);
|
|||
|
}
|
|||
|
})
|
|||
|
};
|
|||
|
|
|||
|
/**
|
|||
|
* 启用低功耗蓝牙设备特征值变化时的notify功能,订阅特征值
|
|||
|
*
|
|||
|
* @param {String} serviceId
|
|||
|
* 蓝牙可订阅服务的uuid
|
|||
|
*
|
|||
|
* @param {String} notifyCharacteristicId
|
|||
|
* 蓝牙可订阅特征值的uuid
|
|||
|
*
|
|||
|
* @param {Function} callback
|
|||
|
* 特征变化的回调函数
|
|||
|
*/
|
|||
|
BLEDevice.prototype.notifyBLECharacteristicValueChange = function (serviceId, notifyCharacteristicId, callback) {
|
|||
|
|
|||
|
wx.onBLECharacteristicValueChange((characteristic) => {
|
|||
|
this.log("onBLECharacteristicValueChange", characteristic.value)
|
|||
|
let cb = this.onBLECharacteristicValueChange[notifyCharacteristicId]
|
|||
|
if (cb) {
|
|||
|
cb(characteristic)
|
|||
|
}
|
|||
|
})
|
|||
|
return new Promise((reslove, reject) => {
|
|||
|
wx.notifyBLECharacteristicValueChange({
|
|||
|
deviceId: this.deviceId,
|
|||
|
serviceId: serviceId,
|
|||
|
characteristicId: notifyCharacteristicId,
|
|||
|
state: true,
|
|||
|
success: (res) => {
|
|||
|
reslove(res)
|
|||
|
this.log('notifyBLECharacteristicValueChange', res);
|
|||
|
this.onBLECharacteristicValueChange[notifyCharacteristicId] = callback
|
|||
|
},
|
|||
|
fail: (res) => {
|
|||
|
reject(res)
|
|||
|
}
|
|||
|
})
|
|||
|
})
|
|||
|
};
|
|||
|
|
|||
|
/**
|
|||
|
* 关闭低功耗蓝牙设备特征值变化时的notify功能,订阅特征值
|
|||
|
*
|
|||
|
* @param {String} serviceId
|
|||
|
* 蓝牙可订阅服务的uuid
|
|||
|
*
|
|||
|
* @param {String} notifyCharacteristicId
|
|||
|
* 蓝牙可订阅特征值的uuid
|
|||
|
*/
|
|||
|
BLEDevice.prototype.unNotifyBLECharacteristicValueChange = function (serviceId, notifyCharacteristicId) {
|
|||
|
return new Promise((resolve, reject) => {
|
|||
|
delete this.onBLECharacteristicValueChange[notifyCharacteristicId]
|
|||
|
|
|||
|
wx.notifyBLECharacteristicValueChange({
|
|||
|
deviceId: this.deviceId,
|
|||
|
serviceId: serviceId,
|
|||
|
characteristicId: notifyCharacteristicId,
|
|||
|
state: false,
|
|||
|
success: function (res) {
|
|||
|
resolve(res)
|
|||
|
},
|
|||
|
fail: function (res) {
|
|||
|
reject(res)
|
|||
|
}
|
|||
|
})
|
|||
|
})
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
const inArray = (arr, key, val) => {
|
|||
|
for (let i = 0; i < arr.length; i++) {
|
|||
|
if (arr[i][key] === val) {
|
|||
|
return i;
|
|||
|
}
|
|||
|
}
|
|||
|
return -1;
|
|||
|
};
|
|||
|
|
|||
|
/**
|
|||
|
* ArrayBuffer转16进度字符串示例
|
|||
|
*/
|
|||
|
const ab2hex = (buffer) => {
|
|||
|
var hexArr = Array.prototype.map.call(
|
|||
|
new Uint8Array(buffer),
|
|||
|
function (bit) {
|
|||
|
return ('00' + bit.toString(16)).slice(-2);
|
|||
|
}
|
|||
|
)
|
|||
|
return hexArr.join('');
|
|||
|
};
|
|||
|
|
|||
|
module.exports = BLEDevice
|