ElinkThings小程序蓝牙插件SDK集合:aifresh、ailink http://doc.elinkthings.com/web/#/36?page_id=127
Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

index.js 14KB

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