1、Uni-APP蓝牙使用流程
在阅读这篇内容之前,建议您详细浏览一遍uni-app官方关于蓝牙和低功耗蓝牙(BLE)使用教程文档
uni-app官方低功耗蓝牙接口,如果您有微信小程序方面的开发经验,那您看这篇文章会很快上手,uni-app官方的蓝牙模块接口均是按照微信小程序的蓝牙模块接口编写。
思路流程
手机上打开蓝牙,设备打开蓝牙
APP初始化蓝牙模块
APP调用蓝牙搜寻设备
APP存储搜寻到的指定设备deviceId
APP根据指定deviceId连接蓝牙设备
APP连接设备之后获取设备的服务列表serviceId
APP根据serviceId获取设备的特征值列表characteristicId
APP根据指定特征值属性开启notify订阅特征值
APP根据指定特征值属性向设备发送写入控制指令
设备根据APP发送指令作出相应操作
设备通过特征值数据改变,APP的notify订阅查看改变的值
详细流程
首先要初始化蓝牙模块openBluetoothAdapter,即查看蓝牙是否可用,若初始化失败,则是蓝牙未打开,提示用户打开蓝牙。若已经打开蓝牙,则准备进行蓝牙搜索。
搜索蓝牙startBluetoothDevicesDiscovery,执行搜索方法,开始搜索蓝牙设备。这一步需要和关闭搜索蓝牙stopBluetoothDevicesDiscovery成对使用,长时间占用搜索设备,浪费资源,在查找到需要的设备连接之后需要主动去停止搜索设备。
查看所有已经发现的蓝牙设备getBluetoothDevices。在这一步可以查看到以前已经获取到的蓝牙设备deviceId。可以在这一步中查看以前已经连接到的设备,主动去尝试连接这个设备。
监听寻找到新设备的事件onBluetoothDeviceFound在这里查看搜索蓝牙已经搜索到的设备,可以在这里通过设备名称(name)或其他已知信息(deviceId等)判断是否已经搜寻到需要的指定设备。
在上两步已经获取到需要连接的设备之后,通过设备ID(deviceId)来连接低功耗蓝牙设备createBLEConnection。在这里如果APP若是已经连接过此低功耗蓝牙设备,可以直接传入之前设备ID进行尝试连接。这一步的连接操作需要和关闭连接closeBLEConnection成对操作。如果多次调用创建连接,有可能会导致系统持有一个设备的多个连接实例,导致在调用关闭连接的时候不能真正关闭连接。
在连接设备之后,APP需要主动去获取蓝牙设备getBLEDeviceServices的所有服务(services),设备会返回给APP设备的服务列表(services)包含设备服务的UUID,该服务是否为主服务。
在获取设备的服务列表之后,根据自己设备的蓝牙协议接口文档,根据指定的服务ID(serviceId)使用获取服务特征值方法getBLEDeviceCharacteristic传入两个参数设备ID(deviceId)和服务ID(serviceId)向设备获取该服务中的所有的特征值(characteristic),设备会向APP返回该服务中的所有特征值列表,列表包含设备特征值的UUID,该特征值支持的操作类型。
启用低功耗蓝牙设备特征值变化时候的notify通知功能,订阅特征值notifyBLECharacteristicValueChange。只有设备的该特征值支持notify或者indicate才可以调用此方法。在此之外,需要注意,只有调用了订阅特征值方法notifyBLECharacteristicValueChange,才能监听设备的特征值改变事件characteristicValueChange。在特征值订阅成功后,只有设备的特征值主动更新,才会触发onBLECharacteristicValueChange回调,回调会显示改变的特征值。
在订阅特征值成功之后,可以向设备写入(发送)控制命令writeBLECharacteristicValue,此方法是向低功耗蓝牙设备特征值写入二进制数据。需要注意只有该特征值的属性支持write才可以调用此方法。在此方法调用成功后,设备特征值发生改变,就会触发onBLECharacteristicValueChange回调,主动返回特征值数据。
流程图
2、Uni-APP蓝牙使用案例
使用uni-app连接血压仪
这部分代码是我测试中写的,主要是按照上面总结的思路流程写的代码,写的有些乱,还又长,就像老太太的裹脚布那样,又臭又长,有的写的不好,无意中总会创造各种各样的BUG,在实际项目中改动了好多部分代码,做了一些蓝牙使用场景的判断,尽量去融合蓝牙设备使用中遇到的各式各样的问题,尽量去提高用户体验。
代码下载连接,拷贝进uni-app项目即可https://download.csdn.net/download/qq_38734155/11195286
1. <template>
2. <view class="content">
3. <view class="" v-for="device in devicesList" :key="device">
4. <text>{{device}}</text>
5. </view>
6. <button type="primary" @tap="nextToDiscoveryBLE">下一步</button>
7. <button type="primary" @tap="pickUpOnce">测量一次</button>
8. <view class="">
9. <text>动态血压值: {{measureResult[0].pressure}}</text>
10. </view>
11. <view class="">
12. <text>收缩压值: {{measureResult[0].SYS}}</text>
13. </view>
14. <view class="">
15. <text>舒张压值: {{measureResult[0].DIA}}</text>
16. </view>
17. <view class="">
18. <text>脉搏值: {{measureResult[0].PUL}}</text>
19. </view>
20. </view>
21. </template>
22.
23. <script>
24. export default {
25. components: {
26. },
27. data() {
28. return {
29. //是否已经打开蓝牙,默认为false,当蓝牙适配器初始化成功后为true
30. isOpenBle: false,
31. //主服务的UUID
32. primaryUUID: "FFF0",
33. //设备列表
34. devicesList: [{
35. "device": "初始设备"
36. }],
37. //设备Id
38. deviceId: "",
39. //服务Id
40. serviceId: "",
41. //特征值ID
42. characteristicId: [{
43. //支持写入操作的特征值
44. writeId: "",
45. //支持notify操作的特征值
46. notifyId: ""
47. }],
48. //控制命令
49. wirteControlCode: [{
50. //开始测量命令
51. beginMeasure: "0240DD04FFFD020198",
52. //停止测量命令
53. stopMeasure: "",
54. //上传数据命令
55. uploadData: "",
56. //停止上传数据命令
57. stopUploadData: "0240DD04FFFD02059C",
58. //关机命令
59. shutDown: ""
60. }],
61. //测量结果
62. measureResult: [{
63. //动态压力值
64. pressure: "",
65. //收缩压
66. SYS: "",
67. //舒张压
68. DIA: "",
69. //脉搏
70. PUL: ""
71. }]
72. }
73. },
74. onLoad() {
75. //在页面加载时候初始化蓝牙适配器
76. uni.openBluetoothAdapter({
77. success: e => {
78. console.log('初始化蓝牙成功:' + e.errMsg);
79. this.$data.isOpenBle = true;
80. console.log(this.$data.isOpenBle);
81. },
82. fail: e => {
83. console.log('初始化蓝牙失败,错误码:' + (e.errCode || e.errMsg));
84. }
85. });
86. //同时监听蓝牙连接状态
87. this.onBLEConnectionStateChange();
88. },
89. methods: {
90. //下一步按钮
91. nextToDiscoveryBLE() {
92. //下一步去查找设备
93. this.startBluetoothDeviceDiscovery();
94. },
95. //测量一次
96. pickUpOnce() {
97. this.notifyBLECharacteristicValue();
98. let self = this;
99. setTimeout(function() {
100. self.writeBLECharacteristicValue(self.wirteControlCode[0].beginMeasure);
101. }, 500);
102. },
103. startBluetoothDeviceDiscovery() {
104. //在页面显示的时候判断是都已经初始化完成蓝牙适配器若成功,则开始查找设备
105. let self = this;
106. setTimeout(function() {
107. if (self.isOpenBle) {
108. console.log("开始搜寻智能设备");
109. // 血压仪的主服务ID为FFF0
110. uni.startBluetoothDevicesDiscovery({
111. //services: ['fff0'],
112. success: res => {
113. //
114. self.onBluetoothDeviceFound();
115. },
116. fail: res => {
117. console.log("查找设备失败!");
118. uni.showToast({
119. icon: "none",
120. title: "查找设备失败!",
121. duration: 3000
122. })
123. }
124. });
125. } else {
126. console.log("未初始化蓝牙是配饰器:" + self.isOpenBle);
127. }
128. }, 300);
129. },
130. /**
131. * 停止搜索蓝牙设备
132. */
133. stopBluetoothDevicesDiscovery() {
134. uni.stopBluetoothDevicesDiscovery({
135. success: e => {
136. console.log('停止搜索蓝牙设备:' + e.errMsg);
137. },
138. fail: e => {
139. console.log('停止搜索蓝牙设备失败,错误码:' + e.errCode);
140. }
141. });
142. },
143. /**
144. * 发现外围设备
145. */
146. onBluetoothDeviceFound() {
147. console.log("监听寻找新设备");
148. //this.getBluetoothDevices();
149. uni.onBluetoothDeviceFound(devices => {
150. console.log('开始监听寻找到新设备的事件');
151. this.getBluetoothDevices();
152. });
153. },
154. /**
155. * 获取在蓝牙模块生效期间所有已发现的蓝牙设备。包括已经和本机处于连接状态的设备。
156. */
157. getBluetoothDevices() {
158. console.log("获取蓝牙设备");
159. uni.getBluetoothDevices({
160. success: res => {
161. console.log('获取蓝牙设备成功:' + res.errMsg);
162. this.devicesList = res.devices;
163. //在这里查找Belter_BT名称的血压仪
164. for (let i = 0; i < this.devicesList.length; i++) {
165. let eq = this.devicesList[i];
166. if (eq.name === "Belter_BT") {
167. this.deviceId = eq.deviceId;
168. console.log("查找到了血压仪:设备deviceId:" + this.deviceId);
169. //在这准备连接设备
170. this.createBLEConnection();
171. //停止搜索设备
172. this.stopBluetoothDevicesDiscovery();
173. break;
174. }
175. }
176. },
177. fail: e => {
178. console.log('获取蓝牙设备错误,错误码:' + e.errCode);
179. }
180. });
181. },
182. /**
183. * 连接设备
184. */
185. createBLEConnection() {
186. //设备deviceId
187. let deviceId = this.deviceId;
188. let self = this;
189. uni.createBLEConnection({
190. // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
191. deviceId,
192. success: res => {
193. console.log("设备连接成功!");
194. //延迟1.5s获取设备的services
195. setTimeout(function() {
196. console.log("获取设备的services");
197. self.getBLEDeviceServices();
198. }, 1500);
199. },
200. fail: res => {
201. console.log(JSON.stringify(res));
202. console.log("设备连接失败!");
203. }
204. });
205. },
206. /**
207. * 获取设备的服务ID
208. */
209. getBLEDeviceServices() {
210. let deviceId = this.deviceId;
211. let serviceList = [];
212. let self = this;
213. uni.getBLEDeviceServices({
214. // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
215. deviceId,
216. success: res => {
217. console.log(JSON.stringify(res));
218. serviceList = res.services;
219. for (let i = 0; i < serviceList.length; i++) {
220. let service = serviceList[i];
221. console.log(JSON.stringify(service) + "----serviceID:" + service.uuid);
222. //比对service是否是FFF0服务
223. if (service.uuid.indexOf(self.primaryUUID) != -1) {
224. self.serviceId = service.uuid;
225. console.log("设备的serviceId: " + self.serviceId);
226. //开始获取指定服务的特征值
227. self.getBLEDeviceCharacteristics();
228. break;
229. }
230. }
231. },
232. fail: res => {
233. console.log('device services:', res.services)
234. }
235. });
236. },
237. /**
238. * 获取指定服务的特征值
239. */
240. getBLEDeviceCharacteristics() {
241. let deviceId = this.deviceId;
242. let serviceId = this.serviceId;
243. let characteristicsList = [];
244. let self = this;
245. uni.getBLEDeviceCharacteristics({
246. // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
247. deviceId,
248. // 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
249. serviceId,
250. success: res => {
251. console.log(JSON.stringify(res));
252. console.log("获取的" + serviceId + "服务的特征值:" + JSON.stringify(res.characteristics));
253. characteristicsList = res.characteristics;
254. for (let i = 0; i < characteristicsList.length; i++) {
255. let characteristic = characteristicsList[i];
256. //判断服务特征值中的特征值FFF3 和 FFF4
257. if (characteristic.uuid.indexOf("FFF3") != -1) {
258. self.characteristicId[0].writeId = characteristic.uuid;
259. console.log("设备的特征值写入ID: " + self.characteristicId[0].writeId);
260. }
261. if (characteristic.uuid.indexOf("FFF4") != -1) {
262. self.characteristicId[0].notifyId = characteristic.uuid;
263. console.log("设备的特征值notifyID: " + self.characteristicId[0].notifyId);
264. }
265. if (self.characteristicId[0].writeId != "" && self.characteristicId[0].notifyId != "") {
266. break;
267. }
268. }
269. },
270. fail: res => {
271. console.log('device getBLEDeviceCharacteristics failed:', JSON.stringify(res))
272. }
273. })
274. },
275. /**
276. * 开启订阅特征值
277. */
278. notifyBLECharacteristicValue() {
279. let deviceId = this.deviceId;
280. let serviceId = this.serviceId;
281. let characteristicId = this.characteristicId[0].notifyId;
282. let notify = true;
283. let self = this;
284. uni.notifyBLECharacteristicValueChange({
285. state: true, // 启用 notify 功能
286. // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
287. deviceId,
288. // 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
289. serviceId,
290. // 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取
291. characteristicId,
292. success(res) {
293. console.log('notifyBLECharacteristicValueChange success:' + JSON.stringify(res));
294.
295. // ArrayBuffer转16进度字符串示例
296. function ab2hex(buffer) {
297. const hexArr = Array.prototype.map.call(
298. new Uint8Array(buffer),
299. function(bit) {
300. return ('00' + bit.toString(16)).slice(-2)
301. }
302. )
303. return hexArr.join('')
304. }
305. uni.onBLECharacteristicValueChange(function(res) {
306. console.log(`characteristic ${res.characteristicId} has changed, now is ${JSON.stringify(res.value)}`)
307. var value = ab2hex(res.value);
308. console.log(value);
309. if (value.length === 14){
310. let result = parseInt(value.slice(10, 12), 16);
311. console.log("动态压力值:" + result);
312. self.measureResult[0].pressure = result;
313. }
314. if (value.length === 34){
315. let sys = parseInt(value.slice(10, 14), 16);
316. let dia = parseInt(value.slice(14, 18), 16);
317. let pul = parseInt(value.slice(22, 26), 16);
318. console.log("高压值 SYS:" + sys);
319. console.log("低压值 DIA:" + dia);
320. console.log("脉搏值 PUL:" + pul);
321. self.measureResult[0].SYS = sys;
322. self.measureResult[0].DIA = dia;
323. self.measureResult[0].PUL = pul;
324. //停止上传数据
325. self.writeBLECharacteristicValue(self.wirteControlCode[0].stopUploadData);
326. }
327. console.log("----------------------------------------------");
328. });
329. },
330. fail(res) {
331. console.log('notifyBLECharacteristicValueChange failed:' + res.errMsg);
332. var value = ab2hex(res.value);
333. console.log(value);
334. }
335. });
336. },
337. /**
338. * 写入控制命令
339. * writeCode 写入的控制命令
340. */
341. writeBLECharacteristicValue(writeCode) {
342. let deviceId = this.deviceId;
343. let serviceId = this.serviceId;
344. let characteristicId = this.characteristicId[0].writeId;
345.
346. //因为协议文档中,一个字节两个字符的控制命令,codeLength为命令字节数
347. let codeLength = writeCode.length / 2;
348. const buffer = new ArrayBuffer(codeLength);
349. const dataView = new DataView(buffer)
350.
351. //在这里解析将要写入的值
352. for (let i = 0; i < codeLength; i++) {
353. dataView.setUint8(i, '0X' + writeCode.substring(i * 2, i * 2 + 2));
354. console.log("次数:" + i + "-----" + writeCode.substring(2 * i, 2 * i + 2));
355. }
356.
357. console.log("写入数据中deviceId:" + deviceId);
358. console.log("写入数据中serviceId:" + serviceId);
359. console.log("写入数据中characteristicId:" + characteristicId);
360. console.log("分割线************************************");
361.
362. console.log("发送的数据:")
363. for (let i = 0; i < dataView.byteLength; i++) {
364. console.log("0x" + dataView.getUint8(i).toString(16))
365. }
366.
367. uni.writeBLECharacteristicValue({
368. // 这里的 deviceId 需要在 getBluetoothDevices 或 onBluetoothDeviceFound 接口中获取
369. deviceId,
370. // 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
371. serviceId,
372. // 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取
373. characteristicId,
374. // 这里的value是ArrayBuffer类型
375. value: buffer,
376. success(res) {
377. console.log('writeBLECharacteristicValue success', res.errMsg)
378. console.log('writeBLECharacteristicValue success', JSON.stringify(res))
379. //this.notifyCharacteristicValueChange();
380. },
381. fail(res) {
382. console.log("写入数据失败", res.errMsg)
383. }
384. });
385. },
386. /**
387. * 监听低功耗蓝牙连接状态的改变事件。包括开发者主动连接或断开连接,设备丢失,连接异常断开等等
388. */
389. onBLEConnectionStateChange() {
390. let count = 0;
391. uni.onBLEConnectionStateChange(res => {
392. // 该方法回调中可以用于处理连接意外断开等异常情况
393. console.log(`蓝牙连接状态 -------------------------->`);
394. console.log(JSON.stringify(res));
395. if (!res.connected) {
396. if (this.isStop) return;
397. console.log('断开低功耗蓝牙成功:');
398.
399. uni.showToast({
400. icon: "none",
401. title: "蓝牙已经断开!",
402. mask: false,
403. duration: 3000
404. });
405.
406. //在这里尝试重连
407. //this.createBLEConnection();
408. //关闭连接
409. this.closeBluetoothAdapter();
410. }
411. });
412. },
413. /**
414. * 断开蓝牙连接
415. */
416. closeBluetoothAdapter() {
417. uni.closeBluetoothAdapter({
418. success: res => {
419. console.log('断开蓝牙模块成功');
420.
421. uni.showToast({
422. icon: "none",
423. title: "蓝牙已经断开!",
424. mask: false,
425. duration: 3000
426. });
427. }
428. });
429. }
430. }
431. }
432. </script>
433.
434. <style>
435. .content {}
436. </style>