微信小程序開發-藍牙功能開發

0. 前言
  這兩天恰好了解了一下微信小程序的藍牙功能。主要用於配網功能。發現微信的小程序藍牙API已經封裝的很好了。編程起來很方便。什麼藍牙知識都不懂的狀況下,不到兩天就晚上數據的收發了,剩下的就是數據幀格式的定義,固然這部分就不是本次博客的重點。
1. 準備硬件
  這裏我準備了CH341SER這個做爲USB轉串口。用sscom5.13.1 串口工具。因爲我不太懂硬件開發。硬件部分都是由公司其餘人開發的。我只是負責把環境搭建起來。而後負責個人微信小程序開發。
html



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   },

  定義搜索設備任務api

 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   },

  設置能夠進行鏈接的設備ide

 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的規則)ui

 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

參考資料:

  http://www.javashuo.com/article/p-rxdikxyf-cm.html

本文地址:

   http://www.javashuo.com/article/p-zmgktgiu-dq.html

相關文章
相關標籤/搜索