// 灵敏度 const BLEDIS = 0.9; // 设备Id var deviceId = ""; // 服务Id var serviceId = ""; var notifyCharacteristicId = ""; var writeCharacteristicId = ""; var currentInstruction = ""; var showToast = false; var bListenerUpdate = false; var bListenerJson = false; var bListenerMac = false; function initData() { // 设备Id this.deviceId = ""; // 服务Id this.serviceId = ""; this.notifyCharacteristicId = ""; this.writeCharacteristicId = ""; this.currentInstruction = ""; this.showToast = false; this.bListenerUpdate = false; this.bListenerJson = false; this.bListenerMac = false; } function openBluetoothAdapter(context) { let { success, fail, complete } = context; //#ifdef H5 console.warn('h5不加载蓝牙模块'); return; //#endif let self = this; // 初始化蓝牙适配器 uni.openBluetoothAdapter({ success: res => { // console.log("初始化蓝牙成功:", res); if (success) success(res); }, fail: res => { // console.error("openBluetoothAdapter == ", res); if (fail) fail(res); }, complete: res => { // console.warn("************openBluetoothAdapter complete == ", res); if (complete) complete(res); } }) } function onBLEConnectionStateChange() { //#ifdef H5 console.warn('h5不加载蓝牙模块'); return; //#endif uni.onBLEConnectionStateChange(res => { // 该方法回调中可以用于处理连接意外断开等异常情况 console.log(`蓝牙连接状态 -------------------------->`, JSON.stringify(res)); if (!res.connected) { // if (this.isStop) return; // console.log('断开低功耗蓝牙成功:'); uni.$emit('callbackCloseBLE'); //todo 在这里尝试重连 //关闭连接 // this.closeBluetoothAdapter(); } }); } /** * 连接低功耗蓝牙设备。 * 若APP在之前已有搜索过某个蓝牙设备,并成功建立连接,可直接传入之前搜索获取的 deviceId 直接尝试连接该设备,无需进行搜索操作。 */ function createBLEConnection(item, success, fail) { let self = this; self.initData(); self.deviceId = item.deviceId; let timeout = 10000; uni.createBLEConnection({ // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接 deviceId: self.deviceId, timeout, success: res => { if (success) { success(res); } }, fail: res => { if (fail) fail(res); } }); } /** * 获取设备的服务ID */ function getBLEDeviceServices(item, callback,getServiceList) { let serviceList = []; let self = this; // console.log("getBLEDeviceServices"); uni.getBLEDeviceServices({ deviceId: item.deviceId, success: res => { console.log("getBLEDeviceServices==", JSON.stringify(res), item); serviceList = res.services; if(serviceList.length == 0){ uni.hideToast(); uni.showModal({ title:'失败', content:'获取服务失败,请尝试重新连接对应的模式。' }) } if(getServiceList){ getServiceList(serviceList); } for (let i = 0; i < serviceList.length; i++) { let service = serviceList[i]; //比对service是否是F393服务 // if (item.primaryUUID && service.uuid.indexOf(item.primaryUUID) != -1) if (service.uuid.toLocaleLowerCase() === item.PRIMARY_SERVICE.toLocaleLowerCase()) { self.serviceId = service.uuid; // console.log("设备的serviceId: " + self.serviceId); //开始获取指定服务的特征值 self.getBLEDeviceCharacteristics(item, callback); break; } } }, fail: failRes => { console.log('device services:', failRes.services) uni.showModal({ title:'连接失败', content:'未获取到相应服务,请继续尝试连接设备。' }) } }); } /** * 获取指定服务的特征值 */ function getBLEDeviceCharacteristics(item, callback) { let deviceId = this.deviceId; let serviceId = this.serviceId; let characteristicsList = []; let self = this; uni.getBLEDeviceCharacteristics({ deviceId, serviceId, success: res => { // console.log(JSON.stringify(res)); // console.log("获取的" + serviceId + "服务的特征值:" + JSON.stringify(res.characteristics)+JSON.stringify(item)); // console.log(res.characteristics); // console.log(item); // characteristicsList = res.characteristics; // for (let i = 0; i < characteristicsList.length; i++) { // let characteristic = characteristicsList[i]; // //该特征值是否支持 notify或indicate 操作 // if (characteristic.properties.notify || characteristic.properties.indicate) { // console.log("该特征值支持 notify 操作:" + characteristic.uuid); // self.notifyCharacteristicId = characteristic.uuid; // self.notifyBLECharacteristicValueChange(deviceId, serviceId); // } // if (characteristic.properties.write) { // console.log("该特征值支持 write 操作:" + characteristic.uuid); // self.writeCharacteristicId = characteristic.uuid; // // break; // } // } if (item.PRIMARY_NOTIFY != '') { self.notifyCharacteristicId = item.PRIMARY_NOTIFY; self.notifyBLECharacteristicValueChange(callback); } if (item.PRIMARY_WRITE != '') { self.writeCharacteristicId = item.PRIMARY_WRITE; } }, fail: res => { console.log('device getBLEDeviceCharacteristics failed:', JSON.stringify(res)) } }) } //启用低功耗蓝牙设备特征值变化时的 notify 功能,订阅特征值。 //注意:必须设备的特征值支持notify或者indicate才可以成功调用,具体参照 characteristic 的 properties 属性 function notifyBLECharacteristicValueChange(callback) { var _self = this; // 启用notify功能 // console.log("启用notify功能"); uni.notifyBLECharacteristicValueChange({ state: true, deviceId: _self.deviceId, serviceId: _self.serviceId, characteristicId: _self.notifyCharacteristicId, success: function(res) { _self.onBLECharacteristicValueChange(callback); //监听特征值变化 }, fail: function(res) { uni.showToast({ title: 'notify启动失败', icon: "none", mask: true }); } }) } //监听低功耗蓝牙设备的特征值变化。必须先启用notify接口才能接收到设备推送的notification。 function onBLECharacteristicValueChange(callback) { var self = this; console.log("onBLECharacteristicValueChange success"); if (callback) { callback({ notifyCharacteristicId: self.notifyCharacteristicId, writeCharacteristicId: self.writeCharacteristicId }) } uni.onBLECharacteristicValueChange(function(res) { let box = {}; var resValue = ab2hext(res.value); //16进制字符串 // console.log(self.currentInstruction +","+ self.bListenerUpdate+","+self.bListenerJson); if (self.currentInstruction == "V" && self.showToast) { var resValueStr = hexToString(resValue); // console.log("v resValueStr:" + resValueStr); // uni.showToast({ // title: resValueStr.replace(/\{|}/g,'').trim(), // icon: 'none', // duration: 5000 // }) let _info = { type: 'version', instruction: 'V', value: resValueStr.replace(/\{|}/g, '').trim() } uni.$emit('listenerBLE', _info); self.currentInstruction = ""; self.showToast = false; return; } else if (self.currentInstruction == 'M' && self.bListenerMac) { var resValueStr = hexToString(resValue); // console.log(resValue); // console.log("m:", resValueStr.replace(/\{|}/g,'')); // uni.showToast({ // title: resValueStr.replace(/\{|}/g,'').trim(), // icon: 'none', // duration: 5000 // }) let _info = { type: 'mac', instruction: 'M', value: resValueStr.replace(/\{|}/g, '').trim() } uni.$emit('listenerBLE', _info); self.currentInstruction = ""; self.bListenerMac = false; return; } //步数数据解析 if (self.bListenerJson) { var resValueStr = hexToString(resValue); // let _info = { // type: 'mac', // instruction: 'M', // value: resValueStr.replace(/\{|}/g, '').trim() // } console.log(resValueStr); box["Json"] = resValueStr; uni.$emit('updateBLEDeviceJson', box); } //原始数据解析 if (self.bListenerUpdate) { // console.log("min:" + min+",s:"+s+",ms:"+ms); //R/L 左手还是右手 let handle = resValue.substr(6, 2); let axStr = resValue.substr(8, 4); let ayStr = resValue.substr(12, 4); let azStr = resValue.substr(16, 4); // console.log("accStr:" + axStr+","+ayStr+","+azStr); // console.log("hex2int:" + hexToSignedInt(axStr)+","+hexToSignedInt(ayStr)+","+hexToSignedInt(azStr)); //一个字节最大只能表示265 2个字节可以表示65536 然后你这边做一下转换 例如00 3E 3*16+14=62 ----->0.03 g //(真实值= AD值 /32768 * 16) //0.003 = 62 / 2768 * 16 let ax = hexToSignedInt(axStr) / 32768 * 16; let ay = hexToSignedInt(ayStr) / 32768 * 16; let az = hexToSignedInt(azStr) / 32768 * 16; let gxStr = resValue.substr(20, 4); let gyStr = resValue.substr(24, 4); let gzStr = resValue.substr(28, 4); //角速度(AD值/32768 * 2000) 角速度单位是 °/s 范围是 正负2000 let gx = hexToSignedInt(gxStr) / 32768 * 2000; let gy = hexToSignedInt(gyStr) / 32768 * 2000; let gz = hexToSignedInt(gzStr) / 32768 * 2000; //毫秒 let ms = hex2int(resValue.substr(2, 4)); //分 let min = hex2int(resValue.substr(32, 2)); //秒 let s = hex2int(resValue.substr(34, 2)); box["handle"] = hexToString(handle); box["acc"] = { ax, ay, az }; box["gyro"] = { gx, gy, gz }; box["min"] = min; box["s"] = s; box["ms"] = ms; // console.log("box:" + JSON.stringify(box)); // if (typeof obj !== 'object' && obj) { // // console.log("连接错误") // //-1为错误 // uni.$emit("callbackBLEState", { // state: -1, // msg: "设备数据错误" // }); // return; // } // if (obj.hasOwnProperty("H")) { // console.log("HHHH:", obj.H); // } //更新数据给webview,在game-play-web||game-play-sub绑定 //后面更新数据都用此接口 uni.$emit('updateBLEDeviceData', box); } }); } // ArrayBuffer转16进制字符串示例 function ab2hext(buffer) { var hexArr = Array.prototype.map.call( new Uint8Array(buffer), function(bit) { return ('00' + bit.toString(16)).slice(-2) } ) return hexArr.join(''); } // 字符串转为ArrayBuffer对象,参数为字符串 function str2ab(str) { var buf = new ArrayBuffer(str.length * 2); // 每个字符占用2个字节 var bufView = new Uint16Array(buf); for (var i = 0, strLen = str.length; i < strLen; i++) { bufView[i] = str.charCodeAt(i); } return buf; } //16进制的字符串 转换为整数 function hex2int(hex) { var len = hex.length, a = new Array(len), code; for (var i = 0; i < len; i++) { code = hex.charCodeAt(i); if (48 <= code && code < 58) { code -= 48; } else { code = (code & 0xdf) - 65 + 10; } a[i] = code; } return a.reduce(function(acc, c) { acc = 16 * acc + c; return acc; }, 0); } //16进制转有符号的整数 function hexToSignedInt(hex) { let two = parseInt(hex, 16).toString(2); let bitNum = hex.length * 4; if (two.length < bitNum) { while (two.length < bitNum) { two = "0" + two; } } if (two.substring(0, 1) == "0") { two = parseInt(two, 2); return two; } else { let two_unsign = ""; two = parseInt(two, 2) - 1; two = two.toString(2); two_unsign = two.substring(1, bitNum); two_unsign = two_unsign.replace(/0/g, "z"); two_unsign = two_unsign.replace(/1/g, "0"); two_unsign = two_unsign.replace(/z/g, "1"); two = parseInt(-two_unsign, 2); return two; } } //十六进制转ASCII码 function hexCharCodeToStr(hexCharCodeStr) { var trimedStr = hexCharCodeStr.trim(); var rawStr = trimedStr.substr(0, 2).toLowerCase() === "0x" ? trimedStr.substr(2) : trimedStr; var len = rawStr.length; if (len % 2 !== 0) { alert("存在非法字符!"); return ""; } var curCharCode; var resultStr = []; for (var i = 0; i < len; i = i + 2) { curCharCode = parseInt(rawStr.substr(i, 2), 16); resultStr.push(String.fromCharCode(curCharCode)); } return resultStr.join(""); } //ASCII码转16进制 function strToHexCharCode(str) { if (str === "") { return ""; } else { var hexCharCode = []; hexCharCode.push("0x"); for (var i = 0; i < str.length; i++) { hexCharCode.push((str.charCodeAt(i)).toString(16)); } return hexCharCode.join(""); } } //16进制转字符串 function hexToString(str) { var trimedStr = str.trim(); var rawStr = trimedStr.substr(0, 2).toLowerCase() === "0x" ? trimedStr.substr(2) : trimedStr; var len = rawStr.length; if (len % 2 !== 0) { // alert("Illegal Format ASCII Code!"); return ""; } var curCharCode; var resultStr = []; for (var i = 0; i < len; i = i + 2) { curCharCode = parseInt(rawStr.substr(i, 2), 16); // ASCII Code Value resultStr.push(String.fromCharCode(curCharCode)); } return resultStr.join(""); } /** * 断开蓝牙模块连接 */ function closeBluetoothAdapter() { //#ifdef H5 console.warn('h5不加载蓝牙模块'); return; //#endif uni.closeBluetoothAdapter({ success: res => { console.log('断开蓝牙模块成功'); uni.showToast({ icon: "none", title: "蓝牙已经断开!", mask: false, duration: 3000 }); } }); } //向低功耗蓝牙设备特征值中写入二进制数据。注意:必须设备的特征值支持 write 才可以成功调用。 function writeBLECharacteristicValue(value, retryCount, success, getFail) { var self = this; if (!value && value === '') { uni.showToast({ title: "指令为空" }) return; } //记录一下指令 self.currentInstruction = value; //V 获取版本,M 获取mac 地址 if (value == "V") self.showToast = true; if (value == "M") { self.showToast = true; self.bListenerMac = true; } if (value == "4") { self.bListenerUpdate = false; } if (value == "3") { if (self.bListenerUpdate) { uni.showToast({ title: "原始数据已开启" }) return; } self.bListenerUpdate = true; } if (value == "6") { self.bListenerJson = false; } if (value == "5") { if (self.bListenerJson) { uni.showToast({ title: "步数数据已开启" }) return; } self.bListenerJson = true; } console.log("currentInstruction:", self.currentInstruction, value, self.bListenerUpdate, self.bListenerMac); console.log("self.deviceId:", self.deviceId, self.serviceId, self.writeCharacteristicId); uni.writeBLECharacteristicValue({ deviceId: self.deviceId, serviceId: self.serviceId, // 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取 characteristicId: self.writeCharacteristicId, // 这里的value是ArrayBuffer类型 value: str2ab(value), success:(res)=> { console.log('writeBLECharacteristicValue success', res.errMsg, value,retryCount); if (success) { success(res); } }, fail: (fail) => { console.log(fail) //重新写入 // uni.showModal({ // title:"提示", // content:"蓝牙通信失败,是否重新执行此操作。", // success: (resValue) => { // if(resValue.confirm){ // self.writeBLECharacteristicValue(value, success); // } // } // }) if(retryCount>0){ console.log("writeBLECharacteristicValue Rewrite ===================>"+value+" == "+retryCount); self.writeBLECharacteristicValue(value,retryCount,success,getFail); retryCount--; } if(getFail){ getFail(fail); } }, complete: (completeRes) => { console.log(completeRes) } }) // uni.readBLECharacteristicValue({ // deviceId:self.deviceId, // serviceId:self.serviceId, // characteristicId:self.notifyCharacteristicId, // success: (resValue) => { // console.log('read success!'); // } // }) } /** * 断开蓝牙连接 */ function closeBLEConnection(deviceId, success) { uni.closeBLEConnection({ deviceId, success: res => { if (success) success(); }, fail: (fail) => { console.log("关闭蓝牙失败",fail); } }); } function getRSSITransDistance(rssi) { return Math.pow(Math.E, (Math.abs(rssi) - 66.78) / 16.56); } /** * 获取在蓝牙模块生效期间所有已发现的蓝牙设备。包括已经和本机处于连接状态的设备。 */ function getBluetoothDevices() { uni.getBluetoothDevices({ success(res) { console.log("getBluetoothDevices:", res) } }) } export default { deviceId, serviceId, //蓝牙模块 openBluetoothAdapter, closeBluetoothAdapter, onBLEConnectionStateChange, getBLEDeviceCharacteristics, notifyBLECharacteristicValueChange, onBLECharacteristicValueChange, //建立连接模块 createBLEConnection, closeBLEConnection, getBLEDeviceServices, getRSSITransDistance, BLEDIS, //写入模块数据 writeBLECharacteristicValue, //获取已经匹配的蓝牙 getBluetoothDevices, //初始化this变量 initData }