ecBLEJD.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  1. const logEnable = false;
  2. let isAndroid = false;
  3. let ecBluetoothAdapterStateChangeCallback = () => { };
  4. let ecBLEConnectionStateChangeCallback = () => { };
  5. let ecDeviceId = '';
  6. let ecGattServerUUID = '';
  7. const ecGattServerUUIDOption1 = '0000FFF0-0000-1000-8000-00805F9B34FB';
  8. const ecGattServerUUIDOption2 = 'FFF0';
  9. let ecGattCharacteristicWriteUUID = '';
  10. const ecGattCharacteristicWriteUUIDOption1 = '0000FFF2-0000-1000-8000-00805F9B34FB';
  11. const ecGattCharacteristicWriteUUIDOption2 = 'FFF2';
  12. const log = data => {
  13. if (logEnable) {
  14. console.log('[eciot]:' + JSON.stringify(data));
  15. }
  16. };
  17. const onBluetoothAdapterStateChange = cb => {
  18. ecBluetoothAdapterStateChangeCallback = cb;
  19. };
  20. // const getSetting = () => {
  21. // return new Promise(function (resolve, reject) {
  22. // jd.getSetting({
  23. // success(res) {
  24. // log(res);
  25. // if (res.authSetting && res.authSetting['scope.bluetooth']) {
  26. // resolve({
  27. // ok: true,
  28. // errCode: 0,
  29. // errMsg: ''
  30. // });
  31. // } else {
  32. // resolve({
  33. // ok: false,
  34. // errCode: 30001,
  35. // errMsg: 'getSetting fail'
  36. // });
  37. // }
  38. // },
  39. // fail(res) {
  40. // log(res);
  41. // resolve({
  42. // ok: false,
  43. // errCode: res.errCode ? res.errCode : 30000,
  44. // errMsg: res.errMsg ? res.errMsg : 'getSetting fail'
  45. // });
  46. // }
  47. // });
  48. // });
  49. // };
  50. // const authorize = () => {
  51. // return new Promise(function (resolve, reject) {
  52. // jd.authorize({
  53. // scope: 'scope.bluetooth',
  54. // success(res) {
  55. // log(res);
  56. // resolve({
  57. // ok: true,
  58. // errCode: 0,
  59. // errMsg: ''
  60. // });
  61. // },
  62. // fail(res) {
  63. // log(res);
  64. // // {"errMsg":"authorize:fail:auth deny"}
  65. // resolve({
  66. // ok: false,
  67. // errCode: 30000,
  68. // errMsg: res.errMsg
  69. // });
  70. // }
  71. // });
  72. // });
  73. // };
  74. const _openBluetoothAdapter = () => {
  75. return new Promise(function (resolve, reject) {
  76. jd.openBluetoothAdapter({
  77. success(res) {
  78. log(res);
  79. // {errno: 0, errMsg: "openBluetoothAdapter:ok"}
  80. resolve({
  81. ok: true,
  82. errCode: 0,
  83. errMsg: ''
  84. });
  85. },
  86. fail(res) {
  87. log(res);
  88. resolve({
  89. ok: false,
  90. errCode: res.errCode ? res.errCode : 30000,
  91. errMsg: res.errMsg
  92. });
  93. }
  94. });
  95. });
  96. };
  97. const openBluetoothAdapter = async () => {
  98. // await _openBluetoothAdapter()
  99. const systemInfo = jd.getSystemInfoSync();
  100. log(systemInfo);
  101. if (systemInfo.platform.toLowerCase() === 'android') {
  102. isAndroid = true;
  103. }
  104. // if (!systemInfo.bluetoothEnabled) {
  105. // ecBluetoothAdapterStateChangeCallback({
  106. // ok: false,
  107. // errCode: 30001,
  108. // errMsg: '请打开系统蓝牙开关'
  109. // });
  110. // return;
  111. // }
  112. // if (isAndroid && !systemInfo.locationEnabled) {
  113. // ecBluetoothAdapterStateChangeCallback({
  114. // ok: false,
  115. // errCode: 30002,
  116. // errMsg: '请打开系统定位开关'
  117. // });
  118. // return;
  119. // }
  120. // if (isAndroid && !systemInfo.locationAuthorized) {
  121. // ecBluetoothAdapterStateChangeCallback({
  122. // ok: false,
  123. // errCode: 30003,
  124. // errMsg: '请打开微信定位权限,允许微信使用您的位置信息'
  125. // });
  126. // return;
  127. // }
  128. // const setting = await getSetting(); //小程序蓝牙权限
  129. // if (!setting.ok) {
  130. // const authRes = await authorize();
  131. // if (!authRes.ok) {
  132. // ecBluetoothAdapterStateChangeCallback({
  133. // ok: false,
  134. // errCode: 30004,
  135. // errMsg: '请打开小程序蓝牙开关,点击右上角三个点,然后点击设置'
  136. // });
  137. // return;
  138. // }
  139. // }
  140. jd.offBluetoothAdapterStateChange();
  141. jd.onBluetoothAdapterStateChange(res => {
  142. log(res); // {available: true, discovering: true}
  143. if (!res.available) {
  144. ecBluetoothAdapterStateChangeCallback({
  145. ok: false,
  146. errCode: 30005,
  147. errMsg: '蓝牙适配器不可用'
  148. });
  149. }
  150. });
  151. const openRes = await _openBluetoothAdapter();
  152. ecBluetoothAdapterStateChangeCallback(openRes);
  153. };
  154. const onBluetoothDeviceFound = cb => {
  155. jd.offBluetoothDeviceFound();
  156. jd.onBluetoothDeviceFound(res => {
  157. // log(res);
  158. const device = res.devices[0];
  159. const name = device.name ? device.name : device.localName;
  160. if (!name) {
  161. return;
  162. }
  163. let id = device.deviceId;
  164. let rssi = device.RSSI;
  165. cb({
  166. id,
  167. name,
  168. rssi
  169. });
  170. });
  171. };
  172. const startBluetoothDevicesDiscovery = () => {
  173. jd.startBluetoothDevicesDiscovery({
  174. //services: [ecServerId],
  175. allowDuplicatesKey: true,
  176. powerLevel: 'high',
  177. complete(res) {
  178. log(res);
  179. }
  180. });
  181. };
  182. const stopBluetoothDevicesDiscovery = () => {
  183. jd.stopBluetoothDevicesDiscovery({
  184. complete(res) {
  185. // {errno: 0, errMsg: "stopBluetoothDevicesDiscovery:ok", isDiscovering: false}
  186. log(res);
  187. }
  188. });
  189. };
  190. const onBLEConnectionStateChange = cb => {
  191. ecBLEConnectionStateChangeCallback = cb;
  192. };
  193. const _createBLEConnection = () => {
  194. return new Promise(function (resolve, reject) {
  195. jd.createBLEConnection({
  196. deviceId: ecDeviceId,
  197. success(res) {
  198. log(res);
  199. // {"errno":0,"errCode":0,"errMsg":"createBLEConnection:ok"}
  200. resolve({
  201. ok: true,
  202. errCode: 0,
  203. errMsg: ''
  204. });
  205. },
  206. fail(res) {
  207. log(res);
  208. // {"errno":1001,"errMsg":"createBLEConnection:fail parameter error: parameter.deviceId should be String instead of Undefined;"}
  209. resolve({
  210. ok: false,
  211. errCode: res.errCode ? res.errCode : res.errno,
  212. errMsg: res.errMsg
  213. });
  214. }
  215. });
  216. if (!isAndroid) {
  217. setTimeout(() => {
  218. jd.createBLEConnection({
  219. deviceId: ecDeviceId,
  220. success(res) {
  221. log(res);
  222. // {"errno":0,"errCode":0,"errMsg":"createBLEConnection:ok"}
  223. resolve({
  224. ok: true,
  225. errCode: 0,
  226. errMsg: ''
  227. });
  228. },
  229. fail(res) {
  230. log(res);
  231. // {"errno":1001,"errMsg":"createBLEConnection:fail parameter error: parameter.deviceId should be String instead of Undefined;"}
  232. resolve({
  233. ok: false,
  234. errCode: res.errCode ? res.errCode : res.errno,
  235. errMsg: res.errMsg
  236. });
  237. }
  238. });
  239. }, 1000)
  240. }
  241. });
  242. };
  243. const getBLEDeviceServices = () => {
  244. return new Promise(function (resolve, reject) {
  245. jd.getBLEDeviceServices({
  246. deviceId: ecDeviceId,
  247. success(res) {
  248. log(res);
  249. //{"services":[{"uuid":"0000FFF0-0000-1000-8000-00805F9B34FB","isPrimary":true}],"errCode":0,"errno":0,"errMsg":"getBLEDeviceServices:ok"}
  250. // {"errno":0,"deviceId":"7C7E20F2-CB75-6DA8-F8DF-FFF702B0D63F","services":[{"isPrimary":true,"uuid":"0000FFF0-0000-1000-8000-00805F9B34FB"}],"errMsg":"getBLEDeviceServices:ok","errCode":0}
  251. resolve({
  252. ok: true,
  253. errCode: 0,
  254. errMsg: '',
  255. services: res.services
  256. });
  257. },
  258. fail(res) {
  259. log(res);
  260. resolve({
  261. ok: false,
  262. errCode: res.errCode,
  263. errMsg: res.errMsg
  264. });
  265. }
  266. });
  267. });
  268. };
  269. const getBLEDeviceCharacteristics = serviceId => {
  270. return new Promise(function (resolve, reject) {
  271. jd.getBLEDeviceCharacteristics({
  272. deviceId: ecDeviceId,
  273. serviceId,
  274. success(res) {
  275. log(res);
  276. // {"characteristics":[{"uuid":"0000FFF2-0000-1000-8000-00805F9B34FB","handle":3,"properties":{"read":false,"write":true,"notify":false,"indicate":false,"writeNoResponse":true,"writeDefault":true}},{"uuid":"0000FFF1-0000-1000-8000-00805F9B34FB","handle":5,"properties":{"read":true,"write":true,"notify":true,"indicate":false,"writeNoResponse":true,"writeDefault":true}}],"errCode":0,"errno":0,"errMsg":"getBLEDeviceCharacteristics:ok"}
  277. // {"characteristics":[{"properties":{"writeDefault":true,"notify":false,"write":true,"indicate":false,"read":false,"writeNoResponse":true},"uuid":"0000FFF2-0000-1000-8000-00805F9B34FB"},{"properties":{"writeDefault":true,"notify":true,"write":true,"indicate":false,"read":true,"writeNoResponse":true},"uuid":"0000FFF1-0000-1000-8000-00805F9B34FB"}],"deviceId":"7C7E20F2-CB75-6DA8-F8DF-FFF702B0D63F","serviceId":"0000FFF0-0000-1000-8000-00805F9B34FB","errno":0,"errMsg":"getBLEDeviceCharacteristics:ok","errCode":0}
  278. resolve({
  279. ok: true,
  280. errCode: 0,
  281. errMsg: '',
  282. characteristics: res.characteristics
  283. });
  284. },
  285. fail(res) {
  286. log(res);
  287. resolve({
  288. ok: false,
  289. errCode: res.errCode,
  290. errMsg: res.errMsg
  291. });
  292. }
  293. });
  294. });
  295. };
  296. const notifyBLECharacteristicValueChange = (serviceId, characteristicId) => {
  297. return new Promise(function (resolve, reject) {
  298. jd.notifyBLECharacteristicValueChange({
  299. state: true,
  300. deviceId: ecDeviceId,
  301. serviceId,
  302. characteristicId,
  303. success(res) {
  304. log(res);
  305. // {"errCode":0,"errno":0,"errMsg":"notifyBLECharacteristicValueChange:ok"}
  306. resolve({
  307. ok: true,
  308. errCode: 0,
  309. errMsg: ''
  310. });
  311. },
  312. fail(res) {
  313. log(res);
  314. resolve({
  315. ok: false,
  316. errCode: res.errCode,
  317. errMsg: res.errMsg
  318. });
  319. }
  320. });
  321. });
  322. };
  323. const setBLEMTU = mtu => {
  324. return new Promise(function (resolve, reject) {
  325. jd.setBLEMTU({
  326. deviceId: ecDeviceId,
  327. mtu,
  328. success(res) {
  329. log(res);
  330. // {"errMsg":"setBLEMTU:ok","errno":0,"errCode":0,"mtu":50}
  331. resolve({
  332. ok: true,
  333. errCode: 0,
  334. errMsg: ''
  335. });
  336. },
  337. fail(res) {
  338. log(res);
  339. // {"errCode":-1,"errno":1500104,"errMsg":"setBLEMTU:fail:internal error"}
  340. resolve({
  341. ok: false,
  342. errCode: res.errCode,
  343. errMsg: res.errMsg
  344. });
  345. }
  346. });
  347. });
  348. };
  349. //和设备建立连接
  350. const createBLEConnection = async id => {
  351. ecDeviceId = id;
  352. jd.offBLEConnectionStateChange();
  353. jd.onBLEConnectionStateChange(async res => {
  354. log(res);
  355. // {"deviceId":"EC:22:05:13:78:49","connected":true}
  356. if (res.connected) {
  357. const servicesResult = await getBLEDeviceServices();
  358. if (!servicesResult.ok) {
  359. ecBLEConnectionStateChangeCallback(servicesResult);
  360. closeBLEConnection();
  361. return;
  362. }
  363. for (const service of servicesResult.services) {
  364. if (service.uuid.toUpperCase() === ecGattServerUUIDOption1 || service.uuid.toUpperCase() === ecGattServerUUIDOption2) {
  365. ecGattServerUUID = service.uuid;
  366. }
  367. const characteristicsResult = await getBLEDeviceCharacteristics(service.uuid);
  368. if (!characteristicsResult.ok) {
  369. ecBLEConnectionStateChangeCallback(characteristicsResult);
  370. closeBLEConnection();
  371. return;
  372. }
  373. for (const characteristic of characteristicsResult.characteristics) {
  374. if (characteristic.properties && characteristic.properties.notify) {
  375. const notifyResult = await notifyBLECharacteristicValueChange(service.uuid, characteristic.uuid);
  376. if (!notifyResult.ok) {
  377. ecBLEConnectionStateChangeCallback({
  378. ok: false,
  379. errCode: 30000,
  380. errMsg: 'notify error'
  381. });
  382. closeBLEConnection();
  383. return;
  384. }
  385. }
  386. if (characteristic.uuid.toUpperCase() === ecGattCharacteristicWriteUUIDOption1 || characteristic.uuid.toUpperCase() === ecGattCharacteristicWriteUUIDOption2) {
  387. ecGattCharacteristicWriteUUID = characteristic.uuid;
  388. }
  389. }
  390. }
  391. if (isAndroid) {
  392. await setBLEMTU(247);
  393. }
  394. ecBLEConnectionStateChangeCallback({
  395. ok: true,
  396. errCode: 0,
  397. errMsg: ''
  398. });
  399. } else {
  400. ecBLEConnectionStateChangeCallback({
  401. ok: false,
  402. errCode: 0,
  403. errMsg: 'disconnect'
  404. });
  405. }
  406. });
  407. const res = await _createBLEConnection();
  408. if (!res.ok) {
  409. ecBLEConnectionStateChangeCallback(res);
  410. }
  411. };
  412. //关闭当前连接
  413. const closeBLEConnection = () => {
  414. jd.closeBLEConnection({
  415. deviceId: ecDeviceId,
  416. complete(res) {
  417. log(res);
  418. }
  419. });
  420. };
  421. const onBLECharacteristicValueChange = cb => {
  422. jd.offBLECharacteristicValueChange();
  423. jd.onBLECharacteristicValueChange(res => {
  424. log(res);
  425. let x = new Uint8Array(res.value);
  426. log(x);
  427. let str = utf8BytesToStr(x);
  428. let strHex = '';
  429. for (let i = 0; i < x.length; i++) {
  430. strHex = strHex + x[i].toString(16).padStart(2, '0').toUpperCase();
  431. }
  432. log(str);
  433. log(strHex);
  434. cb(str, strHex);
  435. });
  436. };
  437. const _writeBLECharacteristicValue = buffer => {
  438. return new Promise(function (resolve, reject) {
  439. jd.writeBLECharacteristicValue({
  440. deviceId: ecDeviceId,
  441. serviceId: ecGattServerUUID,
  442. characteristicId: ecGattCharacteristicWriteUUID,
  443. value: buffer,
  444. writeType: 'writeNoResponse',
  445. success(res) {
  446. log(res);
  447. // {"errno":0,"errCode":0,"errMsg":"writeBLECharacteristicValue:ok"}
  448. resolve({
  449. ok: true,
  450. errCode: 0,
  451. errMsg: ''
  452. });
  453. },
  454. fail(res) {
  455. log(res);
  456. resolve({
  457. ok: false,
  458. errCode: res.errCode,
  459. errMsg: res.errMsg
  460. });
  461. }
  462. });
  463. });
  464. };
  465. const writeBLECharacteristicValue = async (str, isHex) => {
  466. if (str.length === 0) return {
  467. ok: false,
  468. errCode: 30000,
  469. errMsg: 'data is null'
  470. };
  471. let buffer;
  472. if (isHex) {
  473. buffer = new ArrayBuffer(str.length / 2);
  474. let x = new Uint8Array(buffer);
  475. for (let i = 0; i < x.length; i++) {
  476. x[i] = parseInt(str.substr(2 * i, 2), 16);
  477. }
  478. } else {
  479. buffer = new Uint8Array(strToUtf8Bytes(str)).buffer;
  480. }
  481. return await _writeBLECharacteristicValue(buffer);
  482. };
  483. const utf8BytesToStr = utf8Bytes => {
  484. let unicodeStr = '';
  485. for (let pos = 0; pos < utf8Bytes.length;) {
  486. let flag = utf8Bytes[pos];
  487. let unicode = 0;
  488. if (flag >>> 7 === 0) {
  489. unicodeStr += String.fromCharCode(utf8Bytes[pos]);
  490. pos += 1;
  491. }
  492. // else if ((flag & 0xFC) === 0xFC) {
  493. // unicode = (utf8Bytes[pos] & 0x3) << 30
  494. // unicode |= (utf8Bytes[pos + 1] & 0x3F) << 24
  495. // unicode |= (utf8Bytes[pos + 2] & 0x3F) << 18
  496. // unicode |= (utf8Bytes[pos + 3] & 0x3F) << 12
  497. // unicode |= (utf8Bytes[pos + 4] & 0x3F) << 6
  498. // unicode |= (utf8Bytes[pos + 5] & 0x3F)
  499. // unicodeStr += String.fromCharCode(unicode)
  500. // pos += 6
  501. // }
  502. // else if ((flag & 0xF8) === 0xF8) {
  503. // unicode = (utf8Bytes[pos] & 0x7) << 24
  504. // unicode |= (utf8Bytes[pos + 1] & 0x3F) << 18
  505. // unicode |= (utf8Bytes[pos + 2] & 0x3F) << 12
  506. // unicode |= (utf8Bytes[pos + 3] & 0x3F) << 6
  507. // unicode |= (utf8Bytes[pos + 4] & 0x3F)
  508. // unicodeStr += String.fromCharCode(unicode)
  509. // pos += 5
  510. // }
  511. else if ((flag & 0xf0) === 0xf0) {
  512. unicode = (utf8Bytes[pos] & 0xf) << 18;
  513. unicode |= (utf8Bytes[pos + 1] & 0x3f) << 12;
  514. unicode |= (utf8Bytes[pos + 2] & 0x3f) << 6;
  515. unicode |= utf8Bytes[pos + 3] & 0x3f;
  516. unicodeStr += String.fromCharCode(unicode);
  517. pos += 4;
  518. } else if ((flag & 0xe0) === 0xe0) {
  519. unicode = (utf8Bytes[pos] & 0x1f) << 12;
  520. unicode |= (utf8Bytes[pos + 1] & 0x3f) << 6;
  521. unicode |= utf8Bytes[pos + 2] & 0x3f;
  522. unicodeStr += String.fromCharCode(unicode);
  523. pos += 3;
  524. } else if ((flag & 0xc0) === 0xc0) {
  525. //110
  526. unicode = (utf8Bytes[pos] & 0x3f) << 6;
  527. unicode |= utf8Bytes[pos + 1] & 0x3f;
  528. unicodeStr += String.fromCharCode(unicode);
  529. pos += 2;
  530. } else {
  531. unicodeStr += String.fromCharCode(utf8Bytes[pos]);
  532. pos += 1;
  533. }
  534. }
  535. return unicodeStr;
  536. };
  537. const strToUtf8Bytes = str => {
  538. let bytes = [];
  539. for (let i = 0; i < str.length; ++i) {
  540. let code = str.charCodeAt(i);
  541. if (code >= 0x10000 && code <= 0x10ffff) {
  542. bytes.push(code >> 18 | 0xf0); // 第一个字节
  543. bytes.push(code >> 12 & 0x3f | 0x80);
  544. bytes.push(code >> 6 & 0x3f | 0x80);
  545. bytes.push(code & 0x3f | 0x80);
  546. } else if (code >= 0x800 && code <= 0xffff) {
  547. bytes.push(code >> 12 | 0xe0);
  548. bytes.push(code >> 6 & 0x3f | 0x80);
  549. bytes.push(code & 0x3f | 0x80);
  550. } else if (code >= 0x80 && code <= 0x7ff) {
  551. bytes.push(code >> 6 | 0xc0);
  552. bytes.push(code & 0x3f | 0x80);
  553. } else {
  554. bytes.push(code);
  555. }
  556. }
  557. return bytes;
  558. };
  559. module.exports = {
  560. onBluetoothAdapterStateChange,
  561. openBluetoothAdapter,
  562. onBluetoothDeviceFound,
  563. startBluetoothDevicesDiscovery,
  564. stopBluetoothDevicesDiscovery,
  565. onBLEConnectionStateChange,
  566. createBLEConnection,
  567. closeBLEConnection,
  568. onBLECharacteristicValueChange,
  569. writeBLECharacteristicValue
  570. };