From 13b55836beb627dd3f251da69d0e453ceff53884 Mon Sep 17 00:00:00 2001 From: qiaocl <3189782663@qq.com> Date: Tue, 17 Oct 2023 15:36:02 +0800 Subject: [PATCH] =?UTF-8?q?8=E7=94=B5=E6=9E=81=E6=97=A0SDK=E8=93=9D?= =?UTF-8?q?=E7=89=99=E8=BF=9E=E6=8E=A5demo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bluetooth_demo/app.js | 5 + bluetooth_demo/app.json | 14 + bluetooth_demo/app.wxss | 55 +++ bluetooth_demo/pages/index/index.js | 328 +++++++++++++++ bluetooth_demo/pages/index/index.json | 4 + bluetooth_demo/pages/index/index.wxml | 42 ++ bluetooth_demo/project.config.json | 62 +++ bluetooth_demo/sitemap.json | 7 + bluetooth_demo/utils/util.js | 30 ++ bluetooth_demo_L08_NSDK.zip | Bin 0 -> 8417 bytes bluetooth_demo_L08_NSDK/README.md | 8 + bluetooth_demo_L08_NSDK/app.js | 5 + bluetooth_demo_L08_NSDK/app.json | 14 + bluetooth_demo_L08_NSDK/app.wxss | 55 +++ bluetooth_demo_L08_NSDK/pages/index/index.js | 391 ++++++++++++++++++ .../pages/index/index.json | 4 + .../pages/index/index.wxml | 46 +++ bluetooth_demo_L08_NSDK/project.config.json | 57 +++ .../project.private.config.json | 4 + bluetooth_demo_L08_NSDK/sitemap.json | 7 + bluetooth_demo_L08_NSDK/utils/util.js | 30 ++ 21 files changed, 1168 insertions(+) create mode 100644 bluetooth_demo/app.js create mode 100644 bluetooth_demo/app.json create mode 100644 bluetooth_demo/app.wxss create mode 100644 bluetooth_demo/pages/index/index.js create mode 100644 bluetooth_demo/pages/index/index.json create mode 100644 bluetooth_demo/pages/index/index.wxml create mode 100644 bluetooth_demo/project.config.json create mode 100644 bluetooth_demo/sitemap.json create mode 100644 bluetooth_demo/utils/util.js create mode 100644 bluetooth_demo_L08_NSDK.zip create mode 100644 bluetooth_demo_L08_NSDK/README.md create mode 100644 bluetooth_demo_L08_NSDK/app.js create mode 100644 bluetooth_demo_L08_NSDK/app.json create mode 100644 bluetooth_demo_L08_NSDK/app.wxss create mode 100644 bluetooth_demo_L08_NSDK/pages/index/index.js create mode 100644 bluetooth_demo_L08_NSDK/pages/index/index.json create mode 100644 bluetooth_demo_L08_NSDK/pages/index/index.wxml create mode 100644 bluetooth_demo_L08_NSDK/project.config.json create mode 100644 bluetooth_demo_L08_NSDK/project.private.config.json create mode 100644 bluetooth_demo_L08_NSDK/sitemap.json create mode 100644 bluetooth_demo_L08_NSDK/utils/util.js diff --git a/bluetooth_demo/app.js b/bluetooth_demo/app.js new file mode 100644 index 0000000..ff15a95 --- /dev/null +++ b/bluetooth_demo/app.js @@ -0,0 +1,5 @@ +//app.js +App({ + onLaunch: function () { + } +}) \ No newline at end of file diff --git a/bluetooth_demo/app.json b/bluetooth_demo/app.json new file mode 100644 index 0000000..eb60991 --- /dev/null +++ b/bluetooth_demo/app.json @@ -0,0 +1,14 @@ +{ + "pages": [ + "pages/index/index" + ], + "window": { + "navigationBarBackgroundColor": "#0082FE", + "navigationBarTextStyle": "white", + "navigationBarTitleText": "蓝牙连接Demo", + "backgroundColor": "#eeeeee", + "backgroundTextStyle": "light" + }, + "style": "v2", + "sitemapLocation": "sitemap.json" +} diff --git a/bluetooth_demo/app.wxss b/bluetooth_demo/app.wxss new file mode 100644 index 0000000..bee9f30 --- /dev/null +++ b/bluetooth_demo/app.wxss @@ -0,0 +1,55 @@ +/**app.wxss**/ +view, +cover-view, +scroll-view, +swiper, +swiper-item, +movable-area, +movable-view, +button, +input, +textarea, +label, +navigator { + box-sizing: border-box; +} + +page { + --safe-bottom: env(safe-area-inset-bottom); +} + +.container { + position: relative; + display: flex; + flex-direction: column; + align-items: center; + width: 100vw; + height: 100vh; + padding-bottom: var(--safe-bottom); +} + +.header { + width: 100%; +} + +.header button { + font-size: 16px; + line-height: 40px; + width: 100% !important; + border-bottom: 1px solid #dfdfdf; +} + +.device_item { + padding: 15px; + border-bottom: 1px solid #dfdfdf; +} + +.weight { + width: 100%; + margin: 15px; + padding-bottom: 15px; + text-align: center; + font-size: 18px; + font-weight: 700; + border-bottom: 1px solid #dfdfdf; +} diff --git a/bluetooth_demo/pages/index/index.js b/bluetooth_demo/pages/index/index.js new file mode 100644 index 0000000..5020739 --- /dev/null +++ b/bluetooth_demo/pages/index/index.js @@ -0,0 +1,328 @@ +const util = require("../../utils/util"); +const { + inArray, + ab2hex +} = util + +Page({ + data: { + devices: [], + connected: false, + cmd: '', + name: '', + weight: "", + text: "", + imp: "", + deviceId: null, + }, + onLoad: function() {}, + // 初始化蓝牙模块 + openBluetoothAdapter() { + wx.openBluetoothAdapter({ + success: (res) => { + console.log('openBluetoothAdapter success', res) + wx.showToast({ + title: '蓝牙连接中', + icon: "none" + }) + this.startBluetoothDevicesDiscovery() + }, + fail: (res) => { + if (res.errCode === 10001) { + wx.showToast({ + title: '请打开蓝牙', + icon: "none" + }) + // 监听本机蓝牙状态变化的事件 + wx.onBluetoothAdapterStateChange((res) => { + console.log('onBluetoothAdapterStateChange', res) + if (res.available) { + this.startBluetoothDevicesDiscovery() + } + }) + } + } + }) + }, + + // 开始搜寻附近的蓝牙外围设备 + startBluetoothDevicesDiscovery() { + if (this._discoveryStarted) { + return + } + this._discoveryStarted = true + wx.startBluetoothDevicesDiscovery({ + allowDuplicatesKey: true, //是否允许重复上报同一设备 + services: [ //要搜索蓝牙设备主 service 的 uuid 列表 + "FFE0", + ], + success: (res) => { + console.log('startBluetoothDevicesDiscovery success', res) + this.onBluetoothDeviceFound() + }, + }) + }, + // 停止搜寻附近的蓝牙外围设备 + stopBluetoothDevicesDiscovery() { + wx.stopBluetoothDevicesDiscovery() + }, + + // 找到新设备的事件 + onBluetoothDeviceFound() { + wx.onBluetoothDeviceFound((res) => { + res.devices.forEach(device => { + if (device.name.indexOf('AiLink_') != -1) { + wx.stopBluetoothDevicesDiscovery() //搜索到名称为“AiLink_”的蓝牙后,停止搜寻附近的蓝牙 + const foundDevices = this.data.devices + const idx = inArray(foundDevices, 'deviceId', device.deviceId) + const data = {} + let buff = device.advertisData.slice(-6) + device.mac = new Uint8Array(buff) // 保存广播数据中的mac地址,这是由于iOS不直接返回mac地址 + let tempMac = Array.from(device.mac) + tempMac.reverse() + device.macAddr = ab2hex(tempMac, ':').toUpperCase() + if (idx === -1) { + data[`devices[${foundDevices.length}]`] = device + } else { + data[`devices[${idx}]`] = device + } + console.log("131", data) + this.setData(data) + } + }) + }) + }, + // 连接低功耗蓝牙设备 + createBLEConnection(e) { + this._connLoading = true + wx.showLoading({ + title: '连接中', + }) + setTimeout(() => { + if (this._connLoading) { + this._connLoading = false + wx.hideLoading() + } + }, 6000) + const ds = e.currentTarget.dataset + const index = ds.index + this._device = this.data.devices[index] + const deviceId = ds.deviceId + const name = ds.name + this.mac = ds.mac + wx.createBLEConnection({ + deviceId, + success: (res) => { + this.setData({ + connected: true, + name, + deviceId, + }) + console.log("createBLEConnection:success") + this.onBLEConnectionStateChange() + this.getBLEDeviceServices(deviceId) + }, + fail: res => { + this._connLoading = false + wx.hideLoading() + wx.showToast({ + title: '连接失败', + icon: 'none' + }) + } + }) + }, + //监听蓝牙连接状态 + onBLEConnectionStateChange() { + wx.onBLEConnectionStateChange((res) => { + if (!res.connected) { + setTimeout(() => { + wx.showToast({ + title: '连接已断开', + icon: 'none' + }) + }, 500) + this.setData({ + connected: false, + devices: [], + weight: "", + text: "", + imp: "" + }) + } + }) + }, + + // 获取蓝牙设备的 serviceId + getBLEDeviceServices(deviceId) { + wx.getBLEDeviceServices({ + deviceId, + success: (res) => { + for (let i = 0; i < res.services.length; i++) { + if (res.services[i].isPrimary && res.services[i].uuid.indexOf('FFE0') > -1) { + wx.showLoading({ + title: '获取设备的UUID成功', + }) + this.getBLEDeviceCharacteristics(deviceId, res.services[i].uuid) + return + } + } + } + }) + }, + + // 获取蓝牙设备某个服务中所有特征值(characteristic) + /** + * read: true读,write: true写,notify: true广播 + */ + getBLEDeviceCharacteristics(deviceId, serviceId) { + this._deviceId = deviceId + this._serviceId = serviceId + this._device.serviceId = serviceId + wx.getBLEDeviceCharacteristics({ + deviceId, + serviceId, + success: (res) => { + console.log('getBLEDeviceCharacteristics success', res.characteristics) + wx.showLoading({ + title: '获取特征值成功', + }) + //FFE1:write: true, FFE2:notify: true,FFE3:read: true, write: true, notify: true, + for (let i = 0; i < res.characteristics.length; i++) { + let characteristic = res.characteristics[i]; + if (characteristic.uuid.indexOf("FFE2") != -1) { + if (characteristic.properties.notify == true) { + this.notifyBLECharacteristicValue(deviceId, serviceId, characteristic + .uuid) + console.log("ffe2服务的notifyId获取成功") + } + if (characteristic.properties.read == true) { + that.readId = characteristic.uuid + } + if (characteristic.properties.write == true) { + that.writeId = characteristic.uuid + } + break; + } + } + }, + fail(res) { + console.error('getBLEDeviceCharacteristics', res) + } + }) + }, + //解析蓝牙返回数据 + notifyBLECharacteristicValue(deviceId, serviceId, notifyId) { + let that = this + wx.notifyBLECharacteristicValueChange({ + state: true, // 启用 notify 功能 + deviceId: deviceId, + serviceId: serviceId, + characteristicId: notifyId, + success(res) { + wx.onBLECharacteristicValueChange(function(res) { + let value = ab2hex(res.value); + let num = value.substring(18, 19) + let dw = value.substring(19, 20) + let typeInfo = value.substring(10, 12) + console.log("16进制转化:", value); + if (value.length == 26) { + let data = parseInt(value.substring(13, 18), 16) + let dw1 = "kg" + if (dw == "1") { + dw1 = "斤" + console.log("体重单位:斤") + } + if (dw == "4") { + dw1 = "st:lb" + console.log("体重单位:st:lb,计算方法:1 * data + 5") + } + if (dw == "6") { + dw1 = "lb" + console.log("体重单位:lb") + } + if (num == "1") { + data = parseInt(value.substring(13, 18), 16) / 10 + console.log("体重小数点后1位") + } + if (num == "2") { + data = parseInt(value.substring(13, 18), 16) / 100 + console.log("体重小数点后2位") + } + if (num == "3") { + data = parseInt(value.substring(13, 18), 16) / 1000 + console.log("体重小数点后3位") + } + if (typeInfo == "01") { + console.log("实时体重:", data) + that.setData({ + weight: "实时体重是:" + data + dw1 + }) + } + if (typeInfo == "02") { + console.log("稳定体重:", data) + that.setData({ + weight: "稳定体重是:" + data + dw1 + }) + } + } + if (value.length == 30) { + console.log("阻抗值:", value) + if (typeInfo == "03") { + let imp = parseInt(value.substring(17, 22), 16) + that.setData({ + imp: "阻抗值:" + imp + }) + } + + } + if (value.toUpperCase() == "A90026023000589A") { + console.log("测量完成") + that.setData({ + text: "测量完成" + }) + } + }); + }, + fail(res) { + console.log("测量失败", res.value); + } + }); + }, + + /** + * 断开蓝牙模块 + */ + closeBluetoothAdapter() { + wx.closeBluetoothAdapter() + this._discoveryStarted = false + wx.showToast({ + title: '断开蓝牙模块', + icon: 'none' + }) + this.setData({ + devices: [], + weight: "", + text: "", + imp: "" + }) + }, + // 断开与低功耗蓝牙设备的连接 + closeBLEConnection() { + wx.closeBLEConnection({ + deviceId: this._deviceId + }) + wx.showToast({ + title: '断开蓝牙连接', + icon: 'none' + }) + this.setData({ + connected: false, + devices: [], + text: "", + weight: "", + imp: "" + }) + }, + +}); diff --git a/bluetooth_demo/pages/index/index.json b/bluetooth_demo/pages/index/index.json new file mode 100644 index 0000000..b55b5a2 --- /dev/null +++ b/bluetooth_demo/pages/index/index.json @@ -0,0 +1,4 @@ +{ + "usingComponents": { + } +} \ No newline at end of file diff --git a/bluetooth_demo/pages/index/index.wxml b/bluetooth_demo/pages/index/index.wxml new file mode 100644 index 0000000..cbe4525 --- /dev/null +++ b/bluetooth_demo/pages/index/index.wxml @@ -0,0 +1,42 @@ + +module.exports.max = function(n1, n2) { + return Math.max(n1, n2) +} +module.exports.len = function(arr) { + arr = arr || [] + return arr.length +} + + + + + + + + + + {{weight}} + {{imp}} + {{text}} + + 已发现 {{devices.length}} 个外围设备: + + + + {{item.name}} + (信号强度: {{item.RSSI}}dBm) + + mac地址: {{item.macAddr || item.deviceId}} + + + + + + diff --git a/bluetooth_demo/project.config.json b/bluetooth_demo/project.config.json new file mode 100644 index 0000000..6e6b4b9 --- /dev/null +++ b/bluetooth_demo/project.config.json @@ -0,0 +1,62 @@ +{ + "description": "项目配置文件", + "packOptions": { + "ignore": [] + }, + "setting": { + "urlCheck": true, + "scopeDataCheck": false, + "coverView": true, + "es6": false, + "postcss": true, + "compileHotReLoad": false, + "lazyloadPlaceholderEnable": false, + "preloadBackgroundData": false, + "minified": true, + "autoAudits": false, + "newFeature": false, + "uglifyFileName": false, + "uploadWithSourceMap": true, + "useIsolateContext": true, + "nodeModules": false, + "enhance": true, + "useMultiFrameRuntime": true, + "useApiHook": true, + "useApiHostProcess": true, + "showShadowRootInWxmlPanel": true, + "packNpmManually": false, + "packNpmRelationList": [], + "minifyWXSS": true + }, + "compileType": "miniprogram", + "libVersion": "2.16.0", + "projectname": "bluetooth_demo", + "debugOptions": { + "hidedInDevtools": [] + }, + "scripts": {}, + "isGameTourist": false, + "simulatorType": "wechat", + "simulatorPluginLibVersion": {}, + "appid": "wxcea3504a31518eb6", + "condition": { + "search": { + "list": [] + }, + "conversation": { + "list": [] + }, + "game": { + "list": [] + }, + "plugin": { + "list": [] + }, + "gamePlugin": { + "list": [] + }, + "miniprogram": { + "list": [] + } + } +} \ No newline at end of file diff --git a/bluetooth_demo/sitemap.json b/bluetooth_demo/sitemap.json new file mode 100644 index 0000000..ca02add --- /dev/null +++ b/bluetooth_demo/sitemap.json @@ -0,0 +1,7 @@ +{ + "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html", + "rules": [{ + "action": "allow", + "page": "*" + }] +} \ No newline at end of file diff --git a/bluetooth_demo/utils/util.js b/bluetooth_demo/utils/util.js new file mode 100644 index 0000000..b00f2de --- /dev/null +++ b/bluetooth_demo/utils/util.js @@ -0,0 +1,30 @@ +function inArray(arr, key, val) { + if (!arr || !arr.length || typeof arr != 'object' || !Array.isArray(arr)) { + return -1 + } + for (let i = 0; i < arr.length; i++) { + if (!key) { + if (arr[i] == val) { + return i + } + } else if (arr[i][key] === val) { + return i + } + } + return -1; +} + +// ArrayBuffer转16进度字符串示例 +function ab2hex(buffer) { + var hexArr = Array.prototype.map.call( + new Uint8Array(buffer), + function(bit) { + return ('00' + bit.toString(16)).slice(-2) + } + ) + return hexArr.join(""); +} +module.exports = { + inArray, + ab2hex, +} diff --git a/bluetooth_demo_L08_NSDK.zip b/bluetooth_demo_L08_NSDK.zip new file mode 100644 index 0000000000000000000000000000000000000000..10df2aaa49e91286056074ac9b1c595aa395054d GIT binary patch literal 8417 zcmb7~2RvNa8o)E zf(UtI^YYfd-DG#~{LZ~|e>3O)&v(xEo%zbBDFIQ50T+jwyqflJZ~l9NAg?AiF6Pem z_Rf}uX6Cl`hRR$5hN_yLEyPO)|f0FIp9RXFBrz+vsg$@%>Xl5lWf_5#t;+S@4` zyV#jp3ej31FV0r>cC^eav=^UvgS=TTP#3d=3Fwwr*B1PkTi=1agQEZdw111Tx3i6E z!wulV@42V&5JOWtJE&h^F{59Fe@QKiWqg#dz>iMEFFZUp&<-AIpCb}t>)+9ilko8U z8HbzU9KR=R5aflC(4?Yz@pWVBfg;@879Mb-;*h4ohh#G@ItZvU2?)bzl%xDav~Q8R zCogGasYB$|0I*1z%Tz9Tkpz8#fD;tNLIa?|tVPYDtqEQyK0amTf*|*nikX-mfdkQEXk2$6X#2wCB5w$FYaB&(9;5s&zU$*74J_N6^NvW-Ie~vK zzMH#~(=&}3)lx2e@Ax3!wf=qu5n-+CpGdw!cwS9$DOjb{Hf8qsx`Aw5n?)?$E$sHG?3Dr@-GJ25zsjA`aDwPceiKQY#s6C#gALw1`uHDpzH^M)!} zU;eG-O8qdsV!Xh9vR7kCQ^Di)RX$c2~ob zIU~GKYeq#Q&yECJ+(opvbWB|F8uo^VuJE$O%MtQ}18MRfi}4Ca zL_uY2;6;CnTMLvf(2#i4a%5nFReAKGEprklRnnlc!`qS?@QgNjEh-$ysI>)d%zQ$0=K3I&I(#DW8Tg%O;+T=iyFu?!P$Y_aKwLw zwOkOx7U#oGP!MLq#sCt_4yY?Mw|Vwz`p}~aT|(xm>;*J9nIma4hRNl_I|lr>Tlas zao>qmakhuCBS&Z_nyBvM2h3u1bfN$p1|mH{UMCW%?4D|DoWbPRoaV8Zv`>4G!~bU7Vq%~ugWI5xC_RNvqRD49$@5|)DQ%77RZ963lb4KWSI$uvzBK3Y zdylW@8^NMmBTVgAUa}9DJ|9MqZpme&>YW0PF`5fYlrdP>L&EB6p3*Lcu6?(;~P= z-8?h~OlwQ``gFKx74Hk^4~Os=S}b&;1!Kdi;opb^lR4bk^}NL>JW;K(UcX|4mIl!@ zkh}!Dusn2ObMZ{2p0Vq1-Z0@~#MR9349O{SHD5?Z` zJsFed6AJ9u(PDav*b@5$eaw!xV<%GF3`01ao*IUfVQN!}ec9?-AU1T&+jK7@lzQM3 z&B1HeC$UM!idv%O3cwZ{LeN@Mo3}{eEk@tpY3UDsgugCsp0BOXYm=iAX$_uxAcDA} zI8$^ZqtDoCS23Au3f5_i9>-0)<}R#@qagT9LAnoLGizd%ZMbWHcxGg~KXf~ZX*)*F zfz=-cFxc?5yMY^(#M-xYKHXR3;TNloS#_6_fr%7YSAs#koPC{GhAW5uAstX-+C8&1 zZV$1!Tx5kUFG+n<5|=RrGFDMRobXT;VR8jLaaDnmTKUev2eX+w`sJoh>7CbG9%a{N zkUgQ8<}RJ>)30%TIh?6vhO=?zYU^;;bJ%Lt67YnJS3RvCP%h1T%!W%~L-b7^mH#1p z*(4ZocESp&J^WPQw5#j2MtN1LEiq078{xYB6m4HVp;rhNZ+yO>@`dsC4$}>x2^X%# z>Wy83mC39DBJpf8?=?>6Rp#UjY3*=Ck-*Kw&k9zR@seev(#?17IX6F> zWP0Ye6~h#ThPMRrp8vv_7`U;et|BOGvc0e)jX@79N4qGmyS&~y{l&B{pr6+=W zN^*5*F=3*g0|ZDa*iJlHJRIq(eKQNonQv&Rlp1jgG+N)Xst~A4uQh!4#JNysblqI4 z5lmpYlV{0pB8bMkCbI*9V#MzVh;4P$6px|U@Y*OqA+xTH%7<@9-aW_5 zGR&Fo9gA{(=)qg4*5^X1y=cJ)^$DaPXv&U!^lnu4QMj(rEQQXI%tUOrkb{-dCuq66 zoS|^7Il2~M3Y;HXCFuMXy_1feykL50t$cBONoTt=$3DW9^+6+}>AWLQ!@LhO1@`Fw@9&EB$o89zLC)nm3|oVAUrLNySo5;U?M z#|R3yT)ZZ$QhDMbWe|gCrgNxuk1`=DIBq(3BG%ppfXlPK>fgh8nnPjf3f|1}QUS&8 zB4jy2-JH^Rm~dz5{F$k%4gAP^=_CY1qpeW6o=Tt{`?IeFH z62QOG+8?`GK^L}mv@|Q6DM4Y@g|Jzib^)79x||I!&UgFVz_8zVD#lyvK^Q*mw+`Z+ z4tLfoT2UWaPA!l*xIXbB+Up7*k4LrO&7dJ74@ZBfJP(y54`tka;`)pend<^8;m)e; zG|ejC)Hg!{ar}a^&|dL%mPt#;CN)1)%GIu>31hgkF+{gIiFLHwI&rFJkS6=^Ztd9m zDr-37eQC!WFuzeW*Zn+hn=f3V;u`LBT1^dUI#?>a)o)3*l&w`^X!(IEu);;t=`ih{ zGx$P={8$PHfybhXX_3flV8rRQ?P{*Oj61VpT|4IFZXRm`jt%04B{n;Bf-ML!B6l6_ zWO7Q%iWo)K6daAJ6qkA|kNT?dNmLg3;q=1jreNk}Xp1$ycf+eyG1r7XB z^X4G(6MoDhkpUn!CMngy91}X;1vGgmUyLq2i`!GL&sseFEbmUnHdl<=86r z(2@5dmPm4a83T$n)x!%5KGwGs)>B2F84G}_=P(@$Y3s|BIp&sijQ(*H7W6r(IBDC(bw9IM*YUfgm5lUNLu?y;O=e`**VnNsN zD3FoCfi~68W9+3SbTbw$*%B+;t##LY*u!~T^axb-VWSnsbrW`iuFOKU=jIg^MUQ*s zy8joR)KN$z`BZRI__zEfUz;}zUL$F8q~Ai0Ao&?b2FJ!+m9C^7Tj!qVEnd5K>+DpY z>L?R%M?i#?+@oO211(sF~^_Q^{W%77k?_ZkQTjP4i$E|SYThS8v#O-4VEjKeQKe$Vo z3S7aa@^Flla=9Yr-mW3rR~;1{9`@8d8WO6{Z(5x#V%0)88LMJAITxWZWV&XQ8-+wP ztv#|OuTk#1HNUO`6F=>L}F=5A{fd23LWSb|uer~j+e zlpSv+3ItAs9e+Yxj!ZXLHu%GRB4^N3=nwJT!1eoU^<+->=Y9482Z6Gn7G z*7%^8^UnB)huM{s;VrLi!Mb&L(de!~MdC-oTjO{AvEEw0klcjGqz_Sc$X#3WXT9TG zNXM0vur|)-7tax>RSED54BAk0APs8_Q|d73poyk?kA_FHQZ&pWu7fApTI3^2)W3}-edH(wW15# z2e^ryZ=K<|rl-2jxo$Xi83(Ne?(L&GW%;Ap=g9)Y8*y1`Guu5#VV+%VoZR_E=J$5J zRRn2rEN;i1$)uBn0*OEcy%IuvVBl4=$ewf^&Amn-OSKwFj?Y+_v{(>X^t;$}cnDf# z1nm}_S17C#2E*7VbH*pO{4%_y7okCHTZF!t*-BoB&C*hqr%%(p$`Ea5Xu!^qyxyP_ zcfZ)FIbgsjdwFusj50yGC`lQHBb%t%DgcMW$zm3DI5@dXtZT{^3$QerebPE25ER^5 zTEbf*qVO;H)p!BJ#tz4Y*o2j*PEOBT?N#;i(;}(+Z9cZ^Xe)B}k)Z0Gc6`>&yC$lc zN7mxfk~Y#SM$($Oajy`gEvhr2X<_4RcPgE7n;w`O&8z(?!sa+|dkDQT{n5RoxFlt_ z^%(xad6NaP-z`7`8MOLqy4MXOEr1hg0c^jrHAj1Eb5myyQ+qoLt9!_p(caGGmdZ^c z2}oF6&xQAjXnRIrV|G@<9kc5$v3z ztM_r+{HE(n>2t@)F8Iz{o{`g+sY9a48F&y#kw?JYej{&qebceW{>k3_xa&(*)*2N{-mdCBeTHdDz;4ElP3t)2hRv;oTd6w+%ap^Fv^Ay+?I;n-l?!5_ zB_mMiP*RSQu#Skb?oF&vk&ODHtJ9y3Pw&$e=Mx3ow`Rbu_0JriCslK~;+fIaUap3% z8>WNO!%0Un9%4?ZL_kz10U^jkr$SWF;e}LM5@B`Tw=0N+BQpAz-7391ymD+gj1s!d zJFOI?((yZ{G$G)$aLYadUHVUwdH>n|DDb>F&GX4NPC^9B#)t~v@SdR3!k z;j?sz+bfKM)r9>0JKf@hJZX(8O2NTTaT;^4LchLs((yY& z417M_8}B4GdzXtk)A_l9*Fa52nY|WE_TwQ|&+!R%Oo(~_?btJEh)GgGS$>^FZ#!Qa z4O$TJ3Gd#xe5$EiY`sh_gJc9XrdxZYBY#6bj;cFJJT+A_W>-M_=G15GpaQ`c3Z`up zA<1BHOt3PG<{=~+3^TmvhMYYhWhv; z!sjm}N;UM##`f?P2#2c{q(EjK=AW+X80hNg25wuYqeKXkFx*6!Z^Y>rH__c9Mvcsx zseNOa)rWavrC7@M8u6+`Kz{q_r#$8BS(_)$xZwE6%97Q0Is0wDo5e+fcEcCUg@(+% zu_GJLKQIdiM=MujXYnNvlHAZHTj&L8Zk029G6@&6u)VgtpCjqQ%kud zdtRc5*C1@#Q(;rassbK4HG8nk=KN;)RaZOLe#!~F^|?hCmme^RiZ%nLC_V@%R>)ht zHH5ymVga5jet+vp*?z`OTewnE^KI`|`LSa~=`4Ak>rr7=xr;Fi7}XfIcGGc`*=19v zaS(9fVz7HXQGcv*+d+n+7b=E-xSY3c3m& z=?qp;x?_v3cljkT;io}OKA-B6=|Ko`)0_iwIkKt*5Y*4Dy=sILOz}2tt`v~w1{#yK z`@5kYqPflBc2~EwHDM%2?%()*RxfhL_#1(_waD3QL$+UKxty$=&25b@^2hde48tv; zPA=k*h1p_eGMVIAJ%UiRD01cGL}|81^PC*nDN-&hLLe$)G;nhgpX)w}jf?Q=5>>i! z{{uB(b!ZW;BI)XKYwc<5EJtkbA=U8K_EYMvW$={3SC6u)<9NHgDff5`J|O{H)7&zX zcYbg#IteR&fKX=Z%vNLie4=KCB7<(&F0^y#tDkrQ?!*{?Y1EctSPh|8Kn+aeu@)ox zgbnt|f<`=^&yXJtFk#L4N-?gK^S|8Ozsn4|I9u8Lo*Bdh@Z5ZO>#gy}AuFU7>LNF3 zWNPoHrGoE$FV4tm)7KESLrz-y<8CU0d;w@;2FmShvT3$kX=)qaIxQyDajfpP?P0(0DGs9e>7F*P?{dWvcw zqaVkC@14^2)I9gvRxMl2fA!i#8q5<bijR6KW4$p?|ROr>PC{5oMJgj%N!$w()0FP&-M@xn6JHnVnujCTLZNuKkBgt~&B zoP=H7hU8UeG`0uD^ed)g6zf&{>S4x z>1V~DA0%32Cq88RSwZQ4rJ>8n5f@jJexY9@|6KaHjBNS^^1lV3OCkb*6~b4qz5R)Z zKdb*-<}o7s{!GO0JbSy#5?TbRQp5wMe@Xmv$>lPU0cn1}B3=|=E)$<-Mzv@A{z}Hb z+8TZkFBE1RS@!uE@p~EIGEdgNQ{d-n^Rq(3WgY?Q-|@be9sV8luZj~tsI*8aHUA;? zN2%g6y^6LFwg0E9`rTA6^!AVOyOFHb$pkD;(x;rS3m^QkUUb;J=6eg%W{wr7EvXpGdKN { + console.log('openBluetoothAdapter success', res) + wx.showToast({ + title: '蓝牙搜索中', + icon: "none" + }) + this.startBluetoothDevicesDiscovery() + }, + fail: (res) => { + if (res.errCode === 10001) { + wx.showToast({ + title: '请打开蓝牙', + icon: "none" + }) + // 监听本机蓝牙状态变化的事件 + wx.onBluetoothAdapterStateChange((res) => { + console.log('onBluetoothAdapterStateChange', res) + if (res.available) { + this.startBluetoothDevicesDiscovery() + } + }) + } + } + }) + }, + + // 开始搜寻附近的蓝牙外围设备 + startBluetoothDevicesDiscovery() { + if (this._discoveryStarted) { + return + } + this._discoveryStarted = true + wx.startBluetoothDevicesDiscovery({ + allowDuplicatesKey: true, + interval: 1000, //上报设备的间隔 + services: [ + "FFE0", + ], + success: (res) => { + console.log('startBluetoothDevicesDiscovery success', res) + this.onBluetoothDeviceFound() + }, + }) + }, + // 停止搜寻附近的蓝牙外围设备 + stopBluetoothDevicesDiscovery() { + wx.stopBluetoothDevicesDiscovery() + }, + + // 找到新设备的事件 + onBluetoothDeviceFound() { + wx.onBluetoothDeviceFound((res) => { + res.devices.forEach(device => { + if (!device.name && !device.localName) { + return + } + if (device.name.indexOf('AiLink_') != -1) { + wx.stopBluetoothDevicesDiscovery() //搜索到名称为“AiLink_”的蓝牙后,停止搜寻附近的蓝牙 + const foundDevices = this.data.devices + const idx = inArray(foundDevices, 'deviceId', device.deviceId) + const data = {} + let buff = device.advertisData.slice(-6) + device.mac = new Uint8Array(buff) // 保存广播数据中的mac地址,这是由于iOS不直接返回mac地址 + let tempMac = Array.from(device.mac) + tempMac.reverse() + device.macAddr = ab2hex(tempMac, ':').toUpperCase() + if (idx === -1) { + data[`devices[${foundDevices.length}]`] = device + } else { + data[`devices[${idx}]`] = device + } + this.setData(data) + } + }) + }) + }, + // 连接低功耗蓝牙设备 + createBLEConnection(e) { + wx.showLoading({ + title: '蓝牙连接中', + }) + const ds = e.currentTarget.dataset + const index = ds.index + this._device = this.data.devices[index] + const deviceId = ds.deviceId + const name = ds.name + this.mac = ds.mac + wx.createBLEConnection({ + deviceId, + success: (res) => { + this.setData({ + connected: true, + name, + deviceId, + }) + console.log("createBLEConnection:success") + this.onBLEConnectionStateChange() + this.getBLEDeviceServices(deviceId) + }, + fail: res => { + wx.hideLoading() + wx.showToast({ + title: '连接失败', + icon: 'none' + }) + } + }) + }, + //监听蓝牙连接状态 + onBLEConnectionStateChange() { + wx.onBLEConnectionStateChange((res) => { + if (!res.connected) { + setTimeout(() => { + wx.showToast({ + title: '连接已断开', + icon: 'none' + }) + }, 500) + this.setData({ + connected: false, + devices: [], + weight: "", + text: "", + imp2: '', + imp3: '', + imp4: '', + imp5: '', + imp7: '', + }) + } + }) + }, + + // 获取蓝牙设备的 serviceId + getBLEDeviceServices(deviceId) { + wx.getBLEDeviceServices({ + deviceId, + success: (res) => { + for (let i = 0; i < res.services.length; i++) { + if (res.services[i].isPrimary && res.services[i].uuid.indexOf('FFE0') > -1) { + wx.showLoading({ + title: '获取设备的UUID成功', + }) + this.getBLEDeviceCharacteristics(deviceId, res.services[i].uuid) + return + } + } + } + }) + }, + + // 获取蓝牙设备某个服务中所有特征值(characteristic) + /** + * read: true读,write: true写,notify: true广播 + */ + getBLEDeviceCharacteristics(deviceId, serviceId) { + let that = this + that._deviceId = deviceId + that._serviceId = serviceId + that._device.serviceId = serviceId + wx.hideLoading() + wx.getBLEDeviceCharacteristics({ + deviceId, + serviceId, + success: (res) => { + console.log('getBLEDeviceCharacteristics success', res.characteristics) + for (let i = 0; i < res.characteristics.length; i++) { + let item = res.characteristics[i]; + if (item.uuid.indexOf('0000FFE1') != -1) { + that.data.uuid1 = item.uuid + } else if (item.uuid.indexOf('0000FFE2') != -1) { + that.data.uuid2 = item.uuid + } else if (item.uuid.indexOf('0000FFE3') != -1) { + that.data.uuid3 = item.uuid + // 取消加密和握手 + let str = "A603420100466A" + let buf = new Uint8Array(str.match(/[\da-f]{2}/gi).map(function(h) { + return parseInt(h, 16) + })) + console.log("buffer", j, str, buf) + wx.writeBLECharacteristicValue({ + deviceId: deviceId, + serviceId: serviceId, + characteristicId: item.uuid, + value: buf.buffer, + success: res => { + console.log('下发指令成功', res.errMsg) + }, + fail: res => { + console.log("下发指令失败", res); + }, + }) + } + } + wx.notifyBLECharacteristicValueChange({ + deviceId, + serviceId, + characteristicId: that.data.uuid2, + state: true, + }) + wx.notifyBLECharacteristicValueChange({ + deviceId, + serviceId, + characteristicId: that.data.uuid3, + state: true, + }) + + wx.onBLECharacteristicValueChange((characteristic) => { + console.log("characteristic", ab2hex(characteristic.value, "")) + let dw1 = "kg" + let payload = ab2hex(characteristic.value, '') + let type = payload.substring(8, 10) + let typeInfo = payload.substring(10, 12) + console.log("测量开始:", payload, type) + if (type == "01") { //体脂模式 + let data = parseInt(payload.substring(12, 18), 16) + let num = payload.substring(18, 19) + let dw = payload.substring(19, 20) + if (dw == "1") { + dw1 = "斤" + } + if (dw == "4") { + dw1 = "st" + } + if (dw == "6") { + dw1 = "lb" + } + if (num == "1") { + data = data / 10 + } + if (num == "2") { + data = data / 100 + } + if (num == "3") { + data = data / 1000 + } + if (typeInfo == "01") { + if (dw1 == "st") { + let s = data / 14 + let y = data - Math.trunc(s) * 14 + let y0 = y >= 10 ? Math.trunc(y) : y.toFixed(1) + let data0 = Math.trunc(s) + ':' + y0 + console.log("st:lb", s, y, data, data0) + that.setData({ + weight: "实时体重是:" + data0 + dw1 + }) + } else { + that.setData({ + weight: "实时体重是:" + data + dw1 + }) + } + + } + if (typeInfo == "02") { + if (dw1 == "st") { + let s = data / 14 + let y = data - Math.trunc(s) * 14 + let y0 = y >= 10 ? Math.trunc(y) : y.toFixed(1) + let data0 = Math.trunc(s) + ':' + y0 + that.setData({ + weight: "稳定体重是:" + data0 + dw1 + }) + } else { + that.setData({ + weight: "稳定体重是:" + data + dw1 + }) + } + } + } + if (type == "02") { //阻抗 + if (typeInfo == "02") { + that.setData({ + imp7: "阻抗测量失败" + }) + } + if (typeInfo == "03") { + let mcu = payload.substring(12, 14) + let imp = parseInt(payload.substring(14, 22), 16) + if (mcu == "02") { + that.setData({ + imp2: "左手阻抗:" + imp + }) + } + if (mcu == "03") { + that.setData({ + imp3: "右手阻抗:" + imp + }) + } + if (mcu == "04") { + that.setData({ + imp4: "左脚阻抗:" + imp + }) + } + if (mcu == "05") { + that.setData({ + imp5: "右脚阻抗:" + imp + }) + } + + if (mcu == "07") { + that.setData({ + imp7: "躯干阻抗:" + imp + }) + } + } + } + if (type == "0f") { + that.setData({ + text: "测量完成" + }) + } + + }) + + + }, + fail(res) { + console.error('getBLEDeviceCharacteristics', res) + } + }) + }, + /** + * 断开蓝牙模块 + */ + closeBluetoothAdapter() { + wx.closeBluetoothAdapter() + this._discoveryStarted = false + wx.showToast({ + title: '断开蓝牙模块', + icon: 'none' + }) + this.setData({ + devices: [], + weight: "", + text: "", + imp2: '', + imp3: '', + imp4: '', + imp5: '', + imp7: '', + }) + }, + // 断开与低功耗蓝牙设备的连接 + closeBLEConnection() { + wx.closeBLEConnection({ + deviceId: this._deviceId + }) + wx.showToast({ + title: '断开蓝牙连接', + icon: 'none' + }) + this.setData({ + connected: false, + devices: [], + weight: "", + text: "", + imp2: '', + imp3: '', + imp4: '', + imp5: '', + imp7: '', + }) + }, + +}); \ No newline at end of file diff --git a/bluetooth_demo_L08_NSDK/pages/index/index.json b/bluetooth_demo_L08_NSDK/pages/index/index.json new file mode 100644 index 0000000..b55b5a2 --- /dev/null +++ b/bluetooth_demo_L08_NSDK/pages/index/index.json @@ -0,0 +1,4 @@ +{ + "usingComponents": { + } +} \ No newline at end of file diff --git a/bluetooth_demo_L08_NSDK/pages/index/index.wxml b/bluetooth_demo_L08_NSDK/pages/index/index.wxml new file mode 100644 index 0000000..b847b4f --- /dev/null +++ b/bluetooth_demo_L08_NSDK/pages/index/index.wxml @@ -0,0 +1,46 @@ + +module.exports.max = function(n1, n2) { + return Math.max(n1, n2) +} +module.exports.len = function(arr) { + arr = arr || [] + return arr.length +} + + + + + + + + + + {{weight}} + {{text}} + {{imp2}} + {{imp3}} + {{imp4}} + {{imp5}} + {{imp7}} + + 已发现 {{devices.length}} 个外围设备: + + + + {{item.name}} + (信号强度: {{item.RSSI}}dBm) + + mac地址: {{item.macAddr || item.deviceId}} + + + + + + diff --git a/bluetooth_demo_L08_NSDK/project.config.json b/bluetooth_demo_L08_NSDK/project.config.json new file mode 100644 index 0000000..7c29d97 --- /dev/null +++ b/bluetooth_demo_L08_NSDK/project.config.json @@ -0,0 +1,57 @@ +{ + "description": "项目配置文件,详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html", + "setting": { + "urlCheck": true, + "es6": false, + "enhance": true, + "postcss": true, + "preloadBackgroundData": false, + "minified": true, + "newFeature": false, + "coverView": true, + "nodeModules": false, + "autoAudits": false, + "showShadowRootInWxmlPanel": true, + "scopeDataCheck": false, + "uglifyFileName": false, + "checkInvalidKey": true, + "checkSiteMap": true, + "uploadWithSourceMap": true, + "compileHotReLoad": false, + "lazyloadPlaceholderEnable": false, + "useMultiFrameRuntime": true, + "useApiHook": true, + "useApiHostProcess": true, + "babelSetting": { + "ignore": [], + "disablePlugins": [], + "outputPath": "" + }, + "useIsolateContext": true, + "userConfirmedBundleSwitch": false, + "packNpmManually": false, + "packNpmRelationList": [], + "minifyWXSS": true, + "disableUseStrict": false, + "minifyWXML": true, + "showES6CompileOption": false, + "useCompilerPlugins": false, + "ignoreUploadUnusedFiles": true, + "condition": false + }, + "compileType": "miniprogram", + "simulatorType": "wechat", + "simulatorPluginLibVersion": {}, + "projectname": "bluetooth_demo_L08", + "libVersion": "2.23.2", + "appid": "wxcea3504a31518eb6", + "editorSetting": { + "tabIndent": "insertSpaces", + "tabSize": 2 + }, + "condition": {}, + "packOptions": { + "ignore": [], + "include": [] + } +} \ No newline at end of file diff --git a/bluetooth_demo_L08_NSDK/project.private.config.json b/bluetooth_demo_L08_NSDK/project.private.config.json new file mode 100644 index 0000000..41aae2b --- /dev/null +++ b/bluetooth_demo_L08_NSDK/project.private.config.json @@ -0,0 +1,4 @@ +{ + "description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html", + "projectname": "bluetooth_demo_L08" +} \ No newline at end of file diff --git a/bluetooth_demo_L08_NSDK/sitemap.json b/bluetooth_demo_L08_NSDK/sitemap.json new file mode 100644 index 0000000..ca02add --- /dev/null +++ b/bluetooth_demo_L08_NSDK/sitemap.json @@ -0,0 +1,7 @@ +{ + "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html", + "rules": [{ + "action": "allow", + "page": "*" + }] +} \ No newline at end of file diff --git a/bluetooth_demo_L08_NSDK/utils/util.js b/bluetooth_demo_L08_NSDK/utils/util.js new file mode 100644 index 0000000..7aafdf3 --- /dev/null +++ b/bluetooth_demo_L08_NSDK/utils/util.js @@ -0,0 +1,30 @@ +function inArray(arr, key, val) { + if (!arr || !arr.length || typeof arr != 'object' || !Array.isArray(arr)) { + return -1 + } + for (let i = 0; i < arr.length; i++) { + if (!key) { + if (arr[i] == val) { + return i + } + } else if (arr[i][key] === val) { + return i + } + } + return -1; +} + +// ArrayBuffer转16进度字符串示例 +function ab2hex(buffer, split) { + var hexArr = Array.prototype.map.call( + new Uint8Array(buffer), + function(bit) { + return ('00' + bit.toString(16)).slice(-2) + } + ) + return hexArr.join(split); +} +module.exports = { + inArray, + ab2hex, +}