BLE.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682
  1. // 灵敏度
  2. const BLEDIS = 0.9;
  3. // 设备Id
  4. var deviceId = "";
  5. // 服务Id
  6. var serviceId = "";
  7. var notifyCharacteristicId = "";
  8. var writeCharacteristicId = "";
  9. var currentInstruction = "";
  10. var showToast = false;
  11. var bListenerUpdate = false;
  12. var bListenerJson = false;
  13. var bListenerMac = false;
  14. function initData() {
  15. // 设备Id
  16. this.deviceId = "";
  17. // 服务Id
  18. this.serviceId = "";
  19. this.notifyCharacteristicId = "";
  20. this.writeCharacteristicId = "";
  21. this.currentInstruction = "";
  22. this.showToast = false;
  23. this.bListenerUpdate = false;
  24. this.bListenerJson = false;
  25. this.bListenerMac = false;
  26. }
  27. function openBluetoothAdapter(context) {
  28. let {
  29. success,
  30. fail,
  31. complete
  32. } = context;
  33. //#ifdef H5
  34. console.warn('h5不加载蓝牙模块');
  35. return;
  36. //#endif
  37. let self = this;
  38. // 初始化蓝牙适配器
  39. uni.openBluetoothAdapter({
  40. success: res => {
  41. // console.log("初始化蓝牙成功:", res);
  42. if (success)
  43. success(res);
  44. },
  45. fail: res => {
  46. // console.error("openBluetoothAdapter == ", res);
  47. if (fail)
  48. fail(res);
  49. },
  50. complete: res => {
  51. // console.warn("************openBluetoothAdapter complete == ", res);
  52. if (complete)
  53. complete(res);
  54. }
  55. })
  56. }
  57. function onBLEConnectionStateChange() {
  58. //#ifdef H5
  59. console.warn('h5不加载蓝牙模块');
  60. return;
  61. //#endif
  62. uni.onBLEConnectionStateChange(res => {
  63. // 该方法回调中可以用于处理连接意外断开等异常情况
  64. console.log(`蓝牙连接状态 -------------------------->`, JSON.stringify(res));
  65. if (!res.connected) {
  66. // if (this.isStop) return;
  67. // console.log('断开低功耗蓝牙成功:');
  68. uni.$emit('callbackCloseBLE');
  69. //todo 在这里尝试重连
  70. //关闭连接
  71. // this.closeBluetoothAdapter();
  72. }
  73. });
  74. }
  75. /**
  76. * 连接低功耗蓝牙设备。
  77. * 若APP在之前已有搜索过某个蓝牙设备,并成功建立连接,可直接传入之前搜索获取的 deviceId 直接尝试连接该设备,无需进行搜索操作。
  78. */
  79. function createBLEConnection(item, success, fail) {
  80. let self = this;
  81. self.initData();
  82. self.deviceId = item.deviceId;
  83. let timeout = 10000;
  84. uni.createBLEConnection({
  85. // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
  86. deviceId: self.deviceId,
  87. timeout,
  88. success: res => {
  89. if (success) {
  90. success(res);
  91. }
  92. },
  93. fail: res => {
  94. if (fail)
  95. fail(res);
  96. }
  97. });
  98. }
  99. /**
  100. * 获取设备的服务ID
  101. */
  102. function getBLEDeviceServices(item, callback,getServiceList) {
  103. let serviceList = [];
  104. let self = this;
  105. // console.log("getBLEDeviceServices");
  106. uni.getBLEDeviceServices({
  107. deviceId: item.deviceId,
  108. success: res => {
  109. console.log("getBLEDeviceServices==", JSON.stringify(res), item);
  110. serviceList = res.services;
  111. if(serviceList.length == 0){
  112. uni.hideToast();
  113. uni.showModal({
  114. title:'失败',
  115. content:'获取服务失败,请尝试重新连接对应的模式。'
  116. })
  117. }
  118. if(getServiceList){
  119. getServiceList(serviceList);
  120. }
  121. for (let i = 0; i < serviceList.length; i++) {
  122. let service = serviceList[i];
  123. //比对service是否是F393服务
  124. // if (item.primaryUUID && service.uuid.indexOf(item.primaryUUID) != -1)
  125. if (service.uuid.toLocaleLowerCase() === item.PRIMARY_SERVICE.toLocaleLowerCase()) {
  126. self.serviceId = service.uuid;
  127. // console.log("设备的serviceId: " + self.serviceId);
  128. //开始获取指定服务的特征值
  129. self.getBLEDeviceCharacteristics(item, callback);
  130. break;
  131. }
  132. }
  133. },
  134. fail: failRes => {
  135. console.log('device services:', failRes.services)
  136. uni.showModal({
  137. title:'连接失败',
  138. content:'未获取到相应服务,请继续尝试连接设备。'
  139. })
  140. }
  141. });
  142. }
  143. /**
  144. * 获取指定服务的特征值
  145. */
  146. function getBLEDeviceCharacteristics(item, callback) {
  147. let deviceId = this.deviceId;
  148. let serviceId = this.serviceId;
  149. let characteristicsList = [];
  150. let self = this;
  151. uni.getBLEDeviceCharacteristics({
  152. deviceId,
  153. serviceId,
  154. success: res => {
  155. // console.log(JSON.stringify(res));
  156. // console.log("获取的" + serviceId + "服务的特征值:" + JSON.stringify(res.characteristics)+JSON.stringify(item));
  157. // console.log(res.characteristics);
  158. // console.log(item);
  159. // characteristicsList = res.characteristics;
  160. // for (let i = 0; i < characteristicsList.length; i++) {
  161. // let characteristic = characteristicsList[i];
  162. // //该特征值是否支持 notify或indicate 操作
  163. // if (characteristic.properties.notify || characteristic.properties.indicate) {
  164. // console.log("该特征值支持 notify 操作:" + characteristic.uuid);
  165. // self.notifyCharacteristicId = characteristic.uuid;
  166. // self.notifyBLECharacteristicValueChange(deviceId, serviceId);
  167. // }
  168. // if (characteristic.properties.write) {
  169. // console.log("该特征值支持 write 操作:" + characteristic.uuid);
  170. // self.writeCharacteristicId = characteristic.uuid;
  171. // // break;
  172. // }
  173. // }
  174. if (item.PRIMARY_NOTIFY != '') {
  175. self.notifyCharacteristicId = item.PRIMARY_NOTIFY;
  176. self.notifyBLECharacteristicValueChange(callback);
  177. }
  178. if (item.PRIMARY_WRITE != '') {
  179. self.writeCharacteristicId = item.PRIMARY_WRITE;
  180. }
  181. },
  182. fail: res => {
  183. console.log('device getBLEDeviceCharacteristics failed:', JSON.stringify(res))
  184. }
  185. })
  186. }
  187. //启用低功耗蓝牙设备特征值变化时的 notify 功能,订阅特征值。
  188. //注意:必须设备的特征值支持notify或者indicate才可以成功调用,具体参照 characteristic 的 properties 属性
  189. function notifyBLECharacteristicValueChange(callback) {
  190. var _self = this;
  191. // 启用notify功能
  192. // console.log("启用notify功能");
  193. uni.notifyBLECharacteristicValueChange({
  194. state: true,
  195. deviceId: _self.deviceId,
  196. serviceId: _self.serviceId,
  197. characteristicId: _self.notifyCharacteristicId,
  198. success: function(res) {
  199. _self.onBLECharacteristicValueChange(callback); //监听特征值变化
  200. },
  201. fail: function(res) {
  202. uni.showToast({
  203. title: 'notify启动失败',
  204. icon: "none",
  205. mask: true
  206. });
  207. }
  208. })
  209. }
  210. //监听低功耗蓝牙设备的特征值变化。必须先启用notify接口才能接收到设备推送的notification。
  211. function onBLECharacteristicValueChange(callback) {
  212. var self = this;
  213. console.log("onBLECharacteristicValueChange success");
  214. if (callback) {
  215. callback({
  216. notifyCharacteristicId: self.notifyCharacteristicId,
  217. writeCharacteristicId: self.writeCharacteristicId
  218. })
  219. }
  220. uni.onBLECharacteristicValueChange(function(res) {
  221. let box = {};
  222. var resValue = ab2hext(res.value); //16进制字符串
  223. // console.log(self.currentInstruction +","+ self.bListenerUpdate+","+self.bListenerJson);
  224. if (self.currentInstruction == "V" && self.showToast) {
  225. var resValueStr = hexToString(resValue);
  226. // console.log("v resValueStr:" + resValueStr);
  227. // uni.showToast({
  228. // title: resValueStr.replace(/\{|}/g,'').trim(),
  229. // icon: 'none',
  230. // duration: 5000
  231. // })
  232. let _info = {
  233. type: 'version',
  234. instruction: 'V',
  235. value: resValueStr.replace(/\{|}/g, '').trim()
  236. }
  237. uni.$emit('listenerBLE', _info);
  238. self.currentInstruction = "";
  239. self.showToast = false;
  240. return;
  241. } else if (self.currentInstruction == 'M' && self.bListenerMac) {
  242. var resValueStr = hexToString(resValue);
  243. // console.log(resValue);
  244. // console.log("m:", resValueStr.replace(/\{|}/g,''));
  245. // uni.showToast({
  246. // title: resValueStr.replace(/\{|}/g,'').trim(),
  247. // icon: 'none',
  248. // duration: 5000
  249. // })
  250. let _info = {
  251. type: 'mac',
  252. instruction: 'M',
  253. value: resValueStr.replace(/\{|}/g, '').trim()
  254. }
  255. uni.$emit('listenerBLE', _info);
  256. self.currentInstruction = "";
  257. self.bListenerMac = false;
  258. return;
  259. }
  260. //步数数据解析
  261. if (self.bListenerJson) {
  262. var resValueStr = hexToString(resValue);
  263. // let _info = {
  264. // type: 'mac',
  265. // instruction: 'M',
  266. // value: resValueStr.replace(/\{|}/g, '').trim()
  267. // }
  268. console.log(resValueStr);
  269. box["Json"] = resValueStr;
  270. uni.$emit('updateBLEDeviceJson', box);
  271. }
  272. //原始数据解析
  273. if (self.bListenerUpdate) {
  274. // console.log("min:" + min+",s:"+s+",ms:"+ms);
  275. //R/L 左手还是右手
  276. let handle = resValue.substr(6, 2);
  277. let axStr = resValue.substr(8, 4);
  278. let ayStr = resValue.substr(12, 4);
  279. let azStr = resValue.substr(16, 4);
  280. // console.log("accStr:" + axStr+","+ayStr+","+azStr);
  281. // console.log("hex2int:" + hexToSignedInt(axStr)+","+hexToSignedInt(ayStr)+","+hexToSignedInt(azStr));
  282. //一个字节最大只能表示265 2个字节可以表示65536 然后你这边做一下转换 例如00 3E 3*16+14=62 ----->0.03 g
  283. //(真实值= AD值 /32768 * 16)
  284. //0.003 = 62 / 2768 * 16
  285. let ax = hexToSignedInt(axStr) / 32768 * 16;
  286. let ay = hexToSignedInt(ayStr) / 32768 * 16;
  287. let az = hexToSignedInt(azStr) / 32768 * 16;
  288. let gxStr = resValue.substr(20, 4);
  289. let gyStr = resValue.substr(24, 4);
  290. let gzStr = resValue.substr(28, 4);
  291. //角速度(AD值/32768 * 2000) 角速度单位是 °/s 范围是 正负2000
  292. let gx = hexToSignedInt(gxStr) / 32768 * 2000;
  293. let gy = hexToSignedInt(gyStr) / 32768 * 2000;
  294. let gz = hexToSignedInt(gzStr) / 32768 * 2000;
  295. //毫秒
  296. let ms = hex2int(resValue.substr(2, 4));
  297. //分
  298. let min = hex2int(resValue.substr(32, 2));
  299. //秒
  300. let s = hex2int(resValue.substr(34, 2));
  301. box["handle"] = hexToString(handle);
  302. box["acc"] = {
  303. ax,
  304. ay,
  305. az
  306. };
  307. box["gyro"] = {
  308. gx,
  309. gy,
  310. gz
  311. };
  312. box["min"] = min;
  313. box["s"] = s;
  314. box["ms"] = ms;
  315. // console.log("box:" + JSON.stringify(box));
  316. // if (typeof obj !== 'object' && obj) {
  317. // // console.log("连接错误")
  318. // //-1为错误
  319. // uni.$emit("callbackBLEState", {
  320. // state: -1,
  321. // msg: "设备数据错误"
  322. // });
  323. // return;
  324. // }
  325. // if (obj.hasOwnProperty("H")) {
  326. // console.log("HHHH:", obj.H);
  327. // }
  328. //更新数据给webview,在game-play-web||game-play-sub绑定
  329. //后面更新数据都用此接口
  330. uni.$emit('updateBLEDeviceData', box);
  331. }
  332. });
  333. }
  334. // ArrayBuffer转16进制字符串示例
  335. function ab2hext(buffer) {
  336. var hexArr = Array.prototype.map.call(
  337. new Uint8Array(buffer),
  338. function(bit) {
  339. return ('00' + bit.toString(16)).slice(-2)
  340. }
  341. )
  342. return hexArr.join('');
  343. }
  344. // 字符串转为ArrayBuffer对象,参数为字符串
  345. function str2ab(str) {
  346. var buf = new ArrayBuffer(str.length * 2); // 每个字符占用2个字节
  347. var bufView = new Uint16Array(buf);
  348. for (var i = 0, strLen = str.length; i < strLen; i++) {
  349. bufView[i] = str.charCodeAt(i);
  350. }
  351. return buf;
  352. }
  353. //16进制的字符串 转换为整数
  354. function hex2int(hex) {
  355. var len = hex.length,
  356. a = new Array(len),
  357. code;
  358. for (var i = 0; i < len; i++) {
  359. code = hex.charCodeAt(i);
  360. if (48 <= code && code < 58) {
  361. code -= 48;
  362. } else {
  363. code = (code & 0xdf) - 65 + 10;
  364. }
  365. a[i] = code;
  366. }
  367. return a.reduce(function(acc, c) {
  368. acc = 16 * acc + c;
  369. return acc;
  370. }, 0);
  371. }
  372. //16进制转有符号的整数
  373. function hexToSignedInt(hex) {
  374. let two = parseInt(hex, 16).toString(2);
  375. let bitNum = hex.length * 4;
  376. if (two.length < bitNum) {
  377. while (two.length < bitNum) {
  378. two = "0" + two;
  379. }
  380. }
  381. if (two.substring(0, 1) == "0") {
  382. two = parseInt(two, 2);
  383. return two;
  384. } else {
  385. let two_unsign = "";
  386. two = parseInt(two, 2) - 1;
  387. two = two.toString(2);
  388. two_unsign = two.substring(1, bitNum);
  389. two_unsign = two_unsign.replace(/0/g, "z");
  390. two_unsign = two_unsign.replace(/1/g, "0");
  391. two_unsign = two_unsign.replace(/z/g, "1");
  392. two = parseInt(-two_unsign, 2);
  393. return two;
  394. }
  395. }
  396. //十六进制转ASCII码
  397. function hexCharCodeToStr(hexCharCodeStr) {
  398. var trimedStr = hexCharCodeStr.trim();
  399. var rawStr = trimedStr.substr(0, 2).toLowerCase() === "0x" ? trimedStr.substr(2) : trimedStr;
  400. var len = rawStr.length;
  401. if (len % 2 !== 0) {
  402. alert("存在非法字符!");
  403. return "";
  404. }
  405. var curCharCode;
  406. var resultStr = [];
  407. for (var i = 0; i < len; i = i + 2) {
  408. curCharCode = parseInt(rawStr.substr(i, 2), 16);
  409. resultStr.push(String.fromCharCode(curCharCode));
  410. }
  411. return resultStr.join("");
  412. }
  413. //ASCII码转16进制
  414. function strToHexCharCode(str) {
  415. if (str === "") {
  416. return "";
  417. } else {
  418. var hexCharCode = [];
  419. hexCharCode.push("0x");
  420. for (var i = 0; i < str.length; i++) {
  421. hexCharCode.push((str.charCodeAt(i)).toString(16));
  422. }
  423. return hexCharCode.join("");
  424. }
  425. }
  426. //16进制转字符串
  427. function hexToString(str) {
  428. var trimedStr = str.trim();
  429. var rawStr =
  430. trimedStr.substr(0, 2).toLowerCase() === "0x" ?
  431. trimedStr.substr(2) :
  432. trimedStr;
  433. var len = rawStr.length;
  434. if (len % 2 !== 0) {
  435. // alert("Illegal Format ASCII Code!");
  436. return "";
  437. }
  438. var curCharCode;
  439. var resultStr = [];
  440. for (var i = 0; i < len; i = i + 2) {
  441. curCharCode = parseInt(rawStr.substr(i, 2), 16); // ASCII Code Value
  442. resultStr.push(String.fromCharCode(curCharCode));
  443. }
  444. return resultStr.join("");
  445. }
  446. /**
  447. * 断开蓝牙模块连接
  448. */
  449. function closeBluetoothAdapter() {
  450. //#ifdef H5
  451. console.warn('h5不加载蓝牙模块');
  452. return;
  453. //#endif
  454. uni.closeBluetoothAdapter({
  455. success: res => {
  456. console.log('断开蓝牙模块成功');
  457. uni.showToast({
  458. icon: "none",
  459. title: "蓝牙已经断开!",
  460. mask: false,
  461. duration: 3000
  462. });
  463. }
  464. });
  465. }
  466. //向低功耗蓝牙设备特征值中写入二进制数据。注意:必须设备的特征值支持 write 才可以成功调用。
  467. function writeBLECharacteristicValue(value, retryCount, success, getFail) {
  468. var self = this;
  469. if (!value && value === '') {
  470. uni.showToast({
  471. title: "指令为空"
  472. })
  473. return;
  474. }
  475. //记录一下指令
  476. self.currentInstruction = value;
  477. //V 获取版本,M 获取mac 地址
  478. if (value == "V")
  479. self.showToast = true;
  480. if (value == "M") {
  481. self.showToast = true;
  482. self.bListenerMac = true;
  483. }
  484. if (value == "4") {
  485. self.bListenerUpdate = false;
  486. }
  487. if (value == "3") {
  488. if (self.bListenerUpdate) {
  489. uni.showToast({
  490. title: "原始数据已开启"
  491. })
  492. return;
  493. }
  494. self.bListenerUpdate = true;
  495. }
  496. if (value == "6") {
  497. self.bListenerJson = false;
  498. }
  499. if (value == "5") {
  500. if (self.bListenerJson) {
  501. uni.showToast({
  502. title: "步数数据已开启"
  503. })
  504. return;
  505. }
  506. self.bListenerJson = true;
  507. }
  508. console.log("currentInstruction:", self.currentInstruction, value, self.bListenerUpdate, self.bListenerMac);
  509. console.log("self.deviceId:", self.deviceId, self.serviceId, self.writeCharacteristicId);
  510. uni.writeBLECharacteristicValue({
  511. deviceId: self.deviceId,
  512. serviceId: self.serviceId,
  513. // 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取
  514. characteristicId: self.writeCharacteristicId,
  515. // 这里的value是ArrayBuffer类型
  516. value: str2ab(value),
  517. success:(res)=> {
  518. console.log('writeBLECharacteristicValue success', res.errMsg, value,retryCount);
  519. if (success) {
  520. success(res);
  521. }
  522. },
  523. fail: (fail) => {
  524. console.log(fail)
  525. //重新写入
  526. // uni.showModal({
  527. // title:"提示",
  528. // content:"蓝牙通信失败,是否重新执行此操作。",
  529. // success: (resValue) => {
  530. // if(resValue.confirm){
  531. // self.writeBLECharacteristicValue(value, success);
  532. // }
  533. // }
  534. // })
  535. if(retryCount>0){
  536. console.log("writeBLECharacteristicValue Rewrite ===================>"+value+" == "+retryCount);
  537. self.writeBLECharacteristicValue(value,retryCount,success,getFail);
  538. retryCount--;
  539. }
  540. if(getFail){
  541. getFail(fail);
  542. }
  543. },
  544. complete: (completeRes) => {
  545. console.log(completeRes)
  546. }
  547. })
  548. // uni.readBLECharacteristicValue({
  549. // deviceId:self.deviceId,
  550. // serviceId:self.serviceId,
  551. // characteristicId:self.notifyCharacteristicId,
  552. // success: (resValue) => {
  553. // console.log('read success!');
  554. // }
  555. // })
  556. }
  557. /**
  558. * 断开蓝牙连接
  559. */
  560. function closeBLEConnection(deviceId, success) {
  561. uni.closeBLEConnection({
  562. deviceId,
  563. success: res => {
  564. if (success)
  565. success();
  566. },
  567. fail: (fail) => {
  568. console.log("关闭蓝牙失败",fail);
  569. }
  570. });
  571. }
  572. function getRSSITransDistance(rssi) {
  573. return Math.pow(Math.E, (Math.abs(rssi) - 66.78) / 16.56);
  574. }
  575. /**
  576. * 获取在蓝牙模块生效期间所有已发现的蓝牙设备。包括已经和本机处于连接状态的设备。
  577. */
  578. function getBluetoothDevices() {
  579. uni.getBluetoothDevices({
  580. success(res) {
  581. console.log("getBluetoothDevices:", res)
  582. }
  583. })
  584. }
  585. export default {
  586. deviceId,
  587. serviceId,
  588. //蓝牙模块
  589. openBluetoothAdapter,
  590. closeBluetoothAdapter,
  591. onBLEConnectionStateChange,
  592. getBLEDeviceCharacteristics,
  593. notifyBLECharacteristicValueChange,
  594. onBLECharacteristicValueChange,
  595. //建立连接模块
  596. createBLEConnection,
  597. closeBLEConnection,
  598. getBLEDeviceServices,
  599. getRSSITransDistance,
  600. BLEDIS,
  601. //写入模块数据
  602. writeBLECharacteristicValue,
  603. //获取已经匹配的蓝牙
  604. getBluetoothDevices,
  605. //初始化this变量
  606. initData
  607. }