微信小程序开发-蓝牙功能开发

0. 前言
  这两天刚好了解了一下微信小程序的蓝牙功能。主要用于配网功能。发现微信的小程序蓝牙API已经封装的很好了。编程起来很方便。什么蓝牙知识都不懂的情况下,不到两天就晚上数据的收发了,剩下的就是数据帧格式的定义,当然这部分就不是本次博客的重点。
1. 准备硬件
  这里我准备了CH341SER这个作为USB转串口。用sscom5.13.1 串口工具。由于我不太懂硬件开发。硬件部分都是由公司其他人开发的。我只是负责把环境搭建起来。然后负责我的微信小程序开发。



2. 开发小程序简单讲解
  onLoad 这个一方面是用来获取当前连接的WiFi名称,减少用户输入,另一方面也是用来判断当前是否开启GPS功能。对于Android用户,是需要打开GPS蓝牙功能才能搜索到周围的蓝牙设备。

 1   onLoad: function(options) {
 2     var that = this;
 3     wx.startWifi({
 4       success(res) {
 5         console.log(res.errMsg)
 6         wx.getConnectedWifi({
 7           success: function(res) {
 8             console.log(res);
 9             that.setData({
10               ssid: res.wifi.SSID
11             })
12           },
13           fail: function(res) {
14             if(res.errCode == 12006){
15               wx.showModal({
16                 title: '请打开GPS定位',
17                 content: 'Android手机不打开GPS定位,无法搜索到蓝牙设备.',
18                 showCancel: false
19               })
20             }
21             console.log(res);
22           }
23         })
24       }
25     })
26   },

  搜索蓝牙设备相关代码

 1   searchBleEvent: function(ret){
 2     var ssid = this.data.ssid;
 3     var pass = this.data.pass;
 4     console.log(ssid, pass);
 5     if (util.isEmpty(ssid) || util.isEmpty(pass)) {
 6       util.toastError('请输入WiFi名称及密码');
 7       return;
 8     }
 9     this.initBLE();
10   },

  初始化蓝牙适配器

 1   initBLE: function() {
 2     this.printLog("启动蓝牙适配器, 蓝牙初始化")
 3     var that = this;
 4     wx.openBluetoothAdapter({
 5       success: function(res) {
 6         console.log(res);
 7         that.findBLE();
 8       },
 9       fail: function(res) {
10         util.toastError('请先打开蓝牙');
11       }
12     })
13   },

  定义搜索设备任务

 1   findBLE: function() {
 2     this.printLog("打开蓝牙成功.")
 3     var that = this
 4     wx.startBluetoothDevicesDiscovery({
 5       allowDuplicatesKey: false,
 6       interval: 0,
 7       success: function(res) {
 8         wx.showLoading({
 9           title: '正在搜索设备',
10         })
11         console.log(res);
12         delayTimer = setInterval(function(){
13           that.discoveryBLE() //3.0 //这里的discovery需要多次调用
14         }, 1000);
15         setTimeout(function () {
16           if (isFound) {
17             return;
18           } else {
19             wx.hideLoading();
20             console.log("搜索设备超时");
21             wx.stopBluetoothDevicesDiscovery({
22               success: function (res) {
23                 console.log('连接蓝牙成功之后关闭蓝牙搜索');
24               }
25             })
26             clearInterval(delayTimer)
27             wx.showModal({
28               title: '搜索设备超时',
29               content: '请检查蓝牙设备是否正常工作,Android手机请打开GPS定位.',
30               showCancel: false
31             })
32             util.toastError("搜索设备超时,请打开GPS定位,再搜索")
33             return
34           }
35         }, 15000);
36       },
37       fail: function(res) {
38         that.printLog("蓝牙设备服务发现失败: " + res.errMsg);
39       }
40     })
41   },

  搜索设备回调

 1   discoveryBLE: function() {
 2     var that = this
 3     wx.getBluetoothDevices({
 4       success: function(res) {
 5         var list = res.devices;
 6         console.log(list);
 7         if(list.length <= 0){
 8           return ;
 9         }
10         var devices = [];
11         for (var i = 0; i < list.length; i++) {   
12           //that.data.inputValue:表示的是需要连接的蓝牙设备ID,
13           //简单点来说就是我想要连接这个蓝牙设备,
14           //所以我去遍历我搜索到的蓝牙设备中是否有这个ID
15           var name = list[i].name || list[i].localName;
16           if(util.isEmpty(name)){
17             continue;
18           }
19           if(name.indexOf('JL') >= 0 && list[i].RSSI != 0){
20             console.log(list[i]);
21             devices.push(list[i]);
22           }
23         }
24         console.log('总共有' + devices.length + "个设备需要设置")
25         if (devices.length <= 0) {
26           return;
27         }
28         that.connectBLE(devices);
29       },
30       fail: function() {
31         util.toastError('搜索蓝牙设备失败');
32       }
33     })
34   },

  设置可以进行连接的设备

 1   connectBLE: function(devices){
 2     this.printLog('总共有' + devices.length + "个设备需要设置")
 3     var that = this;
 4     wx.hideLoading();
 5     isFound = true;
 6     clearInterval(delayTimer); 
 7     wx.stopBluetoothDevicesDiscovery({
 8       success: function (res) {
 9         that.printLog('连接蓝牙成功之后关闭蓝牙搜索');
10       }
11     })
12     //两个的时候需要选择
13     var list = [];
14     for (var i = 0; i < devices.length; i++) {
15       var name = devices[i].name || devices[i].localName;
16       list.push(name + "[" + devices[i].deviceId + "]")
17     }
18     this.setData({
19       deviceArray: list
20     })
21     //默认选择
22     this.setData({
23       currDeviceID: list[0]
24     })
25   },

  选择设备,然后点击对应的配网按钮,创建BLE连接

 1   createBLE: function(deviceId){
 2     this.printLog("连接: [" + deviceId+"]");
 3     var that = this;
 4     this.closeBLE(deviceId, function(res){
 5       console.log("预先关闭,再打开");
 6       setTimeout(function(){
 7         wx.createBLEConnection({
 8           deviceId: deviceId,
 9           success: function (res) {
10             that.printLog("设备连接成功");
11             that.getBLEServiceId(deviceId);
12           },
13           fail: function (res) {
14             that.printLog("设备连接失败" + res.errMsg);
15           }
16         })
17       }, 2000)
18     });
19   },

  获取蓝牙设备提供的服务UUID(本项目由于只会提供一个服务,就默认选择,实际项目,会自定义这个UUID的前缀或者后缀规则,定义多个不同的服务)

 1   //获取服务UUID
 2   getBLEServiceId: function(deviceId){
 3     this.printLog("获取设备[" + deviceId + "]服务列表")
 4     var that = this;
 5     wx.getBLEDeviceServices({
 6       deviceId: deviceId,
 7       success: function(res) {
 8         console.log(res);
 9         var services = res.services;
10         if (services.length <= 0){
11           that.printLog("未找到主服务列表")
12           return;
13         }
14         that.printLog('找到设备服务列表个数: ' + services.length);
15         if (services.length == 1){
16           var service = services[0];
17           that.printLog("服务UUID:["+service.uuid+"] Primary:" + service.isPrimary);
18           that.getBLECharactedId(deviceId, service.uuid);
19         }else{ //多个主服务
20           //TODO
21         }
22       },
23       fail: function(res){
24         that.printLog("获取设备服务列表失败" + res.errMsg);
25       }
26     })
27   },

  获取服务下的特征值(由于这个例子,是包含两个特征值,一个用于读,一个用于写,实际项目,跟上面的服务一样,要定义好特征量UUID的规则)

 1   getBLECharactedId: function(deviceId, serviceId){
 2     this.printLog("获取设备特征值")
 3     var that = this;
 4     wx.getBLEDeviceCharacteristics({
 5       deviceId: deviceId,
 6       serviceId: serviceId,
 7       success: function(res) {
 8         console.log(res);
 9         //这里会获取到两个特征值,一个用来写,一个用来读
10         var chars = res.characteristics;
11         if(chars.length <= 0){
12           that.printLog("未找到设备特征值")
13           return ;
14         }
15         that.printLog("找到设备特征值个数:" + chars.length);
16         if(chars.length == 2){
17           for(var i=0; i<chars.length; i++){
18             var char = chars[i];
19             that.printLog("特征值[" + char.uuid + "]")
20             var prop = char.properties;
21             if(prop.notify == true){
22               that.printLog("该特征值属性: Notify");
23               that.recvBLECharacterNotice(deviceId, serviceId, char.uuid);
24             }else if(prop.write == true){
25               that.printLog("该特征值属性: Write");
26               that.sendBLECharacterNotice(deviceId, serviceId, char.uuid);
27             }else{
28               that.printLog("该特征值属性: 其他");
29             }
30           }
31         }else{
32           //TODO
33         }
34       },
35       fail: function(res){
36         that.printLog("获取设备特征值失败")
37       }
38     })
39   },

  recv 接收设备发送过来数据

 1   recvBLECharacterNotice: function(deviceId, serviceId, charId){
 2     //接收设置是否成功
 3     this.printLog("注册Notice 回调函数");
 4     var that = this;
 5     wx.notifyBLECharacteristicValueChange({
 6       deviceId: deviceId,
 7       serviceId: serviceId,
 8       characteristicId: charId,
 9       state: true, //启用Notify功能
10       success: function(res) {
11         wx.onBLECharacteristicValueChange(function(res){
12           console.log(res);
13           that.printLog("收到Notify数据: " + that.ab2hex(res.value));
14           //关闭蓝牙
15           wx.showModal({
16             title: '配网成功',
17             content: that.ab2hex(res.value),
18             showCancel: false
19           })
20         });
21       },
22       fail: function(res){
23         console.log(res);
24         that.printLog("特征值Notice 接收数据失败: " + res.errMsg);
25       }
26     })
27   },

  send 小程序发送数据到设备

 1   sendBLECharacterNotice: function (deviceId, serviceId, charId){
 2     //发送ssid/pass
 3     this.printLog("延时1秒后,发送SSID/PASS");
 4     var that = this;
 5     var cell = {
 6       "ssid": this.data.ssid,
 7       "pass": this.data.pass
 8     }
 9     var buffer = this.string2buffer(JSON.stringify(cell));
10     setTimeout(function(){
11       wx.writeBLECharacteristicValue({
12         deviceId: deviceId,
13         serviceId: serviceId,
14         characteristicId: charId,
15         value: buffer,
16         success: function(res) {
17           that.printLog("发送SSID/PASS 成功");
18         },
19         fail: function(res){
20           console.log(res);
21           that.printLog("发送失败." + res.errMsg);
22         },
23         complete: function(){
24           
25         }
26       })
27       
28     }, 1000);
29   },

  手机端可以同时连接多个蓝牙设备,但是同一个蓝牙设备不能被多次连接,所以需要在每次连接前关闭BLE连接

 1   closeBLE: function(deviceId, callback){
 2     var that = this;
 3     wx.closeBLEConnection({
 4       deviceId: deviceId,
 5       success: function(res) {
 6         that.printLog("断开设备[" + deviceId + "]成功.");
 7         console.log(res)
 8       },
 9       fail: function(res){
10         that.printLog("断开设备成功.");
11       },
12       complete: callback
13     })
14   },

  说明:接收数据和发送数据时,注意BLE限制了发送数据包的大小,现在20byte。具体参考微信小程序官方文档: https://developers.weixin.qq.com/miniprogram/dev/api/device/bluetooth-ble/wx.writeBLECharacteristicValue.html

 

