import ble_store from "../store/bluetooth"; import game_store from "../store/game"; import Toast from "../../static/vant/toast/toast"; import Notify from "../../static/vant/notify/notify"; function getDeviceWriteInfo() { let deviceId = ble_store.getters.getDeviceId(); let serviceId = ble_store.getters.getServiceId(); let characteristicWriteId = ble_store.getters.getCharacteristicWriteId(); let characteristicNotifyId = ble_store.getters.getCharacteristicNotifyId(); return { deviceId, serviceId, characteristicWriteId, characteristicNotifyId}; } /** * ArrayBuffer转16进度字符串示例 * @param buffer * @returns {string} */ function ab2hex(buffer) { const hexArr = Array.prototype.map.call( new Uint8Array(buffer), function (bit) { return ("00" + bit.toString(16)).slice(-2); } ); return hexArr.join(""); } /** * todo 解析hex * @param hexStr * @param count * @param len * @returns {string} */ function doAnalysis(hexStr, count, len = 6) { let $result = ""; //byte_count let $str = hexStr.substring(len); let $data = $str.substring(0, count * 2); for (let $i = 0; $i < $data.length; $i += 2) { let $code = parseInt($data.substring($i, $i+2), 16) $result += String.fromCharCode($code) } return $result; } export default { /** * 获取蓝牙设备服务 * @param deviceId 脑机mac */ getBLEDeviceServices(deviceId) { const that = this; wx.getBLEDeviceServices({ deviceId, success: (res) => { console.log("获取蓝牙设备服务service:\n", JSON.stringify(res.services)); for (let i = 0; i < res.services.length; i++) { console.log("第" + (i + 1) + "个UUID:" + res.services[i].uuid + "\n"); if (res.services[i].uuid.indexOf('6E') !== -1 || res.services[i].uuid.indexOf('0000FFF0') !== -1) { // 获取蓝牙设备某个服务中所有特征值 that.getBLEDeviceCharacteristics(deviceId, res.services[i].uuid); ble_store.setters.setServiceId(res.services[i].uuid); console.log("脑机deviceId(mac)", deviceId, "notifyServicesId:" + res.services[i].uuid); return; } } }, fail() { let deviceId = wx.getStorageSync('deviceId'); //断开蓝牙连接 wx.closeBLEConnection({ deviceId: deviceId }); }, }); }, /** * 获取蓝牙设备某个服务中所有特征值 */ getBLEDeviceCharacteristics(deviceId, serviceId) { const that = this; wx.getBLEDeviceCharacteristics({ deviceId, serviceId, success: (res) => { console.log("获取服务", serviceId, "的特征值:\n", JSON.stringify(res)); for (let i = 0; i < res.characteristics.length; i++) { let item = res.characteristics[i] if (item.properties.read) { console.log("第" + (i + 1) + ",该特征值可读:" + item.uuid); } if (item.properties.write) { console.log("第" + (i + 1) + ",该特征值可写:" + item.uuid); if(item.uuid.indexOf("0002") !== -1){ ble_store.setters.setCharacteristicWriteId(item.uuid); //打开数据帧 (打开大包数据) that.SendOrder('ff'); } } if (item.properties.notify || item.properties.indicate) { console.log("第" + (i + 1) + ",该特征值可监听:" + item.uuid); if(item.uuid.indexOf("0003") !== -1){ ble_store.setters.setCharacteristicNotifyId(item.uuid); } // 启用蓝牙低功耗设备特征值变化时的 notify 功能,订阅特征。 wx.notifyBLECharacteristicValueChange({ deviceId: deviceId, serviceId: serviceId, characteristicId: item.uuid, state: true, success() { that.notifyDatas(null); console.log("init正在监听特征值:", item.uuid); } }); } } }, fail() { let deviceId = wx.getStorageSync('deviceId'); //断开蓝牙连接 wx.closeBLEConnection({ deviceId: deviceId }); }, }); }, /** * 脑机连接教具 */ sendToyConnection(toyItem) { let that = this; if(toyItem && toyItem["hex"]) { let $hex = toyItem["hex"].substr(toyItem["hex"].length - 2, 2); if ($hex === "80") { wx.setStorageSync("report_mode", 2) } else { wx.setStorageSync("report_mode", 1) } console.log("连接教具(获取连接ID):", `03 00 ${$hex} 00 0A`, JSON.stringify(toyItem)); // 连接教具: 03 00 ${$hex} 00 0a that.sendConnectOneToMore($hex); } }, /** * 发送一对多连接 * 连接教具(获取连接ID) */ sendConnectOneToMore(id) { this.WriteBufferInBle(`03 00 ${id} 00 0A`); }, /** * 发送一对一连接 * 连接教具(使用获取的ID) */ sendConnectOneToOne(id) { ble_store.setters.setCurrentToyId(id); this.WriteBufferInBle(`03 00 ${id} 01 0A`) }, /** * 连接教具(使用下发的ID) */ sendConnectOneToToy(id) { ble_store.setters.setCurrentToyId(id); this.WriteBufferInBle(`03 00 ${id} 02 0A`) }, /** * todo 写入8位指令 * @param id 末尾id * @constructor */ SendOrder(id) { let $hexStr = `03 00 00 00 ${id}`; this.WriteBufferInBle($hexStr) }, /** * 打开或关闭LED灯 AA CC 03 00 00 ctrl EC CKS * 00/01 */ SendLedOrder(id) { let $hexStr = `03 00 00 ${id} ec`; this.WriteBufferInBle($hexStr) }, /** * 设置教具为无运动状态 AA CC 03 00 00 00 34 CKS * 00/01 */ SendMotionOrder(id) { let $hexStr = `03 00 00 ${id} 34`; this.WriteBufferInBle($hexStr) }, /** * 自动发送RF重连 * @param {Boolean} isOn 是否打开重连功能 * @param timeOut 有效时间 * AA CC 03 00 01 0a d0 21 */ sendAutoConnectRf(isOn, timeOut) { let onVal = isOn ? '01' : '00'; let mTimeOut = timeOut.toString(16); if (mTimeOut.length === 1) { mTimeOut = `0${mTimeOut}`; } let $hexStr = `03 00 ${onVal} ${mTimeOut} d0`; this.WriteBufferInBle($hexStr); }, /** * todo:开启脑控 */ sendControl() { let that = this; wx.showLoading({ title: "正在启动" }) setTimeout(()=>{ that.SendOrder('07'); },500) }, /** * 关闭脑控 */ sendControlClose() { let that = this setTimeout(()=>{ // 打开LED that.SendLedOrder("01"); },500); setTimeout(()=>{ // 关闭脑控 that.SendOrder('09'); ble_store.setters.setCurrentToyId("00"); },1000); }, /** * todo 写入buffer * @param $hex * @param $buffer_len * @constructor */ WriteBufferInBle($hex, $buffer_len = 8) { let { deviceId, serviceId, characteristicWriteId } = getDeviceWriteInfo(); if (deviceId && serviceId && characteristicWriteId) { let that = this; let $hex_header = "aa cc "; let $hex_sum = 0; let $hex_ary = $hex.split(" "); $hex_ary.forEach(($val, $index) => { $hex_sum += parseInt($val, 16); }) let $checksum = ($hex_sum ^ parseInt("ffffffff", 16)) & parseInt("ff", 16); $hex = $hex_header + $hex + " " + ("00" + $checksum.toString(16)).substr(-2, 2); let buffer = new ArrayBuffer($buffer_len); let dataView = new DataView(buffer); $hex_ary = $hex.split(" "); $hex_ary.forEach(($val, $index) => { dataView.setUint8($index, parseInt($val, 16)) }) wx.writeBLECharacteristicValue({ deviceId: deviceId, serviceId: serviceId, characteristicId: characteristicWriteId, value: buffer, success: function (res) { console.log($hex + "写入成功,时间:" + that.getNowTime()) }, fail: function (err) { console.log($hex + "写入失败", err); }, }); } }, /** * 监听脑机数据 * @param $this */ notifyDatas($this) { const that = this; let deviceId = ble_store.getters.getDeviceId(); // 监听蓝牙低功耗设备的特征值变化事件 wx.onBLECharacteristicValueChange((characteristic) => { let hexStr = ab2hex(characteristic.value); console.log("监听脑机数据:", hexStr); // 处理打开脑控的应答 if (hexStr.toUpperCase().indexOf("AADD07") >= 0) { ble_store.setters.setBluetoothLinkStatus(true) } // 收到发送UUID的应答立马发送连接教具的指令 if (hexStr.toUpperCase().indexOf("AADD8E") >= 0) { let $currentToyId = ble_store.getters.getCurrentToyId(); //发送教具连接(连接教具(使用下发的ID)) that.sendConnectOneToToy($currentToyId) } if (hexStr.toUpperCase().indexOf("AAEE87") >= 0) { let $currentToyId = ble_store.getters.getCurrentToyId(); //获取教具电量 if ($currentToyId !== '80') { that.SendOrder('87') } } //let $game_status = game_store.getters.getGameStatus(); //let $currentToyId = ble_store.getters.getCurrentToyId(); // 连接页面 if($this && $this.$options.name){ console.log("当前页面名称:", $this?$this.$options.name:""); // 监听脑机电量 if (hexStr.substring(0, 8) === "55550203") { let $power = parseInt(hexStr.substring(8, 10), 16); // let $voltage = parseInt(hexStr.substring(10, 12), 16); // // 监听是否插入USB // $this.hasUsb = $voltage.toString().substring(0, 1) === "1"; if ($power) { $this.device_power = $power; } if ($power < 10 && $power > 0) { wx.showToast({ title: "脑机电量不足", icon: "none", duration: 2000}); } } // 判断教具连接 if (hexStr.toUpperCase().indexOf("AADD0A") >= 0) { //没连接上教具 if (hexStr.toUpperCase().indexOf("AADD0A0000") >= 0) { $this.connect_toy = 3; return false; } let $baseIndex = hexStr.toUpperCase().indexOf("AADD0A"); let $hex_index = hexStr.substring($baseIndex + 28, 30) let $toy_id = hexStr.substring($baseIndex + 8, 10) console.log("连接", $hex_index) console.log("教具", $toy_id) // 连接上教具 if (new RegExp("00").test($hex_index) === true) { console.log("一对多") // 发送一对一连接 03 00 ${$toy_id} 01 0A that.sendConnectOneToOne($toy_id); ble_store.setters.setCurrentToyId($toy_id); // current_toy_id = $toy_id; } if (new RegExp("01").test($hex_index) === true) { console.log("一对一") wx.showToast({title: "已连接到" + $this.toy_item.name }); //连接成功 $this.connect_toy = 2; // 判断冥想模式不发送获取教具名字 if ($toy_id !== "80") { // 获取一次教具名称 setTimeout(() => { that.SendOrder('87') }, 3000) // 不断获取教具电量 let toy_interval = setInterval(() => { let $game_status = game_store.getters.getGameStatus(); if($game_status !== 1){ clearInterval(toy_interval); } else{ that.SendOrder('8a'); } }, 10000); } } } // 获取教具名称 if (hexStr.toUpperCase().indexOf("AADD87") >= 0) { let $currentToyId = ble_store.getters.getCurrentToyId(); let $mHexStr = hexStr.substring(hexStr.toUpperCase().indexOf("AADD87")) let $datas = doAnalysis($mHexStr, 10); let $number = $datas.match(/\d+/) let toy_list_pre = {'00': "", '01': "SW", '02': "KL", '04': "SC", '05': "PP", '06': "SU", '09': "UF", '12': "JM", '13': "QM"} let $sn = toy_list_pre[$currentToyId] + $number; $this.toy_sn = $sn; //保存教具sn ble_store.setters.setToySn($sn); console.log("获取教具名称hexStr:",hexStr,",获取教具名称$sn",$sn); } // 获取教具电量 if (hexStr.toUpperCase().indexOf("AADD8A") >= 0) {//接收教具电量状态 let $_hexStr = hexStr.substring(hexStr.toUpperCase().indexOf("AADD8A") + 6); let $power = parseInt($_hexStr.substring(0, 2), 16) if ($power > 0) { $this.toy_power = $power } } // 教具断链(连续多次到教具的命令没有响应) if (hexStr.toUpperCase().indexOf("AAEE70") >= 0) { //connect_toy = false wx.showModal({ content: "教具已断开", success(res) { if (res.confirm) { let $game_status = game_store.getters.getGameStatus(); if ($game_status === 1) { $this.game_finished(); } } } }) } // 监听佩戴正确, 当s1为 00时 数据有效 if (hexStr.substring(0, 6) === "555520") { $this.device_bg = (hexStr.substring(8, 10) === "00"); console.log("监听佩戴正确:", hexStr.substring(0, 10), $this.device_bg); //游戏中模块 let $game_status = game_store.getters.getGameStatus(); if ($game_status === 1) { // 获取蓝牙低功耗设备(脑机)的信号强度 wx.getBLEDeviceRSSI({ deviceId: deviceId, success(res) {$this.RSSI = res.RSSI;} }); // 分析实时数据 if($this.$options.name === "StartGames" && $this.device_bg){ $this.analysisGameData(hexStr); } } } //todo 接收脑机关机指令 if (hexStr.toUpperCase().indexOf("AADD5A00000000A5") >= 0) { Notify({ type: 'danger', duration: 0, message: '智脑机已关机,训练结束', onOpened() { setTimeout(() => { $this.game_finished(); //clearInterval(control_close_intv) }, 2000) } }); } } }); }, /** * todo:监听蓝牙连接状态 * 监听蓝牙低功耗连接状态改变事件。包括开发者主动连接或断开连接,设备丢失,连接异常断开等 */ watchBLEstatus($this) { let that = this; // 微信自身监听低功耗蓝牙连接状态的改变事件 wx.onBLEConnectionStateChange((res) => { // 该方法回调中可以用于处理连接意外断开等异常情况 ble_store.setters.setBluetoothLinkStatus(res.connected); console.log(`${that.getNowTime()} 监听脑机是否断连:`, res.connected); if (res.connected === false) { //判断游戏是否游戏中 let $game_status = game_store.getters.getGameStatus(); if ($game_status === 1) { // $that.game_finished(); Notify({ type: 'danger', duration: 0, message: '智脑机已断开连接,正在尝试重新连接', onOpened() { that.reconnect(res.deviceId, $this) } }); // control_close = true // connect_toy = false; } else { //关闭脑控 // connect_toy = false; game_store.setters.setGameStatus(0); // 清空链接得设备 三值 ble_store.setters.clearDeviceToy(); $this.connect_toy = 0; //$this.connect_show = false; $this.device_bg = false; $this.device_status = 0; // $this.device = {} //$this.toy_UUID = ""; $this.$forceUpdate(); wx.closeBluetoothAdapter(); // version = ""; } } }); }, /** * todo:重新连接蓝牙 */ reconnect($deviceId, $this) { let that = this; let $deviceInfo = getServicesAndCharateristc(); //重连的次数 let $connect_count = 0; let $rec = setInterval(() => { let $game_status = game_store.getters.getGameStatus(); if ($game_status === 1) { wx.createBLEConnection({ deviceId: $deviceInfo.deviceId, success() { clearInterval($rec) Notify({type: 'success', message: `第${$connect_count}次重新连接成功`}); LogInDb(`${that.getNowTime()} 第${$connect_count}次重新连接成功`) let $system = wx.getSystemInfoSync() if ($system.platform === 'ios') { that.getBLEDeviceServices($deviceInfo.deviceId) that.notifyDatas($this) } else { //that.openNotify($this) that.watchBLEstatus($this); } // that.sendToyPower(); }, fail(res) { Notify({type: 'danger', message: `第${$connect_count}次重新连接失败`}); game_store.setters.setGameCloseStatus(1); } }) if ($connect_count >= 3) { $this.game_finished(); clearInterval($rec) } $connect_count += 1; } else { clearInterval($rec) } }, 7000) }, /** * 关闭脑机蓝牙连接 */ closeConnection($this) { const that = this; // 关闭脑控 $this.$connection.SendOrder("09"); game_store.setters.setGameStatus(0); // todo 清空链接的设备 $this.connect_toy = 0; //$this.connect_show = false; $this.device_bg = false; $this.connect_status = false; // 断开教具及蓝牙连接 that.SendOrder("31"); setTimeout(()=>{ that.SendOrder("09"); // 移除搜索到新设备的事件的全部监听函数 wx.offBluetoothDeviceFound(); // 停止搜寻附近的蓝牙外围设备。若已经找到需要的蓝牙设备并不需要继续搜索时,建议调用该接口停止蓝牙搜索。 wx.stopBluetoothDevicesDiscovery(); let deviceId = ble_store.getters.getDeviceId(); //断开蓝牙连接 wx.closeBLEConnection({ deviceId: deviceId, success() { Toast.success({ message: "断开蓝牙连接成功", }); ble_store.setters.clearDeviceToy(); wx.closeBluetoothAdapter(); console.log("断开蓝牙连接成功", deviceId); }, fail(err) { console.log("断开蓝牙连接"+deviceId+"失败error:", err); }, complete() { //$this.device = {}; //$this.toy_UUID = ""; $this.$forceUpdate(); }, }); },500); }, /** * 获取当前时间 */ getNowTime() { const date = new Date(); const year = date.getFullYear(); const month = date.getMonth() + 1; const day = date.getDate(); const hour = date.getHours(); const minutes = date.getMinutes(); const seconds = date.getSeconds(); const millSeconds = date.getMilliseconds(); return `${year}/${month}/${day} ${hour}:${minutes}:${seconds}.${millSeconds}`; }, connectionError(errCode) { if (errCode === 10000) { return "未初始化蓝牙适配器"; } if (errCode === 10001) { return "当前蓝牙适配器不可用"; } if (errCode === 10002) { return "没有找到指定设备"; } if (errCode === 10003) { return "连接失败"; } if (errCode === 10006) { return "当前连接已断开"; } return "未知连接错误:"+errCode; }, };