ElinkThings小程序蓝牙插件SDK集合:aifresh、ailink http://doc.elinkthings.com/web/#/36?page_id=127
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

index.js 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614
  1. const util = require("../../utils/util");
  2. const plugin = requirePlugin("sdkPlugin").AiLink;
  3. function inArray(arr, key, val) {
  4. if (!arr || !arr.length || typeof arr != 'object' || !Array.isArray(arr)) {
  5. return -1
  6. }
  7. for (let i = 0; i < arr.length; i++) {
  8. if (!key) {
  9. if (arr[i] == val) {
  10. return i
  11. }
  12. } else if (arr[i][key] === val) {
  13. return i
  14. }
  15. }
  16. return -1;
  17. }
  18. // ArrayBuffer转16进度字符串示例
  19. function ab2hex(buffer, split) {
  20. var hexArr = Array.prototype.map.call(
  21. new Uint8Array(buffer),
  22. function (bit) {
  23. return ('00' + bit.toString(16)).slice(-2)
  24. }
  25. )
  26. return hexArr.join(split);
  27. }
  28. Page({
  29. data: {
  30. showWriteInput: false,
  31. devices: [
  32. // {
  33. // deviceId: "02:03:04:05:06:07",
  34. // name: "elink",
  35. // localName: "elink",
  36. // mac: "02:03:04:05:06:07",
  37. // RSSI: -69,
  38. // advertisServiceUUIDs:[
  39. // "FFE0","FFC0"
  40. // ],
  41. // analyzeDataText:"0102003241123413"
  42. // }
  43. ],
  44. connected: false,
  45. chs: [],
  46. cmd: '',
  47. name: '',
  48. deviceId: null,
  49. historyList: [],
  50. state:'',
  51. electricity:'',
  52. model:'',
  53. skipNumber:'',
  54. skipTime:'',
  55. cumulativeNumber:'',
  56. finishArr:{},
  57. stumbleRope:[
  58. ],
  59. isStumbleRope:1,
  60. historicalRecord:'',
  61. isSupport:'',
  62. record:'',
  63. // isHistoricalRecord:false
  64. },
  65. onLoad: function () {
  66. let historyList = []
  67. historyList = wx.getStorageSync('historyList')
  68. this.setData({
  69. historyList,
  70. })
  71. },
  72. // 初始化蓝牙模块
  73. openBluetoothAdapter() {
  74. wx.openBluetoothAdapter({
  75. success: (res) => {
  76. ;
  77. console.log('openBluetoothAdapter success', res)
  78. this.startBluetoothDevicesDiscovery()
  79. },
  80. fail: (res) => {
  81. ;
  82. if (res.errCode === 10001) {
  83. wx.showToast({
  84. title: '请打开蓝牙',
  85. icon: "none"
  86. })
  87. wx.onBluetoothAdapterStateChange(function (res) {
  88. console.log('onBluetoothAdapterStateChange', res)
  89. if (res.available) {
  90. this.startBluetoothDevicesDiscovery()
  91. }
  92. })
  93. }
  94. }
  95. })
  96. },
  97. // 获取本机蓝牙适配器状态
  98. getBluetoothAdapterState() {
  99. wx.getBluetoothAdapterState({
  100. success: (res) => {
  101. ;
  102. console.log('getBluetoothAdapterState', res)
  103. if (res.discovering) {
  104. this.onBluetoothDeviceFound()
  105. } else if (res.available) {
  106. this.startBluetoothDevicesDiscovery()
  107. }
  108. }
  109. })
  110. },
  111. // 开始搜寻附近的蓝牙外围设备
  112. startBluetoothDevicesDiscovery() {
  113. if (this._discoveryStarted) {
  114. return
  115. }
  116. this._discoveryStarted = true
  117. wx.startBluetoothDevicesDiscovery({
  118. allowDuplicatesKey: true,
  119. services: [
  120. "FFE0",
  121. // "F0A0", // BM30广播模块需加上,如使用连接模块可忽略
  122. ],
  123. success: (res) => {
  124. ;
  125. console.log('startBluetoothDevicesDiscovery success', res)
  126. this.onBluetoothDeviceFound()
  127. },
  128. })
  129. },
  130. // 停止搜寻附近的蓝牙外围设备
  131. stopBluetoothDevicesDiscovery() {
  132. wx.stopBluetoothDevicesDiscovery()
  133. },
  134. // 监听寻找到新设备的事件
  135. onBluetoothDeviceFound() {
  136. wx.onBluetoothDeviceFound((res) => {
  137. res.devices.forEach(device => {
  138. ;
  139. if (!device.name && !device.localName) {
  140. return
  141. }
  142. const foundDevices = this.data.devices
  143. const idx = inArray(foundDevices, 'deviceId', device.deviceId)
  144. const data = {}
  145. // console.log(device)
  146. // console.log(ab2hex(device.advertisData))
  147. // 此处判断是否BM30广播模块,如使用连接模块请删除此 if ,只保留 else 内容
  148. if (device.advertisServiceUUIDs[0].indexOf("F0A0") !== -1) {
  149. let parseDataRes = plugin.parseBroadcastData(device.advertisData)
  150. console.log(parseDataRes)
  151. if (parseDataRes.status == 1) {
  152. let analyzeData = plugin.analyzeBroadcastScaleData(parseDataRes)
  153. console.log(analyzeData)
  154. device.analyzeDataText = analyzeData.text
  155. }
  156. } else {
  157. let buff = device.advertisData.slice(-6)
  158. device.mac = new Uint8Array(buff) // 保存广播数据中的mac地址,这是由于iOS不直接返回mac地址
  159. let tempMac = Array.from(device.mac)
  160. tempMac.reverse()
  161. device.macAddr = ab2hex(tempMac, ':').toUpperCase()
  162. }
  163. if (idx === -1) {
  164. data[`devices[${foundDevices.length}]`] = device
  165. } else {
  166. data[`devices[${idx}]`] = device
  167. }
  168. this.setData(data)
  169. })
  170. })
  171. },
  172. // 连接低功耗蓝牙设备
  173. createBLEConnection(e) {
  174. this._connLoading = true
  175. wx.showLoading({
  176. title: '连接中',
  177. })
  178. setTimeout(() => {
  179. ;
  180. if (this._connLoading) {
  181. this._connLoading = false
  182. wx.hideLoading()
  183. }
  184. }, 6000)
  185. const ds = e.currentTarget.dataset
  186. const index = ds.index
  187. // 保存当前连接的设备,注意不能从wxml的dataset中直接返回该对象,因为ArrarBuffer类型的数据无法保留
  188. this._device = this.data.devices[index]
  189. console.log(this._device)
  190. const deviceId = ds.deviceId
  191. const name = ds.name
  192. this.mac = ds.mac
  193. wx.createBLEConnection({
  194. deviceId,
  195. success: (res) => {
  196. ;
  197. this.setData({
  198. connected: true,
  199. name,
  200. deviceId,
  201. })
  202. console.log("createBLEConnection:success")
  203. wx.stopBluetoothDevicesDiscovery()
  204. this.onBLEConnectionStateChange()
  205. this.getBLEDeviceServices(deviceId)
  206. },
  207. fail: res => {
  208. ;
  209. this._connLoading = false
  210. wx.hideLoading()
  211. wx.showToast({
  212. title: '连接失败',
  213. icon: 'none'
  214. })
  215. }
  216. })
  217. // 连接上设备就可以停止蓝牙搜索,减少功耗。
  218. this.stopBluetoothDevicesDiscovery()
  219. },
  220. onBLEConnectionStateChange() {
  221. wx.onBLEConnectionStateChange((res) => {
  222. console.log('wx.onBLEConnectionStateChange() ', res.deviceId, res.connected, res.errorCode, res.errorMsg)
  223. // 该方法回调中可以用于处理连接意外断开等异常情况
  224. // console.log(`%c device ${res.deviceId} state has changed, connected: ${res.connected}`, 'color: #F26363')
  225. if (!res.connected) {
  226. ;
  227. wx.showToast({
  228. title: '连接已断开',
  229. icon: 'none'
  230. })
  231. this.setData({
  232. connected: false,
  233. showWriteInput: false,
  234. })
  235. }
  236. })
  237. },
  238. // 断开与低功耗蓝牙设备的连接
  239. closeBLEConnection() {
  240. wx.closeBLEConnection({
  241. deviceId: this._deviceId
  242. })
  243. this.setData({
  244. connected: false,
  245. chs: [],
  246. showWriteInput: false,
  247. })
  248. },
  249. // 获取蓝牙设备的 serviceId
  250. getBLEDeviceServices(deviceId) {
  251. wx.getBLEDeviceServices({
  252. deviceId,
  253. success: (res) => {
  254. ;
  255. for (let i = 0; i < res.services.length; i++) {
  256. if (res.services[i].isPrimary && res.services[i].uuid.indexOf('FFE0') > -1) {
  257. this.getBLEDeviceCharacteristics(deviceId, res.services[i].uuid)
  258. return
  259. }
  260. }
  261. }
  262. })
  263. },
  264. // 获取蓝牙设备某个服务中所有特征值(characteristic)
  265. getBLEDeviceCharacteristics(deviceId, serviceId) {
  266. this._deviceId = deviceId
  267. this._serviceId = serviceId
  268. this._device.serviceId = serviceId
  269. wx.getBLEDeviceCharacteristics({
  270. deviceId,
  271. serviceId,
  272. success: (res) => {
  273. ;
  274. console.log('getBLEDeviceCharacteristics success', res.characteristics)
  275. // 这部分功能在插件的 initPlugin 中已实现,如需用到其中的 uuid 也可取消注释
  276. // // let uuid1, uuid2, uuid3;
  277. for (let i = 0; i < res.characteristics.length; i++) {
  278. let item = res.characteristics[i]
  279. if (item.uuid.indexOf('0000FFE1') != -1) {
  280. this.uuid1 = item.uuid //下发数据
  281. } else if (item.uuid.indexOf('0000FFE2') != -1) {
  282. this.uuid2 = item.uuid //监听数据
  283. } else if (item.uuid.indexOf('0000FFE3') != -1) {
  284. this.uuid3 = item.uuid //写入设置
  285. }
  286. }
  287. // 打开监听
  288. wx.notifyBLECharacteristicValueChange({
  289. deviceId,
  290. serviceId,
  291. characteristicId: this.uuid2,
  292. state: true,
  293. })
  294. wx.notifyBLECharacteristicValueChange({
  295. deviceId,
  296. serviceId,
  297. characteristicId: this.uuid3,
  298. state: true,
  299. })
  300. // 初始化插件
  301. plugin.initPlugin(res.characteristics, this._device)
  302. wx.onBLECharacteristicValueChange((characteristic) => {
  303. // 解析特征值,返回解密后的数据
  304. let bleData = plugin.parseBleData(characteristic.value)
  305. console.log('蓝牙返回',bleData)
  306. if (bleData.status == 0) {
  307. console.log("握手成功")
  308. this._connLoading = false
  309. wx.hideLoading()
  310. wx.showToast({
  311. title: '连接成功',
  312. })
  313. this.setData({
  314. showWriteInput : true
  315. })
  316. } else if (bleData.status == 1) {
  317. let payload = bleData.data //对应协议中的payload数据,可以自行解析该数据
  318. if (payload[0] === 0x10) {
  319. this.setData({
  320. isStumbleRope:1
  321. })
  322. let skipNumber = payload[4] * 256 + payload[3]
  323. let skipTime = payload[6] * 256 + payload[5]
  324. let cumulativeNumber = payload[8] * 256 + payload[7]
  325. this.setData({
  326. state:(payload[1]),//状态
  327. model:(payload[2]),//模式
  328. electricity:(payload[9]),//电量
  329. skipNumber:skipNumber,//跳绳数量
  330. skipTime:skipTime,//跳绳时间
  331. cumulativeNumber:cumulativeNumber//累计个数
  332. })
  333. }else if(payload[0] === 0x20){
  334. let str = payload[1].toString(16)
  335. let laststring = str.substring(str.length-1)
  336. if (laststring == 0) {
  337. var timeStamp = payload[2]+payload[3]*256+payload[4]*65536+payload[5]*16777216//时间戳
  338. var model = (payload[6]).toString(16)//模式
  339. var skipNumber = payload[8] * 256 + payload[7]//跳绳数量
  340. this.setData({
  341. ['finishArr.timeStamp']:timeStamp,
  342. ['finishArr.model']:model,
  343. ['finishArr.skipNumber']:skipNumber,
  344. })
  345. }else if(laststring == 1){
  346. var skipTime = payload[3] * 256 + payload[2]//跳绳时间
  347. var cumulativeNumber = payload[5] * 256 + payload[4]//累计个数
  348. var averageVelocity = payload[7] * 256 + payload[6]//平均速度
  349. var prestissimo = payload[9] * 256 + payload[8]//最快速度
  350. this.setData({
  351. ['finishArr.skipTime']:skipTime,
  352. ['finishArr.cumulativeNumber']:cumulativeNumber,
  353. ['finishArr.averageVelocity']:averageVelocity,
  354. ['finishArr.prestissimo']:prestissimo,
  355. })
  356. }else{
  357. if (payload[2] != 65535 && payload[4] != 65535) {
  358. var stumbleRopeZeroTime = payload[3] * 256 + payload[2]
  359. var stumbleRopeZeroNum = payload[5] * 256 + payload[4]
  360. }
  361. if (payload[6] != 65535 && payload[8] != 65535) {
  362. var stumbleRopeOneTime = payload[7] * 256 + payload[6]
  363. var stumbleRopeOneNum = payload[9] * 256 + payload[8]
  364. }
  365. if (payload[10] != 65535 && payload[12] != 65535) {
  366. var stumbleRopeTwoTime = payload[11] * 256 + payload[10]
  367. var stumbleRopeTwoNum = payload[13] * 256 + payload[12]
  368. }
  369. let stumbleRopeOjeckt = {
  370. stumbleRopeZeroTime:stumbleRopeZeroTime,
  371. stumbleRopeZeroNum:stumbleRopeZeroNum,
  372. stumbleRopeOneTime:stumbleRopeOneTime,
  373. stumbleRopeOneNum:stumbleRopeOneNum,
  374. stumbleRopeTwoTime:stumbleRopeTwoTime,
  375. stumbleRopeTwoNum:stumbleRopeTwoNum,
  376. }
  377. var temp = []
  378. temp.push(stumbleRopeOjeckt)
  379. this.setData({
  380. stumbleRope:temp
  381. })
  382. }
  383. this.setData({
  384. isStumbleRope:2
  385. })
  386. }
  387. }else if(bleData.status == 2){
  388. let payload = bleData.data
  389. if (payload[0].toString(16) == 'f1') {
  390. let historicalRecord = payload[1]//请求历史记录标识
  391. let isSupport = payload[2]//是否支持
  392. let record = payload[4] * 256 + payload[3]
  393. this.setData({
  394. historicalRecord:historicalRecord,
  395. isSupport:isSupport,
  396. record:record,
  397. isStumbleRope:3,
  398. })
  399. }
  400. }
  401. const idx = inArray(this.data.chs, 'uuid', characteristic.characteristicId)
  402. const data = {}
  403. if (idx === -1) {
  404. data[`chs[${this.data.chs.length}]`] = {
  405. uuid: characteristic.characteristicId,
  406. value: ab2hex(bleData.completeData, ' ')
  407. }
  408. } else {
  409. data[`chs[${idx}]`] = {
  410. uuid: characteristic.characteristicId,
  411. value: ab2hex(bleData.completeData, ' ')
  412. }
  413. }
  414. this.setData(data)
  415. })
  416. },
  417. fail(res) {
  418. console.error('getBLEDeviceCharacteristics', res)
  419. }
  420. })
  421. },
  422. writeBLECharacteristicValue(buffer, uuid, deviceId, serviceId) {
  423. // 向蓝牙设备发送一个二进制流数据
  424. wx.writeBLECharacteristicValue({
  425. deviceId,
  426. serviceId,
  427. characteristicId: uuid,
  428. value: buffer,
  429. success(res) {
  430. ;
  431. console.log('writeBLECharacteristicValue success', res)
  432. console.log('下发指令==> ' + ab2hex(buffer))
  433. }
  434. })
  435. },
  436. closeBluetoothAdapter() {
  437. wx.closeBluetoothAdapter()
  438. this._discoveryStarted = false
  439. },
  440. //发送指令给设备
  441. SendInstructions(e){
  442. let key = e.currentTarget.dataset.index
  443. let cmd = ''
  444. switch (key) {
  445. case '1':
  446. cmd = 'a7,00,2f,05,00,67,28,9f,61,c3,7a'
  447. this.submitCmd(null,cmd)
  448. break;
  449. case '2':
  450. cmd = 'a7,00,2f,02,01,01,33,7a'
  451. this.submitCmd(null,cmd)
  452. this.setData({
  453. isStumbleRope:1
  454. })
  455. break;
  456. case '3':
  457. cmd = 'a7,00,2f,02,01,00,32,7a'
  458. this.submitCmd(null,cmd)
  459. break;
  460. case '4':
  461. cmd = 'a7,00,2f,02,02,01,34,7a'
  462. this.submitCmd(null,cmd)
  463. this.setData({
  464. isStumbleRope:1
  465. })
  466. break;
  467. case '5':
  468. cmd = 'a7,00,2f,02,02,00,33,7a'
  469. this.submitCmd(null,cmd)
  470. break;
  471. case '6':
  472. cmd = 'a7,00,2f,02,03,01,35,7a'
  473. this.submitCmd(null,cmd)
  474. this.setData({
  475. isStumbleRope:1
  476. })
  477. break;
  478. case '7':
  479. cmd = 'a7,00,2f,02,03,00,34,7a'
  480. this.submitCmd(null,cmd)
  481. break;
  482. case '8':
  483. cmd = 'a7,00,2f,03,04,78,00,ae,7a'
  484. this.submitCmd(null,cmd)
  485. break;
  486. case '9':
  487. cmd = 'a7,00,2f,03,05,32,00,69,7a'
  488. this.submitCmd(null,cmd)
  489. break;
  490. case '10':
  491. cmd = 'a6,02,f1,01,f4,6a'
  492. this.submitCmd(null,cmd)
  493. break;
  494. case '11':
  495. cmd = 'a7,00,2f,02,b0,01,e2,7a'
  496. this.submitCmd(null,cmd)
  497. break;
  498. }
  499. },
  500. // 指令下发
  501. submitCmd(e, cmd) {
  502. let arr = []
  503. let temp = []
  504. if (!cmd) {
  505. cmd = this.data.cmd
  506. }
  507. console.log(cmd)
  508. if(!cmd || !this.data.connected){
  509. return
  510. }
  511. if(cmd.indexOf(",") == -1){
  512. temp = cmd.split(" ")
  513. } else {
  514. temp = cmd.split(",")
  515. }
  516. let tempCmd = temp.join(' ')
  517. for(let i = 0; i < temp.length; i++){
  518. arr[i] = parseInt(temp[i],16)
  519. }
  520. // let arr = [
  521. // 0xA6,
  522. // 0x01,
  523. // 0x28,
  524. // 0x29,
  525. // 0x6A,
  526. // //A6 01 28 29 6A
  527. // ]
  528. if(arr[0] == 0xA6){
  529. let len = arr[1]
  530. let payload = arr.slice(2, 2 + len)
  531. plugin.sendDataOfA6(payload)
  532. // let buff = new Uint8Array(arr).buffer
  533. // this.writeBLECharacteristicValue(buff, this.uuid3, this._deviceId, this._serviceId)
  534. } else if(arr[0] == 0xA7) {
  535. let len = arr[3]
  536. let payload = arr.slice(4, 4 + len)
  537. plugin.sendDataOfA7(payload)
  538. }
  539. let historyList = wx.getStorageSync('historyList') || []
  540. let idx = historyList.findIndex(item => item.cmd == tempCmd)
  541. if (idx < 0) {
  542. historyList.push({cmd: tempCmd})
  543. }
  544. this.setData({
  545. historyList
  546. })
  547. wx.setStorage({
  548. data: historyList,
  549. key: 'historyList',
  550. })
  551. wx.showToast({
  552. title: '已发送',
  553. icon: 'none'
  554. })
  555. },
  556. history_delete(e) {
  557. ;
  558. let index = e.currentTarget.dataset.index
  559. this.data.historyList.splice(index, 1)
  560. this.setData({
  561. historyList: this.data.historyList
  562. })
  563. wx.setStorage({
  564. data: this.data.historyList,
  565. key: 'historyList',
  566. })
  567. },
  568. history_copy(e) {
  569. ;
  570. let index = e.currentTarget.dataset.index
  571. wx.setClipboardData({
  572. data: this.data.historyList[index].cmd
  573. })
  574. },
  575. history_send(e) {
  576. let index = e.currentTarget.dataset.index
  577. let cmd = this.data.historyList[index].cmd
  578. this.submitCmd(null, cmd)
  579. },
  580. });