3. 蓝牙相关的所有JS代码

  1 // pages/bluetoothconfig/bluetoothconfig.js
  2 const util = require('../../utils/util.js')
  3 
  4 var delayTimer; //用来控制是否持续服务发现
  5 var isFound = false;
  6 
  7 Page({
  8   /**
  9    * 页面的初始数据
 10    */
 11   data: {
 12     ssid: '',
 13     pass: '',
 14     logs: [],
 15     deviceArray: [],
 16     currDeviceID: '请选择...'
 17   },
 18   onLoad: function(options) {
 19     var that = this;
 20     wx.startWifi({
 21       success(res) {
 22         console.log(res.errMsg)
 23         wx.getConnectedWifi({
 24           success: function(res) {
 25             console.log(res);
 26             that.setData({
 27               ssid: res.wifi.SSID
 28             })
 29           },
 30           fail: function(res) {
 31             if(res.errCode == 12006){
 32               wx.showModal({
 33                 title: '请打开GPS定位',
 34                 content: 'Android手机不打开GPS定位,无法搜索到蓝牙设备.',
 35                 showCancel: false
 36               })
 37             }
 38             console.log(res);
 39           }
 40         })
 41       }
 42     })
 43   },
 44   bindPickerChange: function(ret){
 45     var array = this.data.deviceArray;
 46     console.log(array[ret.detail.value]);
 47     this.setData({
 48       currDeviceID: array[ret.detail.value]
 49     })
 50   },
 51   searchBleEvent: function(ret){
 52     var ssid = this.data.ssid;
 53     var pass = this.data.pass;
 54     console.log(ssid, pass);
 55     if (util.isEmpty(ssid) || util.isEmpty(pass)) {
 56       util.toastError('请输入WiFi名称及密码');
 57       return;
 58     }
 59     this.initBLE();
 60   },
 61   bleConfigEvent: function (ret) {
 62     var deviceID = this.data.currDeviceID;
 63     console.log("选中:" + deviceID);
 64     if (util.isEmpty(deviceID) || deviceID == "请选择..."){
 65       util.toastError("请先搜索设备");
 66       return ;
 67     }
 68     var device = deviceID.split('[');
 69     if(device.length <= 1){
 70       util.toastError("请先搜索设备");
 71       return ;
 72     }
 73     var id = device[device.length - 1].replace("]", "");
 74     console.log(id);
 75     util.toastError("连接" + id);
 76     this.createBLE(id);
 77   },
 78 
 79 
 80   initBLE: function() {
 81     this.printLog("启动蓝牙适配器, 蓝牙初始化")
 82     var that = this;
 83     wx.openBluetoothAdapter({
 84       success: function(res) {
 85         console.log(res);
 86         that.findBLE();
 87       },
 88       fail: function(res) {
 89         util.toastError('请先打开蓝牙');
 90       }
 91     })
 92   },
 93   findBLE: function() {
 94     this.printLog("打开蓝牙成功.")
 95     var that = this
 96     wx.startBluetoothDevicesDiscovery({
 97       allowDuplicatesKey: false,
 98       interval: 0,
 99       success: function(res) {
100         wx.showLoading({
101           title: '正在搜索设备',
102         })
103         console.log(res);
104         delayTimer = setInterval(function(){
105           that.discoveryBLE() //3.0 //这里的discovery需要多次调用
106         }, 1000);
107         setTimeout(function () {
108           if (isFound) {
109             return;
110           } else {
111             wx.hideLoading();
112             console.log("搜索设备超时");
113             wx.stopBluetoothDevicesDiscovery({
114               success: function (res) {
115                 console.log('连接蓝牙成功之后关闭蓝牙搜索');
116               }
117             })
118             clearInterval(delayTimer)
119             wx.showModal({
120               title: '搜索设备超时',
121               content: '请检查蓝牙设备是否正常工作,Android手机请打开GPS定位.',
122               showCancel: false
123             })
124             util.toastError("搜索设备超时,请打开GPS定位,再搜索")
125             return
126           }
127         }, 15000);
128       },
129       fail: function(res) {
130         that.printLog("蓝牙设备服务发现失败: " + res.errMsg);
131       }
132     })
133   },
134   discoveryBLE: function() {
135     var that = this
136     wx.getBluetoothDevices({
137       success: function(res) {
138         var list = res.devices;
139         console.log(list);
140         if(list.length <= 0){
141           return ;
142         }
143         var devices = [];
144         for (var i = 0; i < list.length; i++) {   
145           //that.data.inputValue:表示的是需要连接的蓝牙设备ID,
146           //简单点来说就是我想要连接这个蓝牙设备,
147           //所以我去遍历我搜索到的蓝牙设备中是否有这个ID
148           var name = list[i].name || list[i].localName;
149           if(util.isEmpty(name)){
150             continue;
151           }
152           if(name.indexOf('JL') >= 0 && list[i].RSSI != 0){
153             console.log(list[i]);
154             devices.push(list[i]);
155           }
156         }
157         console.log('总共有' + devices.length + "个设备需要设置")
158         if (devices.length <= 0) {
159           return;
160         }
161         that.connectBLE(devices);
162       },
163       fail: function() {
164         util.toastError('搜索蓝牙设备失败');
165       }
166     })
167   },
168   connectBLE: function(devices){
169     this.printLog('总共有' + devices.length + "个设备需要设置")
170     var that = this;
171     wx.hideLoading();
172     isFound = true;
173     clearInterval(delayTimer); 
174     wx.stopBluetoothDevicesDiscovery({
175       success: function (res) {
176         that.printLog('连接蓝牙成功之后关闭蓝牙搜索');
177       }
178     })
179     //两个的时候需要选择
180     var list = [];
181     for (var i = 0; i < devices.length; i++) {
182       var name = devices[i].name || devices[i].localName;
183       list.push(name + "[" + devices[i].deviceId + "]")
184     }
185     this.setData({
186       deviceArray: list
187     })
188     //默认选择
189     this.setData({
190       currDeviceID: list[0]
191     })
192   },
193 
194 
195   createBLE: function(deviceId){
196     this.printLog("连接: [" + deviceId+"]");
197     var that = this;
198     this.closeBLE(deviceId, function(res){
199       console.log("预先关闭,再打开");
200       setTimeout(function(){
201         wx.createBLEConnection({
202           deviceId: deviceId,
203           success: function (res) {
204             that.printLog("设备连接成功");
205             that.getBLEServiceId(deviceId);
206           },
207           fail: function (res) {
208             that.printLog("设备连接失败" + res.errMsg);
209           }
210         })
211       }, 2000)
212     });
213   },
214   //获取服务UUID
215   getBLEServiceId: function(deviceId){
216     this.printLog("获取设备[" + deviceId + "]服务列表")
217     var that = this;
218     wx.getBLEDeviceServices({
219       deviceId: deviceId,
220       success: function(res) {
221         console.log(res);
222         var services = res.services;
223         if (services.length <= 0){
224           that.printLog("未找到主服务列表")
225           return;
226         }
227         that.printLog('找到设备服务列表个数: ' + services.length);
228         if (services.length == 1){
229           var service = services[0];
230           that.printLog("服务UUID:["+service.uuid+"] Primary:" + service.isPrimary);
231           that.getBLECharactedId(deviceId, service.uuid);
232         }else{ //多个主服务
233           //TODO
234         }
235       },
236       fail: function(res){
237         that.printLog("获取设备服务列表失败" + res.errMsg);
238       }
239     })
240   },
241   getBLECharactedId: function(deviceId, serviceId){
242     this.printLog("获取设备特征值")
243     var that = this;
244     wx.getBLEDeviceCharacteristics({
245       deviceId: deviceId,
246       serviceId: serviceId,
247       success: function(res) {
248         console.log(res);
249         //这里会获取到两个特征值,一个用来写,一个用来读
250         var chars = res.characteristics;
251         if(chars.length <= 0){
252           that.printLog("未找到设备特征值")
253           return ;
254         }
255         that.printLog("找到设备特征值个数:" + chars.length);
256         if(chars.length == 2){
257           for(var i=0; i<chars.length; i++){
258             var char = chars[i];
259             that.printLog("特征值[" + char.uuid + "]")
260             var prop = char.properties;
261             if(prop.notify == true){
262               that.printLog("该特征值属性: Notify");
263               that.recvBLECharacterNotice(deviceId, serviceId, char.uuid);
264             }else if(prop.write == true){
265               that.printLog("该特征值属性: Write");
266               that.sendBLECharacterNotice(deviceId, serviceId, char.uuid);
267             }else{
268               that.printLog("该特征值属性: 其他");
269             }
270           }
271         }else{
272           //TODO
273         }
274       },
275       fail: function(res){
276         that.printLog("获取设备特征值失败")
277       }
278     })
279   },
280   recvBLECharacterNotice: function(deviceId, serviceId, charId){
281     //接收设置是否成功
282     this.printLog("注册Notice 回调函数");
283     var that = this;
284     wx.notifyBLECharacteristicValueChange({
285       deviceId: deviceId,
286       serviceId: serviceId,
287       characteristicId: charId,
288       state: true, //启用Notify功能
289       success: function(res) {
290         wx.onBLECharacteristicValueChange(function(res){
291           console.log(res);
292           that.printLog("收到Notify数据: " + that.ab2hex(res.value));
293           //关闭蓝牙
294           wx.showModal({
295             title: '配网成功',
296             content: that.ab2hex(res.value),
297             showCancel: false
298           })
299         });
300       },
301       fail: function(res){
302         console.log(res);
303         that.printLog("特征值Notice 接收数据失败: " + res.errMsg);
304       }
305     })
306   },
307   sendBLECharacterNotice: function (deviceId, serviceId, charId){
308     //发送ssid/pass
309     this.printLog("延时1秒后,发送SSID/PASS");
310     var that = this;
311     var cell = {
312       "ssid": this.data.ssid,
313       "pass": this.data.pass
314     }
315     var buffer = this.string2buffer(JSON.stringify(cell));
316     setTimeout(function(){
317       wx.writeBLECharacteristicValue({
318         deviceId: deviceId,
319         serviceId: serviceId,
320         characteristicId: charId,
321         value: buffer,
322         success: function(res) {
323           that.printLog("发送SSID/PASS 成功");
324         },
325         fail: function(res){
326           console.log(res);
327           that.printLog("发送失败." + res.errMsg);
328         },
329         complete: function(){
330           
331         }
332       })
333       
334     }, 1000);
335   },
336 
337   closeBLE: function(deviceId, callback){
338     var that = this;
339     wx.closeBLEConnection({
340       deviceId: deviceId,
341       success: function(res) {
342         that.printLog("断开设备[" + deviceId + "]成功.");
343         console.log(res)
344       },
345       fail: function(res){
346         that.printLog("断开设备成功.");
347       },
348       complete: callback
349     })
350   },
351   
352 
353 
354 
355   printLog: function(msg){
356     var logs = this.data.logs;
357     logs.push(msg);
358     this.setData({ logs: logs })
359   },
360   /**
361    * 将字符串转换成ArrayBufer
362    */
363   string2buffer(str) {
364     if (!str) return;
365     var val = "";
366     for (var i = 0; i < str.length; i++) {
367       val += str.charCodeAt(i).toString(16);
368     }
369     console.log(val);
370     str = val;
371     val = "";
372     let length = str.length;
373     let index = 0;
374     let array = []
375     while (index < length) {
376       array.push(str.substring(index, index + 2));
377       index = index + 2;
378     }
379     val = array.join(",");
380     // 将16进制转化为ArrayBuffer
381     return new Uint8Array(val.match(/[\da-f]{2}/gi).map(function (h) {
382       return parseInt(h, 16)
383     })).buffer
384   },
385   /**
386    * 将ArrayBuffer转换成字符串
387    */
388   ab2hex(buffer) {
389     var hexArr = Array.prototype.map.call(
390       new Uint8Array(buffer),
391       function (bit) {
392         return ('00' + bit.toString(16)).slice(-2)
393       }
394     )
395     return hexArr.join('');
396   },
397   inputSSID: function(res) {
398     var ssid = res.detail.value;
399     this.setData({
400       ssid: ssid
401     })
402   },
403   inputPASS: function(res) {
404     var pass = res.detail.value;
405     this.setData({
406       pass: pass
407     })
408   }
409 
410 })
View Code

 

4. 运行时截图

   

 


工具下载地址:

  https://files.cnblogs.com/files/wunaozai/sscom5.13.1.zip

  https://files.cnblogs.com/files/wunaozai/CH341SER_64bit.zip

源代码下载 :

  https://files.cnblogs.com/files/wunaozai/bluetoothconfig.rar

参考资料:

  https://www.cnblogs.com/guhonghao/p/9947144.html

本文地址:

   https://www.cnblogs.com/wunaozai/p/11512874.html

posted @ 2019-09-12 17:50  无脑仔的小明  阅读(...)  评论(...编辑  收藏