ElinkThings小程序蓝牙插件SDK集合:aifresh、ailink http://doc.elinkthings.com/web/#/36?page_id=127
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  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. },
  51. onLoad: function () {
  52. let historyList = []
  53. historyList = wx.getStorageSync('historyList')
  54. this.setData({
  55. historyList,
  56. })
  57. },
  58. // 初始化蓝牙模块
  59. openBluetoothAdapter() {
  60. wx.openBluetoothAdapter({
  61. success: (res) => {
  62. console.log('openBluetoothAdapter success', res)
  63. this.startBluetoothDevicesDiscovery()
  64. },
  65. fail: (res) => {
  66. if (res.errCode === 10001) {
  67. wx.showToast({
  68. title: '请打开蓝牙',
  69. icon: "none"
  70. })
  71. wx.onBluetoothAdapterStateChange(function (res) {
  72. console.log('onBluetoothAdapterStateChange', res)
  73. if (res.available) {
  74. this.startBluetoothDevicesDiscovery()
  75. }
  76. })
  77. }
  78. }
  79. })
  80. },
  81. // 获取本机蓝牙适配器状态
  82. getBluetoothAdapterState() {
  83. wx.getBluetoothAdapterState({
  84. success: (res) => {
  85. console.log('getBluetoothAdapterState', res)
  86. if (res.discovering) {
  87. this.onBluetoothDeviceFound()
  88. } else if (res.available) {
  89. this.startBluetoothDevicesDiscovery()
  90. }
  91. }
  92. })
  93. },
  94. // 开始搜寻附近的蓝牙外围设备
  95. startBluetoothDevicesDiscovery() {
  96. if (this._discoveryStarted) {
  97. return
  98. }
  99. this._discoveryStarted = true
  100. wx.startBluetoothDevicesDiscovery({
  101. allowDuplicatesKey: true,
  102. services: [
  103. "FFE0",
  104. "F0A0", // BM30广播模块需加上,如使用连接模块可忽略
  105. ],
  106. powerLevel: 'high',
  107. success: (res) => {
  108. console.log('startBluetoothDevicesDiscovery success', res)
  109. this.onBluetoothDeviceFound()
  110. },
  111. })
  112. },
  113. // 停止搜寻附近的蓝牙外围设备
  114. stopBluetoothDevicesDiscovery() {
  115. wx.stopBluetoothDevicesDiscovery()
  116. },
  117. // 监听寻找到新设备的事件
  118. onBluetoothDeviceFound() {
  119. wx.onBluetoothDeviceFound((res) => {
  120. res.devices.forEach(device => {
  121. if (!device.name && !device.localName) {
  122. return
  123. }
  124. const foundDevices = this.data.devices
  125. const idx = inArray(foundDevices, 'deviceId', device.deviceId)
  126. const data = {}
  127. // console.log(device)
  128. // console.log(ab2hex(device.advertisData))
  129. // 此处判断是否BM30广播模块,如使用连接模块请删除此 if ,只保留 else 内容
  130. if (device.advertisServiceUUIDs[0].indexOf("F0A0") !== -1) {
  131. let parseDataRes = plugin.parseBroadcastData(device.advertisData)
  132. console.log(parseDataRes)
  133. if (parseDataRes.status == 1) {
  134. let analyzeData = plugin.analyzeBroadcastScaleData(parseDataRes)
  135. console.log(analyzeData)
  136. device.analyzeDataText = analyzeData.text
  137. }
  138. } else {
  139. let buff = device.advertisData.slice(8,14)
  140. device.mac = new Uint8Array(buff) // 保存广播数据中的mac地址,这是由于iOS不直接返回mac地址
  141. let tempMac = Array.from(device.mac)
  142. tempMac.reverse()
  143. device.macAddr = ab2hex(tempMac, ':').toUpperCase()
  144. }
  145. if (idx === -1) {
  146. data[`devices[${foundDevices.length}]`] = device
  147. } else {
  148. data[`devices[${idx}]`] = device
  149. }
  150. this.setData(data)
  151. })
  152. })
  153. },
  154. // 连接低功耗蓝牙设备
  155. createBLEConnection(e) {
  156. this._connLoading = true
  157. wx.showLoading({
  158. title: '连接中',
  159. })
  160. setTimeout(() => {
  161. if (this._connLoading) {
  162. this._connLoading = false
  163. wx.hideLoading()
  164. }
  165. }, 6000)
  166. const ds = e.currentTarget.dataset
  167. const index = ds.index
  168. // 保存当前连接的设备,注意不能从wxml的dataset中直接返回该对象,因为ArrarBuffer类型的数据无法保留
  169. this._device = this.data.devices[index]
  170. console.log(this._device)
  171. const deviceId = ds.deviceId
  172. const name = ds.name
  173. this.mac = ds.mac
  174. wx.createBLEConnection({
  175. deviceId,
  176. success: (res) => {
  177. this.setData({
  178. connected: true,
  179. name,
  180. deviceId,
  181. })
  182. console.log("createBLEConnection:success")
  183. wx.stopBluetoothDevicesDiscovery()
  184. this.onBLEConnectionStateChange()
  185. this.getBLEDeviceServices(deviceId)
  186. },
  187. fail: res => {
  188. this._connLoading = false
  189. wx.hideLoading()
  190. wx.showToast({
  191. title: '连接失败',
  192. icon: 'none'
  193. })
  194. }
  195. })
  196. // 连接上设备就可以停止蓝牙搜索,减少功耗。
  197. this.stopBluetoothDevicesDiscovery()
  198. },
  199. onBLEConnectionStateChange() {
  200. wx.onBLEConnectionStateChange((res) => {
  201. console.log('wx.onBLEConnectionStateChange() ', res.deviceId, res.connected, res.errorCode, res.errorMsg)
  202. // 该方法回调中可以用于处理连接意外断开等异常情况
  203. // console.log(`%c device ${res.deviceId} state has changed, connected: ${res.connected}`, 'color: #F26363')
  204. if (!res.connected) {
  205. wx.showToast({
  206. title: '连接已断开',
  207. icon: 'none'
  208. })
  209. this.setData({
  210. connected: false,
  211. showWriteInput: false,
  212. })
  213. }
  214. })
  215. },
  216. // 断开与低功耗蓝牙设备的连接
  217. closeBLEConnection() {
  218. wx.closeBLEConnection({
  219. deviceId: this._deviceId
  220. })
  221. this.setData({
  222. connected: false,
  223. chs: [],
  224. showWriteInput: false,
  225. })
  226. },
  227. // 获取蓝牙设备的 serviceId
  228. getBLEDeviceServices(deviceId) {
  229. wx.getBLEDeviceServices({
  230. deviceId,
  231. success: (res) => {
  232. for (let i = 0; i < res.services.length; i++) {
  233. if (res.services[i].isPrimary && res.services[i].uuid.indexOf('FFE0') > -1) {
  234. this.getBLEDeviceCharacteristics(deviceId, res.services[i].uuid)
  235. return
  236. }
  237. }
  238. }
  239. })
  240. },
  241. // 获取蓝牙设备某个服务中所有特征值(characteristic)
  242. getBLEDeviceCharacteristics(deviceId, serviceId) {
  243. this._deviceId = deviceId
  244. this._serviceId = serviceId
  245. this._device.serviceId = serviceId
  246. wx.getBLEDeviceCharacteristics({
  247. deviceId,
  248. serviceId,
  249. success: (res) => {
  250. console.log('getBLEDeviceCharacteristics success', res.characteristics)
  251. // 这部分功能在插件的 initPlugin 中已实现,如需用到其中的 uuid 也可取消注释
  252. // // let uuid1, uuid2, uuid3;
  253. for (let i = 0; i < res.characteristics.length; i++) {
  254. let item = res.characteristics[i]
  255. if (item.uuid.indexOf('0000FFE1') != -1) {
  256. this.uuid1 = item.uuid //下发数据
  257. } else if (item.uuid.indexOf('0000FFE2') != -1) {
  258. this.uuid2 = item.uuid //监听数据
  259. } else if (item.uuid.indexOf('0000FFE3') != -1) {
  260. this.uuid3 = item.uuid //写入设置
  261. }
  262. }
  263. // 打开监听
  264. wx.notifyBLECharacteristicValueChange({
  265. deviceId,
  266. serviceId,
  267. characteristicId: this.uuid2,
  268. state: true,
  269. })
  270. wx.notifyBLECharacteristicValueChange({
  271. deviceId,
  272. serviceId,
  273. characteristicId: this.uuid3,
  274. state: true,
  275. })
  276. // 初始化插件
  277. plugin.initPlugin(res.characteristics, this._device)
  278. wx.onBLECharacteristicValueChange((characteristic) => {
  279. // 解析特征值,返回解密后的数据
  280. let bleData = plugin.parseBleData(characteristic.value)
  281. console.log(bleData)
  282. if (bleData.status == 0) {
  283. console.log("握手成功")
  284. this._connLoading = false
  285. wx.hideLoading()
  286. wx.showToast({
  287. title: '连接成功',
  288. })
  289. } else if (bleData.status == 1) {
  290. let payload = bleData.data //对应协议中的payload数据,可以自行解析该数据
  291. // console.log(ab2hex(payload, ' '))
  292. // console.log(ab2hex(bleData.completeData, ' '))
  293. // 以体脂秤数据解析为例
  294. switch (payload[0]) { // payload[0] 指示当前指令的类型,根据类型区分
  295. /**
  296. * 例如: A7 00 0E 05 01 00 01 F4 10 19 7A---------50.0kg
  297. * 其中 01 00 01 F4 10 为 payload
  298. * 具体指令请根据协议解析
  299. */
  300. case 0x01:
  301. case 0x02:
  302. let weightValue = (payload[1] << 16) | (payload[2] << 8) | payload[3]
  303. let decPoint = (payload[4] & 0xf0) >> 4
  304. let unit = payload[4] & 0x0f
  305. // console.log("体重数值:" + weightValue)
  306. // console.log("小数点:" + decPoint)
  307. // console.log("单位:" + unit)
  308. if (unit == 1) { // 单位为斤
  309. weightValue = weightValue / 2
  310. } else {
  311. // ... 其他单位
  312. }
  313. this.weight = weightValue / (10 ** decPoint) // 除去小数点位数
  314. break;
  315. // ... 其他指令请根据协议解析
  316. /**
  317. * 例如: A7 00 0E 03 07 02 30 4A 7A---------阻抗测量成功,阻抗 560Ω
  318. * 其中 07 02 30 为 payload
  319. * 具体指令请根据协议解析
  320. */
  321. case 0x07:
  322. this.adc = (payload[1] << 8) | payload[2]
  323. break;
  324. case 0x0A:
  325. //测量完成
  326. let bodyData = plugin.getBodyData(1, 20, 170, this.weight, this.adc) // 体脂秤数据解析 (男,20岁,身高170,体重,阻抗)
  327. console.log("解析后的体脂数据: ", bodyData)
  328. console.log(util.getWeightDisplay(170, this.weight))
  329. break;
  330. }
  331. const idx = inArray(this.data.chs, 'uuid', characteristic.characteristicId)
  332. const data = {}
  333. if (idx === -1) {
  334. data[`chs[${this.data.chs.length}]`] = {
  335. uuid: characteristic.characteristicId,
  336. value: ab2hex(bleData.completeData, ' ')
  337. }
  338. } else {
  339. data[`chs[${idx}]`] = {
  340. uuid: characteristic.characteristicId,
  341. value: ab2hex(bleData.completeData, ' ')
  342. }
  343. }
  344. this.setData(data)
  345. } else {
  346. // console.log(bleData)
  347. }
  348. })
  349. },
  350. fail(res) {
  351. console.error('getBLEDeviceCharacteristics', res)
  352. }
  353. })
  354. },
  355. writeBLECharacteristicValue(buffer, uuid, deviceId, serviceId) {
  356. // 向蓝牙设备发送一个二进制流数据
  357. wx.writeBLECharacteristicValue({
  358. deviceId,
  359. serviceId,
  360. characteristicId: uuid,
  361. value: buffer,
  362. success(res) {
  363. console.log('writeBLECharacteristicValue success', res)
  364. console.log('下发指令==> ' + ab2hex(buffer))
  365. }
  366. })
  367. },
  368. closeBluetoothAdapter() {
  369. wx.closeBluetoothAdapter()
  370. this._discoveryStarted = false
  371. },
  372. // 打开指令输入框
  373. showWriteInputView() {
  374. this.setData({
  375. showWriteInput: true
  376. })
  377. },
  378. // 关闭指令输入框
  379. hideWriteInputView() {
  380. this.setData({
  381. showWriteInput: false
  382. })
  383. },
  384. // 指令下发
  385. submitCmd(e, cmd) {
  386. let arr = []
  387. let temp = []
  388. if (!cmd) {
  389. cmd = this.data.cmd
  390. }
  391. console.log(cmd)
  392. if(!cmd || !this.data.connected){
  393. return
  394. }
  395. if(cmd.indexOf(",") == -1){
  396. temp = cmd.split(" ")
  397. } else {
  398. temp = cmd.split(",")
  399. }
  400. let tempCmd = temp.join(' ')
  401. for(let i = 0; i < temp.length; i++){
  402. arr[i] = parseInt(temp[i],16)
  403. }
  404. // let arr = [
  405. // 0xA6,
  406. // 0x01,
  407. // 0x28,
  408. // 0x29,
  409. // 0x6A,
  410. // //A6 01 28 29 6A
  411. // ]
  412. if(arr[0] == 0xA6){
  413. let len = arr[1]
  414. let payload = arr.slice(2, 2 + len)
  415. plugin.sendDataOfA6(payload)
  416. // let buff = new Uint8Array(arr).buffer
  417. // this.writeBLECharacteristicValue(buff, this.uuid3, this._deviceId, this._serviceId)
  418. } else if(arr[0] == 0xA7) {
  419. let len = arr[3]
  420. let payload = arr.slice(4, 4 + len)
  421. plugin.sendDataOfA7(payload)
  422. }
  423. let historyList = wx.getStorageSync('historyList') || []
  424. let idx = historyList.findIndex(item => item.cmd == tempCmd)
  425. if (idx < 0) {
  426. historyList.push({cmd: tempCmd})
  427. }
  428. this.setData({
  429. historyList
  430. })
  431. wx.setStorage({
  432. data: historyList,
  433. key: 'historyList',
  434. })
  435. wx.showToast({
  436. title: '已发送',
  437. icon: 'none'
  438. })
  439. },
  440. history_delete(e) {
  441. let index = e.currentTarget.dataset.index
  442. this.data.historyList.splice(index, 1)
  443. this.setData({
  444. historyList: this.data.historyList
  445. })
  446. wx.setStorage({
  447. data: this.data.historyList,
  448. key: 'historyList',
  449. })
  450. },
  451. history_copy(e) {
  452. let index = e.currentTarget.dataset.index
  453. wx.setClipboardData({
  454. data: this.data.historyList[index].cmd
  455. })
  456. },
  457. history_send(e) {
  458. let index = e.currentTarget.dataset.index
  459. let cmd = this.data.historyList[index].cmd
  460. this.submitCmd(null, cmd)
  461. },
  462. });