简单的蓝牙显示(串口手动发数据测试)
最近搞了这方面的东西,是刚刚开始接触微信小程序,因为是刚刚开始接触蓝牙设备,所以这篇文章适合既不熟悉小程序,又不熟悉蓝牙的新手看。
项目要求是获取到蓝牙传输过来的数据,并显示成图表实时显示;
我看了很多网上的文章,也鼓捣了很长时间ChatGPT,最后弄出来了,其中出现了很多坑,在这里分享一下;
我想了一下这个文章还是写在CSDN,我知道这个平台有点拉,广告多,但毕竟这个平台普及度高,我希望这篇文章能帮到更多的人,至于什么关注看文章,收费!收NMLGB的费!
首先,微信开发者工具一些简单的配置我就不多说了,先说一些坑的地方;
当我刚刚准备在微信小程序搞蓝牙的是时候,当然是先去翻微信的官方文档,官方文档还是很不错的,给了一个蓝牙项目例子,运行起来很丝滑;
蓝牙 (Bluetooth) | 微信开放文档
在这个文档的最后会有一个代码示例:
我下载下来直接用,可以获取到数据,这个示例里提供了一个16进制显示的函数;
其中获取数据的地方是这里:
getBLEDeviceCharacteristics(deviceId, serviceId) { 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.properties.read) { wx.readBLECharacteristicValue({ deviceId, serviceId, characteristicId: item.uuid, }) } if (item.properties.write) { this.setData({ canWrite: true }) this._deviceId = deviceId this._serviceId = serviceId this._characteristicId = item.uuid this.writeBLECharacteristicValue() } if (item.properties.notify || item.properties.indicate) { wx.notifyBLECharacteristicValueChange({ deviceId, serviceId, characteristicId: item.uuid, state: true, }) } } }, fail(res) { console.error('getBLEDeviceCharacteristics', res) } }) // 操作之前先监听,保证第一时间获取数据 wx.onBLECharacteristicValueChange((characteristic) => { const idx = inArray(this.data.chs, 'uuid', characteristic.characteristicId) const data = {} if (idx === -1) { data[`chs[${this.data.chs.length}]`] = { uuid: characteristic.characteristicId, // value: ab2hex(characteristic.value) //转16进制 value: toASCII(characteristic.value) } } else { data[`chs[${idx}]`] = { uuid: characteristic.characteristicId, value: toASCII(characteristic.value) } } this.setData(data); // 图表刷新 this.Refresh2(dataGenerator(this.data.bleDataList01, this.data.chs[0].value, 50)); }) }, })
有点长,监听数据变化并获取数据的仅仅是 wx.onBLECharacteristicValueChange((characteristic)这部分;
其中这里的data对象获取到的值是这样一个东西:
它会将每个特征值和其绑定的对象值在这里组成一个单个对象。每次获取到蓝牙对象的时候在这里刷新,所以我只要将数据获取并刷新图表的函数卸载这里就行了,也不用写什么定时刷新。
我发现一个问题,这个示例代码跑真机测试经常经常经常连不上,连的我怀疑人生,这样我Log大法输出的变量和任何报错都看不到!
最后还是新建了一个项目,使用这个示例程序的代码重新写才稳定的连接到手机,诸位如果和我一样,那我们真是难兄难弟……
OK,现在回到重构好的之后的程序。
硬件的蓝牙芯片是沁恒,型号是CH9141,这个是他们官网的下载资源:
搜索 CH9141 - 南京沁恒微电子股份有限公司 (wch.cn)
他们提供了Android的串口助手,在电脑端我用的Windows商店的串口工具:
感觉确实比网上的野鸡串口工具好用。
我的的硬件是低功耗蓝牙,这里连接后会有很多uuid,甚至这些uuid还分组,这是用Android串口工具看到的数据:
左边的uuid才是我要的。
我也不是搞硬件的,只能摸索代码,最后发现是这里:
getBLEDeviceServices(deviceId) { wx.getBLEDeviceServices({ deviceId, success: (res) => { console.log(res); //这里的services[1]就是定位分组的位置 this.getBLEDeviceCharacteristics(deviceId, res.services[1].uuid) return } }) },
这里我的低功耗蓝牙硬件是有多个服务,然后对应的,我要的服务在这里数组中的位置是1:
可以看一下和上面对的上,然后在这个服务里又有两个uuid,我只要第一个(具体服务具体对应),所以我在第一个代码块那里才会有 this.data.chs[0].value这种写法;
所以对接蓝牙数据的时候各位先打印一下这里的services,然后改成自己需要的uuid。
Refresh2(dataLists) { const chart = this.chart; if (chart) { chart.setOption({ series: [{ data: dataLists }] }); console.log('完成刷新'); } },
最开始我按照ECharts官网的小程序代码,把ECharts的初始化代码写在了Page对象外面,这样出现了一个问题,我在Page外部不能使用this来吧我声明好的chart对象保存到Page中;
所以改造了一下写法:
Page({ data: { motto: 'Hello World', devices: [], connected: false, chs: [], bleDataList01: [], bleDataList02: [], ec: { onInit: null, }, option: option, }, onLoad() { this.chart = null; // 保存图表实例 this.setData({ ec: { onInit: this.initChart } }); }, initChart(canvas, width, height, dpr) { this.chart = echarts.init(canvas, null, { width: width, height: height, devicePixelRatio: dpr // 像素比 }); canvas.setChart(this.chart); this.chart.setOption(this.data.option); return this.chart; }, })
option是定义在Page外部的ECharts样式变量,所有代码都是写在index.js文件里的,
这样就能保证我在Page内部写ECharts初始化函数,又不用ECharts懒加载了;
最后写一下数据刷新函数,其调用是这样的:
// 图表数据填充 const dataGenerator = (dataList, data, xLength) => { if (data != "") { dataList.push(Number(data)); } if (dataList.length === xLength) { dataList.shift() } return dataList; }; //这里的数据刷新是写在Page内部的 Refresh(dataLists) { const chart = this.chart; if (chart) { chart.setOption({ series: [{ data: dataLists }] }); console.log('完成刷新'); } }, // 图表刷新,在 wx.onBLECharacteristicValueChange中调用,因为是写在Page内部的,所以前面带过this this.Refresh(dataGenerator(this.data.bleDataList01, this.data.chs[0].value, 50));
整体代码是这样的:
import * as echarts from '../components/ec-canvas/echarts'; var option = { title: { text: '蓝牙对接数据图表', left: 'center' }, legend: { data: ['测试数据'], top: 50, left: 'center', z: 100 }, grid: { containLabel: true }, tooltip: { show: true, trigger: 'axis' }, xAxis: { type: 'category', boundaryGap: true, }, yAxis: { x: 'center', type: 'value', }, series: [{ name: '测试数据', type: 'line', smooth: true, data: [] }, ] }; function inArray(arr, key, val) { for (let i = 0; i < arr.length; i++) { 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(''); } function toASCII(buffer) { return String.fromCharCode.apply(null, new Uint8Array(buffer)); }; // 图表数据填充 const dataGenerator = (dataList, data, xLength) => { if (data != "") { dataList.push(Number(data)); } if (dataList.length === xLength) { dataList.shift() } return dataList; }; Page({ data: { motto: 'Hello World', devices: [], connected: false, chs: [], bleDataList01: [], bleDataList02: [], ec: { onInit: null, }, option: option, }, onLoad() { this.chart = null; // 保存图表实例 this.setData({ ec: { onInit: this.initChart } }); }, Refresh(dataLists) { const chart = this.chart; if (chart) { chart.setOption({ series: [{ data: dataLists }] }); console.log('完成刷新'); } }, initChart(canvas, width, height, dpr) { this.chart = echarts.init(canvas, null, { width: width, height: height, devicePixelRatio: dpr // 像素比 }); canvas.setChart(this.chart); this.chart.setOption(this.data.option); return this.chart; }, openBluetoothAdapter() { wx.openBluetoothAdapter({ success: (res) => { console.log('openBluetoothAdapter success', res) this.startBluetoothDevicesDiscovery() }, fail: (res) => { if (res.errCode === 10001) { wx.onBluetoothAdapterStateChange(function (res) { console.log('onBluetoothAdapterStateChange', res) if (res.available) { this.startBluetoothDevicesDiscovery() } }) } } }) }, getBluetoothAdapterState() { wx.getBluetoothAdapterState({ success: (res) => { console.log('getBluetoothAdapterState', res) if (res.discovering) { this.onBluetoothDeviceFound() } else if (res.available) { this.startBluetoothDevicesDiscovery() } } }) }, startBluetoothDevicesDiscovery() { if (this._discoveryStarted) { return } this._discoveryStarted = true wx.startBluetoothDevicesDiscovery({ allowDuplicatesKey: true, 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 } const foundDevices = this.data.devices const idx = inArray(foundDevices, 'deviceId', device.deviceId) const data = {} if (idx === -1) { data[`devices[${foundDevices.length}]`] = device } else { data[`devices[${idx}]`] = device } this.setData(data) }) }) }, createBLEConnection(e) { const ds = e.currentTarget.dataset const deviceId = ds.deviceId const name = ds.name wx.createBLEConnection({ deviceId, success: (res) => { this.setData({ connected: true, name, deviceId, }) this.getBLEDeviceServices(deviceId) } }) this.stopBluetoothDevicesDiscovery() }, closeBLEConnection() { wx.closeBLEConnection({ deviceId: this.data.deviceId }) this.setData({ connected: false, chs: [], canWrite: false, bleDataList01: [], bleDataList02: [], }); //断开连接的时候清理图表数据 if (this.chart) { this.chart.setOption({ series: [{ data: [] }] }); } console.log('Bluetooth connection closed and data cleared'); }, getBLEDeviceServices(deviceId) { wx.getBLEDeviceServices({ deviceId, success: (res) => { console.log(res); //这里的services[1]就是定位分组的位置 this.getBLEDeviceCharacteristics(deviceId, res.services[1].uuid) return } }) }, getBLEDeviceCharacteristics(deviceId, serviceId) { 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.properties.read) { wx.readBLECharacteristicValue({ deviceId, serviceId, characteristicId: item.uuid, }) } if (item.properties.write) { this.setData({ canWrite: true }) this._deviceId = deviceId this._serviceId = serviceId this._characteristicId = item.uuid this.writeBLECharacteristicValue() } if (item.properties.notify || item.properties.indicate) { wx.notifyBLECharacteristicValueChange({ deviceId, serviceId, characteristicId: item.uuid, state: true, }) } } }, fail(res) { console.error('getBLEDeviceCharacteristics', res) } }) // 操作之前先监听,保证第一时间获取数据 wx.onBLECharacteristicValueChange((characteristic) => { const idx = inArray(this.data.chs, 'uuid', characteristic.characteristicId) const data = {} if (idx === -1) { data[`chs[${this.data.chs.length}]`] = { uuid: characteristic.characteristicId, // value: ab2hex(characteristic.value) //转16进制 value: toASCII(characteristic.value) } } else { data[`chs[${idx}]`] = { uuid: characteristic.characteristicId, value: toASCII(characteristic.value) } } this.setData(data); // 图表刷新 this.Refresh(dataGenerator(this.data.bleDataList01, this.data.chs[0].value, 50)); }) }, writeBLECharacteristicValue() { // 向蓝牙设备发送一个0x00的16进制数据 let buffer = new ArrayBuffer(1) let dataView = new DataView(buffer) dataView.setUint8(0, Math.random() * 255 | 0) wx.writeBLECharacteristicValue({ deviceId: this._deviceId, serviceId: this._deviceId, characteristicId: this._characteristicId, value: buffer, }) }, closeBluetoothAdapter() { wx.closeBluetoothAdapter() this._discoveryStarted = false }, })
再说一下,这些代码都是写在index.js文件里的,
这个是项目代码:GitHub - DingAi/WeChatProject-EchartsBluetooth: 一个微信小程序,用Echarts实时显示蓝牙数据
蓝牙数据缓冲区
以上是一些蓝牙的渐渐调试,在次之后我又对接了真实蓝牙设备,新的问题出现了,发过来的串并没有什么中断规律,虽然是能连在一块,但是从串的哪里间断完全不知道,例如:
FE01A1455E34FFFFFF,这个是我的16进制数据,FE01开头,FFFFFF结尾,中间是数据,它发过来可能是完整的,也可能是55E34FFFFFFFE01E136,也可能是FF0,也可能的FFFFFF……
所以只能写个缓冲区,然后冲缓冲区里识别数据帧取数据,这里用的是字符串对象来做缓冲区:
extractFirstFrame(buffer) { // 找到第一个 "fe01" 的位置 let startIndex = buffer.indexOf("fe01"); // 如果找不到 "fe01",直接返回空字符串 if (startIndex === -1) { return ""; } // 从 "fe01" 开始查找 "ffffff" let endIndex = buffer.indexOf("ffffff", startIndex); // 如果找不到 "ffffff",直接返回空字符串 if (endIndex === -1) { return ""; } // 提取完整的数据帧 let frame = buffer.substring(startIndex, endIndex + 6); // 更新缓冲区,移除已处理的部分 buffer = buffer.substring(endIndex + 6); return { frame, buffer }; },
这里的缓冲区处理其实很简单,诸位可以考虑将其改成批量处理的形式,我也写了一点会在后面的代码中贴出来,但是我还没有写完批量处理,诸位也就看看。
缓冲区对象我直接在Page的data中定义了:
Page({ data: { devices: [], connected: false, chs: [], bleDataList01: [], bleDataList02: [], ec: { onInit: null, }, option: option, Buffer: "", display: [], },
在这里说一下,因为是字符串缓冲区,所以一定要注意十六进制的大小写问题,我就在这里卡了一下
缓冲区的逻辑是将数据存链接到Buffer字符串后面,逐渐累加,然后由程序判断取出一帧数据,将其中的数据取出来翻译成数字(这里的翻译和硬件人员对接一下)
取出来最前面的数据帧之后,把数据帧分析一下,我这里数据帧里包含了4个数据:
function hexStringToFloats(hexStr) { // if (hexStr.length !== 16) { // throw new Error("Hex string must be exactly 16 characters long"); // } // 拆分成四个部分,每部分两个字节(16 位) const intParts = [ hexStr.substring(4, 8), hexStr.substring(8, 12), hexStr.substring(12, 16), hexStr.substring(16, 20), ]; // 将每部分的 16 进制字符串转换成整数 const intArray = intParts.map(part => parseInt(part, 16)); // 将每个整数除以 100,还原成浮点数 const floatArray = intArray.map(intValue => intValue / 100); return floatArray; }
最后干脆直接将数据处理整合到一个函数:
handleFrames() { const { frame, buffer } = this.extractFirstFrame(this.data.Buffer); // 提取帧数据并更新缓冲区 const value = hexStringToFloats(frame); // 从帧数据中提取后两位数据并转换为浮点数 const [dataList1, dataList2] = this.dataGenerator(this.data.bleDataList01, this.data.bleDataList02, value, 20); // 使用dataGenerator处理数据,最后一个数值是 this.setData({ Buffer: buffer }); // 更新缓冲区 this.Refresh([dataList1, dataList2]); // 更新图表 // console.log(this.extractFrames(this.data.Buffer)); },
整函数在前面说的获取到data并且可以做数据刷新的地方调用一下:
// 操作之前先监听,保证第一时间获取数据 wx.onBLECharacteristicValueChange((characteristic) => { const idx = inArray( this.data.chs, "uuid", characteristic.characteristicId ); const data = {}; if (idx === -1) { data[`chs[${this.data.chs.length}]`] = { uuid: characteristic.characteristicId, value: ab2hex(characteristic.value), //转16进制 }; // this.setData({ // Buffer: this.data.Buffer + ab2hex(characteristic.value) // }); } else { data[`chs[${idx}]`] = { uuid: characteristic.characteristicId, value: ab2hex(characteristic.value), }; this.setData({ Buffer: this.data.Buffer + ab2hex(characteristic.value) }); } console.log(data); this.setData(data); // 从缓冲区提取帧并处理 this.handleFrames(); }); },
最后说下,硬件的数据发送速度不能太快了,要不然处理速度可能跟不上。
我发现自己写的程序并不能在公布GItHub上之后下载给别人用,和微信账号有绑定,我也是才接触微信小程序,不了解,各位就在这里看下代码(有一些函数没有使用)吧:
import * as echarts from "../components/ec-canvas/echarts"; var option = { // title: { // text: "蓝牙对接数据图表", // left: "center", // }, legend: { data: ["红外", "红"], top: 20, left: "center", z: 100, }, grid: { containLabel: true, }, tooltip: { show: true, trigger: "axis", }, xAxis: { type: "category", boundaryGap: true, }, yAxis: { x: "center", type: "value", min: function (value) { return value.min; } }, series: [{ name: "红外", type: "line", showSymbol: false, // 取消小圆点显示 data: [], }, { name: "红", type: "line", showSymbol: false, // 取消小圆点显示 data: [], }, ], dataZoom: [{ type: 'slider', // 滑动条型数据区域缩放组件 yAxisIndex: [0], // 控制 Y 轴,这里假设有两个 Y 轴 start: 0, // 左边在 10% 的位置 end: 100 // 右边在 60% 的位置 }, { type: 'inside', // 内置型数据区域缩放组件 yAxisIndex: [0], start: 0, end: 100 } ] }; function inArray(arr, key, val) { for (let i = 0; i < arr.length; i++) { 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(""); } // 从提取的帧数据中获取后两位数据并返回 function hexStringToFloats(hexStr) { // if (hexStr.length !== 16) { // throw new Error("Hex string must be exactly 16 characters long"); // } // 拆分成四个部分,每部分两个字节(16 位) const intParts = [ hexStr.substring(4, 8), hexStr.substring(8, 12), hexStr.substring(12, 16), hexStr.substring(16, 20), ]; // 将每部分的 16 进制字符串转换成整数 const intArray = intParts.map(part => parseInt(part, 16)); // 将每个整数除以 100,还原成浮点数 const floatArray = intArray.map(intValue => intValue / 100); return floatArray; } Page({ data: { devices: [], connected: false, chs: [], bleDataList01: [], bleDataList02: [], ec: { onInit: null, }, option: option, Buffer: "", display: [], }, onLoad() { this.chart = null; // 保存图表实例 this.setData({ ec: { onInit: this.initChart, }, }); }, // 从缓冲区中提取完整帧数据并更新缓冲区 extractFrames(buffer, maxFrames = 100) { const frames = []; let startIndex = 0; let endIndex; // 循环直到找到足够的帧或buffer中没有更多帧 while (frames.length < maxFrames && startIndex !== -1) { // 找到下一个"fe01"的位置 startIndex = buffer.indexOf("fe01", startIndex); // 如果找不到"fe01",跳出循环 if (startIndex === -1) break; // 从当前"fe01"开始查找"ffffff" endIndex = buffer.indexOf("ffffff", startIndex); // 如果找不到"ffffff",则尝试下一个"fe01" if (endIndex === -1) { startIndex++; // 或者你可以选择跳过一定的字节数来避免无限循环 continue; } // 提取完整的数据帧 let frame = buffer.substring(startIndex, endIndex + 6); frames.push(frame); // 将帧添加到frames数组中 // 更新startIndex为下一个可能的"fe01"位置 startIndex = endIndex + 6; } // 更新缓冲区,移除已处理的部分 buffer = buffer.substring(startIndex); return frames; }, extractFirstFrame(buffer) { // 找到第一个 "fe01" 的位置 let startIndex = buffer.indexOf("fe01"); // 如果找不到 "fe01",直接返回空字符串 if (startIndex === -1) { return ""; } // 从 "fe01" 开始查找 "ffffff" let endIndex = buffer.indexOf("ffffff", startIndex); // 如果找不到 "ffffff",直接返回空字符串 if (endIndex === -1) { return ""; } // 提取完整的数据帧 let frame = buffer.substring(startIndex, endIndex + 6); // 更新缓冲区,移除已处理的部分 buffer = buffer.substring(endIndex + 6); return { frame, buffer }; }, // 将数据添加到图表数据列表 dataGenerator(dataList1, dataList2, valueList, xLength) { // if (valueList.length > 0) { dataList1.push(valueList[0]); dataList2.push(valueList[1]); // } this.setData({ display: "心跳: " + valueList[2] + " 血氧: " + valueList[3] }) if (dataList1.length === xLength) { dataList1.shift(); dataList2.shift(); } return [dataList1, dataList2]; }, Refresh(dataList) { const chart = this.chart; if (chart) { chart.setOption({ series: [{ data: dataList[0], }, { data: dataList[1], }, ], }); } }, handleFrames() { const { frame, buffer } = this.extractFirstFrame(this.data.Buffer); // 提取帧数据并更新缓冲区 const value = hexStringToFloats(frame); // 从帧数据中提取后两位数据并转换为浮点数 const [dataList1, dataList2] = this.dataGenerator(this.data.bleDataList01, this.data.bleDataList02, value, 20); // 使用dataGenerator处理数据,最后一个数值是 this.setData({ Buffer: buffer }); // 更新缓冲区 this.Refresh([dataList1, dataList2]); // 更新图表 // console.log(this.extractFrames(this.data.Buffer)); }, initChart(canvas, width, height, dpr) { this.chart = echarts.init(canvas, null, { width: width, height: height, devicePixelRatio: dpr, // 像素比 }); canvas.setChart(this.chart); this.chart.setOption(this.data.option); return this.chart; }, openBluetoothAdapter() { wx.openBluetoothAdapter({ success: (res) => { console.log("openBluetoothAdapter success", res); this.startBluetoothDevicesDiscovery(); }, fail: (res) => { if (res.errCode === 10001) { wx.onBluetoothAdapterStateChange(function (res) { console.log("onBluetoothAdapterStateChange", res); if (res.available) { this.startBluetoothDevicesDiscovery(); } }); } }, }); }, getBluetoothAdapterState() { wx.getBluetoothAdapterState({ success: (res) => { console.log("getBluetoothAdapterState", res); if (res.discovering) { this.onBluetoothDeviceFound(); } else if (res.available) { this.startBluetoothDevicesDiscovery(); } }, }); }, startBluetoothDevicesDiscovery() { if (this._discoveryStarted) { return; } this._discoveryStarted = true; wx.startBluetoothDevicesDiscovery({ allowDuplicatesKey: true, 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; } const foundDevices = this.data.devices; const idx = inArray(foundDevices, "deviceId", device.deviceId); const data = {}; if (idx === -1) { data[`devices[${foundDevices.length}]`] = device; } else { data[`devices[${idx}]`] = device; } this.setData(data); }); }); }, createBLEConnection(e) { const ds = e.currentTarget.dataset; const deviceId = ds.deviceId; const name = ds.name; wx.createBLEConnection({ deviceId, success: (res) => { this.setData({ connected: true, name, deviceId, }); this.getBLEDeviceServices(deviceId); }, }); this.stopBluetoothDevicesDiscovery(); }, closeBLEConnection() { wx.closeBLEConnection({ deviceId: this.data.deviceId, }); this.setData({ connected: false, chs: [], canWrite: false, bleDataList01: [], bleDataList02: [], buffer: "", }); //断开连接的时候清理图表数据 if (this.chart) { this.chart.setOption({ series: [{ data: [], }, ], }); } console.log("Bluetooth connection closed and data cleared"); }, getBLEDeviceServices(deviceId) { wx.getBLEDeviceServices({ deviceId, success: (res) => { console.log(res); //这里的services[1]就是定位分组的位置 this.getBLEDeviceCharacteristics(deviceId, res.services[1].uuid); return; }, }); }, getBLEDeviceCharacteristics(deviceId, serviceId) { 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.properties.read) { wx.readBLECharacteristicValue({ deviceId, serviceId, characteristicId: item.uuid, }); } if (item.properties.write) { this.setData({ canWrite: true, }); this._deviceId = deviceId; this._serviceId = serviceId; this._characteristicId = item.uuid; this.writeBLECharacteristicValue(); } if (item.properties.notify || item.properties.indicate) { wx.notifyBLECharacteristicValueChange({ deviceId, serviceId, characteristicId: item.uuid, state: true, }); } } }, fail(res) { console.error("getBLEDeviceCharacteristics", res); }, }); // 操作之前先监听,保证第一时间获取数据 wx.onBLECharacteristicValueChange((characteristic) => { const idx = inArray( this.data.chs, "uuid", characteristic.characteristicId ); const data = {}; if (idx === -1) { data[`chs[${this.data.chs.length}]`] = { uuid: characteristic.characteristicId, value: ab2hex(characteristic.value), //转16进制 }; // this.setData({ // Buffer: this.data.Buffer + ab2hex(characteristic.value) // }); } else { data[`chs[${idx}]`] = { uuid: characteristic.characteristicId, value: ab2hex(characteristic.value), }; this.setData({ Buffer: this.data.Buffer + ab2hex(characteristic.value) }); } console.log(data); this.setData(data); // 从缓冲区提取帧并处理 this.handleFrames(); }); }, writeBLECharacteristicValue() { // 向蓝牙设备发送一个0x00的16进制数据 let buffer = new ArrayBuffer(1); let dataView = new DataView(buffer); dataView.setUint8(0, (Math.random() * 255) | 0); wx.writeBLECharacteristicValue({ deviceId: this._deviceId, serviceId: this._deviceId, characteristicId: this._characteristicId, value: buffer, }); }, closeBluetoothAdapter() { wx.closeBluetoothAdapter(); this._discoveryStarted = false; }, });
对于前端的的wxml基本没啥变化,就有一个数值显示的绑定,在这里就不贴了,我这里数据显示还是正常的:
还没有评论,来说两句吧...