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回调,主动返回特征值数据。
    流程图
    uni-app 使用低功耗蓝牙连接血压仪测量 - 图1
    2、Uni-APP蓝牙使用案例
    使用uni-app连接血压仪
    这部分代码是我测试中写的,主要是按照上面总结的思路流程写的代码,写的有些乱,还又长,就像老太太的裹脚布那样,又臭又长,有的写的不好,无意中总会创造各种各样的BUG,在实际项目中改动了好多部分代码,做了一些蓝牙使用场景的判断,尽量去融合蓝牙设备使用中遇到的各式各样的问题,尽量去提高用户体验。
    代码下载连接,拷贝进uni-app项目即可https://download.csdn.net/download/qq_38734155/11195286

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