connection.js 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612
  1. import ble_store from "../store/bluetooth";
  2. import game_store from "../store/game";
  3. import Toast from "../../static/vant/toast/toast";
  4. import Notify from "../../static/vant/notify/notify";
  5. function getDeviceWriteInfo() {
  6. let deviceId = ble_store.getters.getDeviceId();
  7. let serviceId = ble_store.getters.getServiceId();
  8. let characteristicWriteId = ble_store.getters.getCharacteristicWriteId();
  9. let characteristicNotifyId = ble_store.getters.getCharacteristicNotifyId();
  10. return { deviceId, serviceId, characteristicWriteId, characteristicNotifyId};
  11. }
  12. /**
  13. * ArrayBuffer转16进度字符串示例
  14. * @param buffer
  15. * @returns {string}
  16. */
  17. function ab2hex(buffer) {
  18. const hexArr = Array.prototype.map.call(
  19. new Uint8Array(buffer), function (bit) {
  20. return ("00" + bit.toString(16)).slice(-2);
  21. }
  22. );
  23. return hexArr.join("");
  24. }
  25. /**
  26. * todo 解析hex
  27. * @param hexStr
  28. * @param count
  29. * @param len
  30. * @returns {string}
  31. */
  32. function doAnalysis(hexStr, count, len = 6) {
  33. let $result = "";
  34. //byte_count
  35. let $str = hexStr.substring(len);
  36. let $data = $str.substring(0, count * 2);
  37. for (let $i = 0; $i < $data.length; $i += 2) {
  38. let $code = parseInt($data.substring($i, $i+2), 16)
  39. $result += String.fromCharCode($code)
  40. }
  41. return $result;
  42. }
  43. export default {
  44. /**
  45. * 获取蓝牙设备服务
  46. * @param deviceId 脑机mac
  47. */
  48. getBLEDeviceServices(deviceId) {
  49. const that = this;
  50. wx.getBLEDeviceServices({
  51. deviceId,
  52. success: (res) => {
  53. console.log("获取蓝牙设备服务service:\n", JSON.stringify(res.services));
  54. for (let i = 0; i < res.services.length; i++) {
  55. console.log("第" + (i + 1) + "个UUID:" + res.services[i].uuid + "\n");
  56. if (res.services[i].uuid.indexOf('6E') !== -1 || res.services[i].uuid.indexOf('0000FFF0') !== -1) {
  57. // 获取蓝牙设备某个服务中所有特征值
  58. that.getBLEDeviceCharacteristics(deviceId, res.services[i].uuid);
  59. ble_store.setters.setServiceId(res.services[i].uuid);
  60. console.log("脑机deviceId(mac)", deviceId, "notifyServicesId:" + res.services[i].uuid);
  61. return;
  62. }
  63. }
  64. },
  65. fail() {
  66. let deviceId = wx.getStorageSync('deviceId');
  67. //断开蓝牙连接
  68. wx.closeBLEConnection({
  69. deviceId: deviceId
  70. });
  71. },
  72. });
  73. },
  74. /**
  75. * 获取蓝牙设备某个服务中所有特征值
  76. */
  77. getBLEDeviceCharacteristics(deviceId, serviceId) {
  78. const that = this;
  79. wx.getBLEDeviceCharacteristics({
  80. deviceId,
  81. serviceId,
  82. success: (res) => {
  83. console.log("获取服务", serviceId, "的特征值:\n", JSON.stringify(res));
  84. for (let i = 0; i < res.characteristics.length; i++) {
  85. let item = res.characteristics[i]
  86. if (item.properties.read) {
  87. console.log("第" + (i + 1) + ",该特征值可读:" + item.uuid);
  88. }
  89. if (item.properties.write) {
  90. console.log("第" + (i + 1) + ",该特征值可写:" + item.uuid);
  91. if(item.uuid.indexOf("0002") !== -1){
  92. ble_store.setters.setCharacteristicWriteId(item.uuid);
  93. //打开数据帧 (打开大包数据)
  94. that.SendOrder('ff');
  95. }
  96. }
  97. if (item.properties.notify || item.properties.indicate) {
  98. console.log("第" + (i + 1) + ",该特征值可监听:" + item.uuid);
  99. if(item.uuid.indexOf("0003") !== -1){
  100. ble_store.setters.setCharacteristicNotifyId(item.uuid);
  101. }
  102. // 启用蓝牙低功耗设备特征值变化时的 notify 功能,订阅特征。
  103. wx.notifyBLECharacteristicValueChange({
  104. deviceId: deviceId,
  105. serviceId: serviceId,
  106. characteristicId: item.uuid,
  107. state: true,
  108. success() {
  109. that.notifyDatas(null);
  110. console.log("init正在监听特征值:", item.uuid);
  111. }
  112. });
  113. }
  114. }
  115. },
  116. fail() {
  117. let deviceId = wx.getStorageSync('deviceId');
  118. //断开蓝牙连接
  119. wx.closeBLEConnection({
  120. deviceId: deviceId
  121. });
  122. },
  123. });
  124. },
  125. /**
  126. * 脑机连接教具
  127. */
  128. sendToyConnection(toyItem) {
  129. let that = this;
  130. if(toyItem && toyItem["hex"]) {
  131. let $hex = toyItem["hex"].substr(toyItem["hex"].length - 2, 2);
  132. if ($hex === "80") {
  133. wx.setStorageSync("report_mode", 2)
  134. } else {
  135. wx.setStorageSync("report_mode", 1)
  136. }
  137. console.log("连接教具(获取连接ID):", `03 00 ${$hex} 00 0A`, JSON.stringify(toyItem));
  138. // 连接教具: 03 00 ${$hex} 00 0a
  139. that.sendConnectOneToMore($hex);
  140. }
  141. },
  142. /**
  143. * 发送一对多连接
  144. * 连接教具(获取连接ID)
  145. */
  146. sendConnectOneToMore(id) {
  147. this.WriteBufferInBle(`03 00 ${id} 00 0A`);
  148. },
  149. /**
  150. * 发送一对一连接
  151. * 连接教具(使用获取的ID)
  152. */
  153. sendConnectOneToOne(id) {
  154. ble_store.setters.setCurrentToyId(id);
  155. this.WriteBufferInBle(`03 00 ${id} 01 0A`)
  156. },
  157. /**
  158. * 连接教具(使用下发的ID)
  159. */
  160. sendConnectOneToToy(id) {
  161. ble_store.setters.setCurrentToyId(id);
  162. this.WriteBufferInBle(`03 00 ${id} 02 0A`)
  163. },
  164. /**
  165. * todo 写入8位指令
  166. * @param id 末尾id
  167. * @constructor
  168. */
  169. SendOrder(id) {
  170. let $hexStr = `03 00 00 00 ${id}`;
  171. this.WriteBufferInBle($hexStr)
  172. },
  173. /**
  174. * 打开或关闭LED灯 AA CC 03 00 00 ctrl EC CKS
  175. * 00/01
  176. */
  177. SendLedOrder(id) {
  178. let $hexStr = `03 00 00 ${id} ec`;
  179. this.WriteBufferInBle($hexStr)
  180. },
  181. /**
  182. * 设置教具为无运动状态 AA CC 03 00 00 00 34 CKS
  183. * 00/01
  184. */
  185. SendMotionOrder(id) {
  186. let $hexStr = `03 00 00 ${id} 34`;
  187. this.WriteBufferInBle($hexStr)
  188. },
  189. /**
  190. * 自动发送RF重连
  191. * @param {Boolean} isOn 是否打开重连功能
  192. * @param timeOut 有效时间
  193. * AA CC 03 00 01 0a d0 21
  194. */
  195. sendAutoConnectRf(isOn, timeOut) {
  196. let onVal = isOn ? '01' : '00';
  197. let mTimeOut = timeOut.toString(16);
  198. if (mTimeOut.length === 1) {
  199. mTimeOut = `0${mTimeOut}`;
  200. }
  201. let $hexStr = `03 00 ${onVal} ${mTimeOut} d0`;
  202. this.WriteBufferInBle($hexStr);
  203. },
  204. /**
  205. * todo:开启脑控
  206. */
  207. sendControl() {
  208. let that = this;
  209. wx.showLoading({
  210. title: "正在启动"
  211. })
  212. setTimeout(()=>{
  213. that.SendOrder('07');
  214. },500)
  215. },
  216. /**
  217. * 关闭脑控
  218. */
  219. sendControlClose() {
  220. let that = this
  221. setTimeout(()=>{
  222. // 打开LED
  223. that.SendLedOrder("01");
  224. },500);
  225. setTimeout(()=>{
  226. // 关闭脑控
  227. that.SendOrder('09');
  228. ble_store.setters.setCurrentToyId("00");
  229. },1000);
  230. },
  231. /**
  232. * todo 写入buffer
  233. * @param $hex
  234. * @param $buffer_len
  235. * @constructor
  236. */
  237. WriteBufferInBle($hex, $buffer_len = 8) {
  238. let { deviceId, serviceId, characteristicWriteId } = getDeviceWriteInfo();
  239. if (deviceId && serviceId && characteristicWriteId) {
  240. let that = this;
  241. let $hex_header = "aa cc ";
  242. let $hex_sum = 0;
  243. let $hex_ary = $hex.split(" ");
  244. $hex_ary.forEach(($val, $index) => {
  245. $hex_sum += parseInt($val, 16);
  246. })
  247. let $checksum = ($hex_sum ^ parseInt("ffffffff", 16)) & parseInt("ff", 16);
  248. $hex = $hex_header + $hex + " " + ("00" + $checksum.toString(16)).substr(-2, 2);
  249. let buffer = new ArrayBuffer($buffer_len);
  250. let dataView = new DataView(buffer);
  251. $hex_ary = $hex.split(" ");
  252. $hex_ary.forEach(($val, $index) => {
  253. dataView.setUint8($index, parseInt($val, 16))
  254. })
  255. wx.writeBLECharacteristicValue({
  256. deviceId: deviceId,
  257. serviceId: serviceId,
  258. characteristicId: characteristicWriteId,
  259. value: buffer,
  260. success: function (res) {
  261. console.log($hex + "写入成功,时间:" + that.getNowTime())
  262. },
  263. fail: function (err) {
  264. console.log($hex + "写入失败", err);
  265. },
  266. });
  267. }
  268. },
  269. /**
  270. * 监听脑机数据
  271. * @param $this
  272. */
  273. notifyDatas($this) {
  274. const that = this;
  275. let deviceId = ble_store.getters.getDeviceId();
  276. // 监听蓝牙低功耗设备的特征值变化事件
  277. wx.onBLECharacteristicValueChange((characteristic) => {
  278. let hexStr = ab2hex(characteristic.value);
  279. console.log("监听脑机数据:", hexStr);
  280. // 处理打开脑控的应答
  281. if (hexStr.toUpperCase().indexOf("AADD07") >= 0) {
  282. ble_store.setters.setBluetoothLinkStatus(true)
  283. }
  284. // 收到发送UUID的应答立马发送连接教具的指令
  285. if (hexStr.toUpperCase().indexOf("AADD8E") >= 0) {
  286. let $currentToyId = ble_store.getters.getCurrentToyId();
  287. //发送教具连接(连接教具(使用下发的ID))
  288. that.sendConnectOneToToy($currentToyId)
  289. }
  290. if (hexStr.toUpperCase().indexOf("AAEE87") >= 0) {
  291. let $currentToyId = ble_store.getters.getCurrentToyId();
  292. //获取教具电量
  293. if ($currentToyId !== '80') {
  294. that.SendOrder('87')
  295. }
  296. }
  297. //let $game_status = game_store.getters.getGameStatus();
  298. //let $currentToyId = ble_store.getters.getCurrentToyId();
  299. // 连接页面
  300. if($this && $this.$options.name){
  301. console.log("当前页面名称:", $this?$this.$options.name:"");
  302. // 监听脑机电量
  303. if (hexStr.substring(0, 8) === "55550203") {
  304. let $power = parseInt(hexStr.substring(8, 10), 16);
  305. // let $voltage = parseInt(hexStr.substring(10, 12), 16);
  306. // // 监听是否插入USB
  307. // $this.hasUsb = $voltage.toString().substring(0, 1) === "1";
  308. if ($power) {
  309. $this.device_power = $power;
  310. }
  311. if ($power < 10 && $power > 0) {
  312. wx.showToast({ title: "脑机电量不足", icon: "none", duration: 2000});
  313. }
  314. }
  315. // 判断教具连接
  316. if (hexStr.toUpperCase().indexOf("AADD0A") >= 0) {
  317. //没连接上教具
  318. if (hexStr.toUpperCase().indexOf("AADD0A0000") >= 0) {
  319. $this.connect_toy = 3;
  320. return false;
  321. }
  322. let $baseIndex = hexStr.toUpperCase().indexOf("AADD0A");
  323. let $hex_index = hexStr.substring($baseIndex + 28, 30)
  324. let $toy_id = hexStr.substring($baseIndex + 8, 10)
  325. console.log("连接", $hex_index)
  326. console.log("教具", $toy_id)
  327. // 连接上教具
  328. if (new RegExp("00").test($hex_index) === true) {
  329. console.log("一对多")
  330. // 发送一对一连接 03 00 ${$toy_id} 01 0A
  331. that.sendConnectOneToOne($toy_id);
  332. ble_store.setters.setCurrentToyId($toy_id);
  333. // current_toy_id = $toy_id;
  334. }
  335. if (new RegExp("01").test($hex_index) === true) {
  336. console.log("一对一")
  337. wx.showToast({title: "已连接到" + $this.toy_item.name });
  338. //连接成功
  339. $this.connect_toy = 2;
  340. // 判断冥想模式不发送获取教具名字
  341. if ($toy_id !== "80") {
  342. // 获取一次教具名称
  343. setTimeout(() => {
  344. that.SendOrder('87')
  345. }, 3000)
  346. // 不断获取教具电量
  347. let toy_interval = setInterval(() => {
  348. let $game_status = game_store.getters.getGameStatus();
  349. if($game_status !== 1){
  350. clearInterval(toy_interval);
  351. } else{
  352. that.SendOrder('8a');
  353. }
  354. }, 10000);
  355. }
  356. }
  357. }
  358. // 获取教具名称
  359. if (hexStr.toUpperCase().indexOf("AADD87") >= 0) {
  360. let $currentToyId = ble_store.getters.getCurrentToyId();
  361. let $mHexStr = hexStr.substring(hexStr.toUpperCase().indexOf("AADD87"))
  362. let $datas = doAnalysis($mHexStr, 10);
  363. let $number = $datas.match(/\d+/)
  364. let toy_list_pre = {'00': "", '01': "SW", '02': "KL", '04': "SC", '05': "PP", '06': "SU", '09': "UF", '12': "JM", '13': "QM"}
  365. let $sn = toy_list_pre[$currentToyId] + $number;
  366. $this.toy_sn = $sn;
  367. //保存教具sn
  368. ble_store.setters.setToySn($sn);
  369. console.log("获取教具名称hexStr:",hexStr,",获取教具名称$sn",$sn);
  370. }
  371. // 获取教具电量
  372. if (hexStr.toUpperCase().indexOf("AADD8A") >= 0) {//接收教具电量状态
  373. let $_hexStr = hexStr.substring(hexStr.toUpperCase().indexOf("AADD8A") + 6);
  374. let $power = parseInt($_hexStr.substring(0, 2), 16)
  375. if ($power > 0) {
  376. $this.toy_power = $power
  377. }
  378. }
  379. // 教具断链(连续多次到教具的命令没有响应)
  380. if (hexStr.toUpperCase().indexOf("AAEE70") >= 0) {
  381. //connect_toy = false
  382. wx.showModal({
  383. content: "教具已断开",
  384. success(res) {
  385. if (res.confirm) {
  386. let $game_status = game_store.getters.getGameStatus();
  387. if ($game_status === 1) {
  388. $this.game_finished();
  389. }
  390. }
  391. }
  392. })
  393. }
  394. // 监听佩戴正确, 当s1为 00时 数据有效
  395. if (hexStr.substring(0, 6) === "555520") {
  396. $this.device_bg = (hexStr.substring(8, 10) === "00");
  397. console.log("监听佩戴正确:", hexStr.substring(0, 10), $this.device_bg);
  398. //游戏中模块
  399. let $game_status = game_store.getters.getGameStatus();
  400. if ($game_status === 1) {
  401. // 获取蓝牙低功耗设备(脑机)的信号强度
  402. wx.getBLEDeviceRSSI({
  403. deviceId: deviceId,
  404. success(res) {$this.RSSI = res.RSSI;}
  405. });
  406. // 分析实时数据
  407. if($this.$options.name === "StartGames" && $this.device_bg){
  408. $this.analysisGameData(hexStr);
  409. }
  410. }
  411. }
  412. //todo 接收脑机关机指令
  413. if (hexStr.toUpperCase().indexOf("AADD5A00000000A5") >= 0) {
  414. Notify({
  415. type: 'danger',
  416. duration: 0,
  417. message: '智脑机已关机,训练结束',
  418. onOpened() {
  419. setTimeout(() => {
  420. $this.game_finished();
  421. //clearInterval(control_close_intv)
  422. }, 2000)
  423. }
  424. });
  425. }
  426. }
  427. });
  428. },
  429. /**
  430. * todo:监听蓝牙连接状态
  431. * 监听蓝牙低功耗连接状态改变事件。包括开发者主动连接或断开连接,设备丢失,连接异常断开等
  432. */
  433. watchBLEstatus($this) {
  434. let that = this;
  435. // 微信自身监听低功耗蓝牙连接状态的改变事件
  436. wx.onBLEConnectionStateChange((res) => {
  437. // 该方法回调中可以用于处理连接意外断开等异常情况
  438. ble_store.setters.setBluetoothLinkStatus(res.connected);
  439. console.log(`${that.getNowTime()} 监听脑机是否断连:`, res.connected);
  440. if (res.connected === false) {
  441. //判断游戏是否游戏中
  442. let $game_status = game_store.getters.getGameStatus();
  443. if ($game_status === 1) {
  444. // $that.game_finished();
  445. Notify({
  446. type: 'danger',
  447. duration: 0,
  448. message: '智脑机已断开连接,正在尝试重新连接',
  449. onOpened() {
  450. that.reconnect(res.deviceId, $this)
  451. }
  452. });
  453. // control_close = true
  454. // connect_toy = false;
  455. } else {
  456. //关闭脑控
  457. // connect_toy = false;
  458. game_store.setters.setGameStatus(0);
  459. // 清空链接得设备 三值
  460. ble_store.setters.clearDeviceToy();
  461. $this.connect_toy = 0;
  462. //$this.connect_show = false;
  463. $this.device_bg = false;
  464. $this.device_status = 0;
  465. // $this.device = {}
  466. //$this.toy_UUID = "";
  467. $this.$forceUpdate();
  468. wx.closeBluetoothAdapter();
  469. // version = "";
  470. }
  471. }
  472. });
  473. },
  474. /**
  475. * todo:重新连接蓝牙
  476. */
  477. reconnect($deviceId, $this) {
  478. let that = this;
  479. let $deviceInfo = getServicesAndCharateristc();
  480. //重连的次数
  481. let $connect_count = 0;
  482. let $rec = setInterval(() => {
  483. let $game_status = game_store.getters.getGameStatus();
  484. if ($game_status === 1) {
  485. wx.createBLEConnection({
  486. deviceId: $deviceInfo.deviceId,
  487. success() {
  488. clearInterval($rec)
  489. Notify({type: 'success', message: `第${$connect_count}次重新连接成功`});
  490. LogInDb(`${that.getNowTime()} 第${$connect_count}次重新连接成功`)
  491. let $system = wx.getSystemInfoSync()
  492. if ($system.platform === 'ios') {
  493. that.getBLEDeviceServices($deviceInfo.deviceId)
  494. that.notifyDatas($this)
  495. } else {
  496. //that.openNotify($this)
  497. that.watchBLEstatus($this);
  498. }
  499. // that.sendToyPower();
  500. },
  501. fail(res) {
  502. Notify({type: 'danger', message: `第${$connect_count}次重新连接失败`});
  503. game_store.setters.setGameCloseStatus(1);
  504. }
  505. })
  506. if ($connect_count >= 3) {
  507. $this.game_finished();
  508. clearInterval($rec)
  509. }
  510. $connect_count += 1;
  511. } else {
  512. clearInterval($rec)
  513. }
  514. }, 7000)
  515. },
  516. /**
  517. * 关闭脑机蓝牙连接
  518. */
  519. closeConnection($this) {
  520. const that = this;
  521. // 关闭脑控
  522. $this.$connection.SendOrder("09");
  523. game_store.setters.setGameStatus(0);
  524. // todo 清空链接的设备
  525. $this.connect_toy = 0;
  526. //$this.connect_show = false;
  527. $this.device_bg = false;
  528. $this.connect_status = false;
  529. // 断开教具及蓝牙连接
  530. that.SendOrder("31");
  531. setTimeout(()=>{
  532. that.SendOrder("09");
  533. // 移除搜索到新设备的事件的全部监听函数
  534. wx.offBluetoothDeviceFound();
  535. // 停止搜寻附近的蓝牙外围设备。若已经找到需要的蓝牙设备并不需要继续搜索时,建议调用该接口停止蓝牙搜索。
  536. wx.stopBluetoothDevicesDiscovery();
  537. let deviceId = ble_store.getters.getDeviceId();
  538. //断开蓝牙连接
  539. wx.closeBLEConnection({
  540. deviceId: deviceId,
  541. success() {
  542. Toast.success({
  543. message: "断开蓝牙连接成功",
  544. });
  545. ble_store.setters.clearDeviceToy();
  546. wx.closeBluetoothAdapter();
  547. console.log("断开蓝牙连接成功", deviceId);
  548. },
  549. fail(err) {
  550. console.log("断开蓝牙连接"+deviceId+"失败error:", err);
  551. },
  552. complete() {
  553. //$this.device = {};
  554. //$this.toy_UUID = "";
  555. $this.$forceUpdate();
  556. },
  557. });
  558. },500);
  559. },
  560. /**
  561. * 获取当前时间
  562. */
  563. getNowTime() {
  564. const date = new Date();
  565. const year = date.getFullYear();
  566. const month = date.getMonth() + 1;
  567. const day = date.getDate();
  568. const hour = date.getHours();
  569. const minutes = date.getMinutes();
  570. const seconds = date.getSeconds();
  571. const millSeconds = date.getMilliseconds();
  572. return `${year}/${month}/${day} ${hour}:${minutes}:${seconds}.${millSeconds}`;
  573. },
  574. connectionError(errCode) {
  575. if (errCode === 10000) {
  576. return "未初始化蓝牙适配器";
  577. }
  578. if (errCode === 10001) {
  579. return "当前蓝牙适配器不可用";
  580. }
  581. if (errCode === 10002) {
  582. return "没有找到指定设备";
  583. }
  584. if (errCode === 10003) {
  585. return "连接失败";
  586. }
  587. if (errCode === 10006) {
  588. return "当前连接已断开";
  589. }
  590. return "未知连接错误:"+errCode;
  591. },
  592. };