0%

微信小程序蓝牙BLE开发——实战案例(二)

上篇主要介绍在开发过程中应用到相关API操作。接下来介绍个人在项目开发中应用蓝牙BLE一些事情。

由于时间比较仓促, 有些注释没那么详细。请理解~写的不好欢迎各位大神指。

项目介绍

由于公司近阶段开发的共享产品比较多,有共享充电宝、陪护床等,这里就不一一介绍哈。大部分产品通过蓝牙通讯的。

快捷方便通过扫设备二维码进入小程序使用设备

  • 这里拿最近开发一个产品售卖机,生活中比较常见的一个,例如: 储物柜、格子柜等。
  • 特点: 一个售卖机中有n个小格子(根据产品分类)

效果图

在这里插入图片描述

扫码使用格子柜流程

售卖机上有设备的二维码(自己生成普通二维码),直接扫码

ps: 有个别标注链接的,是个人在项目开发中应用总结知识点

  • 微信扫普通二维码进入小程序授权获取用户信息 用户授权成功后,检测用户是否打开蓝牙
  • 未打开时弹窗提示用户, 否则自动跳转到售卖机页面。
  • 售卖机页面会展示售卖商品(比如8个,那么就展示8个商品)
  • 选择需要购买的商品, 点击购买按钮
  • 支付金额
  • 支付完成后, 调用执行蓝牙方法开始搜索设备—>匹配设备—>连接设备...—>开锁成功(柜门打开)查看蓝牙具体流程】如连接设备失败,页面显示重连按钮
  • 拿出商品,关好柜门(可返回购买页,继续购买)。

蓝牙通讯协议

主要通过与供应商提供的蓝牙板子通讯协议和硬件对接,实现操控设备具备的功能。

  • 不同的供应商设备蓝牙通讯协议有所不同。反之产品不同通讯流程也不同。

    有些文档比较不规范,看起来会很茫然, 慢慢理解查资料

加密

基于数据安全性完整性等等,蓝牙协议通过会在有加密的过程,抵御攻击者。

密码之AES五种加密模式(CBC、ECB、CTR、OCF、CFB)

  • 下发某个指令时需要加密,数据回来后可直接使用
  • 下发指令进行进行加密, 数据回来后需要解密才可以使用

不管使用哪种加密模式,会标注在协议文档或以其他方式说明

ps: 个人在项目中使用模式有AES 128CFB 128, 还有最近使用一种CRC(循环冗余校验)来校验。

后面有时间会发布,请及时关注

实例(蓝牙操作功能)

说明:

  • 以下代码直接copy.js中就可以使用。(这里有分段说明下,按顺序复制即可)
  • 因在项目中对接设备比较多, 把蓝牙设备相关功能剥离到js文件中。
  • 注意: 开发工具上不支持调试蓝牙功能的
  • 蓝牙API详情介绍请点击

一. 新建JS文件及使用时引入

在这里插入图片描述

1. 定义全局变量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
const app = getApp()
var utils = require('.././util.js');
var cryptoService = require('../bleFile/cryptoService.js'); //引入加密文件
var crc = require('../bleFile/crc.js'); //引入crc校验


var serviceUUID = [] //主 service 的 uuid 列表
var writeUUID = ""; //写读 UUID
var notifyUUID = ""; //notify UUID
var delayUUID = ""; //护眼仪控制时长UUID
var filterServiceUUID = ""; //过滤获取到的服务uuid(有些会返回多条数据)
var filterDeviceName = ""; //设备名称

var macAddress = ""; //保存得到mac地址
var flagFromTypes = ''; //来源类型(根据不同产品执行蓝牙协议) 例:1-->充电宝 2-->售卖机....
var _discoveryStarted = false;
var deviceId = ''; //用于区分设备的 id

var _deviceId = '';
var _serviceId = '';
var _characteristicId = '';
var status = false; //当前状态
var action_type = ''; //操作类型
var code = -1;
var isnotExist = true //防止多次执行

var asddCallBack = null; //接收数据处理
var asddErrorCallback = null; //接收错误码处理
var asddWriteErrors = null; //接收写入错误处理

var tempStr = ''; //拼接设备返回数据
var rssiVal = ''; //RSSI值
var currentSerialVal = ''; //当前点击格子柜序号


//当前操作类型
var OptionEnum = {
None: -1,
Connection: 0, //连接
Reconnection: 1, //重连
CloseLock: 2, //关闭
VendingOpenLock: 8, //打开售卖机
ReadStatus: 9, //读售卖机状态
ReadBattery: 10, //读售卖机电量
};
//这个是变量, 存储数据的变量
var action_type = OptionEnum.None;

2. 自定义方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
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 clearSymbol(str) {
str = str.replace(/:/g, ""); //取消字符串中出现的所有冒号
return str;
}

/**
* 匹配规则: 取名称后面的mac
*/
function getNameMac(macAddress, len, name) {
let clearColonMac = clearSymbol(macAddress);
let lastFourMac = clearColonMac.substring(clearColonMac.length - len);
let strName = name.toUpperCase();
strName = strName + lastFourMac.toUpperCase(); //转大写
console.log('拼接后的' + strName);
return strName
}


/**
* 区分不同类型的服务相关uuid
* 1-->充电宝 2-->售卖机
*/
function flagServiceIDFun(serviceType) {
console.log('方法中拿到type======>', serviceType);
if (serviceType == 1) {
serviceUUID[0] = "0000*E0-00*0-*0*0-*0*0-00**5F9**4*B"; //主 service的uuid 列表
writeUUID = "00*0**E2-00*0-*0*0-*0*0-00**5F9**4*B"; //写读 UUID
notifyUUID = "00*0**E1-00*0-*0*0-*0*0-00**5F9**4*B"; //notify UUID
filterServiceUUID = "*E0";
} else if (serviceType == 2) {
serviceUUID[0] = "0000*E0-00*0-*0*0-*0*0-00**5F9**4*B"; //主 service的uuid 列表
writeUUID = "00*0**E2-00*0-*0*0-*0*0-00**5F9**4*B"; //写读 UUID
notifyUUID = "00*0**E1-00*0-*0*0-*0*0-00**5F9**4*B"; //notify UUID
filterServiceUUID = "*E0";

//这里介绍用name匹配方法
filterDeviceName = getNameMac(macAddress, 6, 'abc_'); //设备名称
}
}

3. 初始化蓝牙入口

注意: 在使用.js文件中, 只需要调用initBle()方法即可实现蓝牙实现整个过程。【方法里面每个步骤都有调用对应方法】


说明: 所有asddErrorCallback(res.errCode, "");处理错误码回调, 在使用的页面中接收并处理回调函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/**
* 初始化蓝牙模块
* 参数1: mac地址
* 参数2: 当前操作类型
* 参数3: 当前点击格子的序号(第几个格子)
*/
function initBle(fromMac, flagTypes, currentSerial) {
tempStr = ''; //清空

//断开连接【每次初始化先断开连接】
closeBLEConnection();

console.log("你获取到mac地址了么....", fromMac + '获取到类型为====》' + flagTypes);
// macAddress = clearSymbol(fromMac);
macAddress = fromMac; //保存mac
flagFromTypes = flagTypes //类型来源

//获取RSSI值
let getRssi = wx.getStorageSync('rssi');
rssiVal = getRssi;

//调用主服务id相关
flagServiceIDFun(flagTypes);

// 获取当前点击售卖机序号值
currentSerialVal = currentSerial


_discoveryStarted = false;

//初始化蓝牙模块
wx.openBluetoothAdapter({
success: (res) => {
console.log('openBluetoothAdapter 初始化蓝牙模块是否成功:', res)
// 监听寻找新设备事件
onBluetoothDeviceFound();

//开始搜寻附近的蓝牙外围设备
startBluetoothDevicesDiscovery();
},
fail: (res) => {
console.log('初始化蓝牙失败', res);
asddErrorCallback(res.errCode, "");

//监听蓝牙适配器状态变化事件【根据需求是否执行】
// wx.onBluetoothAdapterStateChange(function (res) {
// console.log('蓝牙适配器状态更改结果: ', res)
// if (res.available) {
// console.log('蓝牙可用,搜索设备:--》 ')
// onBluetoothDeviceFound();
// startBluetoothDevicesDiscovery();
// }
// })
}
})
}

4. 应用到蓝牙API方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
/**
* 监听寻找新设备事件
* 注意咯: 这里有展示三种不同方式来连接设备,请看备注【mac, name, 根据系统的】 各取所需吧。
*/
function onBluetoothDeviceFound() {
wx.onBluetoothDeviceFound((res) => {
console.log('广播数据结果:', res);

res.devices.forEach(device => {
if (!device.name && !device.localName) {
return
}

// 转换后, 根据协议文档取需要数据 (advertisData不一定有数据)
if (device.advertisData != null) { //判断对象是否为null,advertisData不一定有
var hexStr = ab2hex(device.advertisData);
console.log("广播数据中转换后:advertisData---->" + hexStr);
}


// 充电宝 android用mac, ios用name匹配
if (flagFromTypes == 1) {
if (app.getPlatform() == 'android') {
if ((macAddress != "") && (macAddress == device.deviceId) && isnotExist) {
isnotExist = false;
deviceId = device.deviceId;

stopBluetoothDevicesDiscovery();
//连接设备
createBLEConnection();
}
} else if (app.getPlatform() == 'ios') {
let deviceName = device.name.toUpperCase();
if ((deviceName.indexOf(filterDeviceName) != -1) && isnotExist) {
isnotExist = false;
deviceId = device.deviceId;

//停止搜寻附近的蓝牙外围设备
stopBluetoothDevicesDiscovery();
//连接设备
createBLEConnection();
}
}
} else if (flagFromTypes == 2) {
// 格子柜 Android IOS通过名称匹配
if (parseFloat(device.RSSI) > parseFloat(rssiVal)) {
let deviceName = device.name.toUpperCase();
if ((deviceName.indexOf(filterDeviceName) != -1) && isnotExist) {
isnotExist = false;
deviceId = device.deviceId;

//停止搜寻附近的蓝牙外围设备
stopBluetoothDevicesDiscovery();
//连接设备
createBLEConnection();
}
}
}
})
})
}



/**
* 执行连接蓝牙设备
*/
function startBluetoothDevicesDiscovery() {
if (_discoveryStarted) {
return;
}
_discoveryStarted = true

wx.startBluetoothDevicesDiscovery({
services: serviceUUID,
allowDuplicatesKey: false,
success: (res) => {
console.log('启动搜索蓝牙设备, 结果 :', res)
//onBluetoothDeviceFound() //先调用此方法再使用startBluetoothDevicesDiscovery
},
fail(res) {
asddErrorCallback(res.errCode, "");
console.log('startBluetoothDevicesDiscovery fail', res);
}
})
}


//停止搜寻附近的蓝牙外围设备。
function stopBluetoothDevicesDiscovery() {
wx.stopBluetoothDevicesDiscovery()
}


/**
* 连接蓝牙设备
*/
function createBLEConnection() {
var that = this;
wx.createBLEConnection({
deviceId: deviceId,
success: (res) => {
wx.showToast({
title: '设备连接成功',
duration: 2000
})
//获取蓝牙所有服务
getBLEDeviceServices(deviceId)
},
fail: (res) => {
console.log('createBLEConnection fail', res);
asddErrorCallback(res.errCode, "");
}
})
stopBluetoothDevicesDiscovery(); //停止搜索
}


/**
* 断开蓝牙连接
*/
function closeBLEConnection() {
//停止搜索
stopBluetoothDevicesDiscovery();

tempStr = ''; //清空

if (deviceId) {
wx.closeBLEConnection({
deviceId: deviceId,
success: function(res) {
console.log("closeBLEConnection。success", res);

},
fail: function(res) {
console.log("closeBLEConnection。fail", res);
},
complete: function() {
status = false;
}
})

wx.closeBluetoothAdapter({
success: function(res) {
console.log("closeBluetoothAdapter ==>res:", res);
},
fail: function(error) {
console.log("closeBluetoothAdapter ==>error:", error);
}
})
}

_discoveryStarted = false;
isnotExist = true;
_deviceId = '';
deviceId = '';
}


/**
* 获取蓝牙所有服务
*/
function getBLEDeviceServices(deviceId) {
wx.onBLEConnectionStateChange(function(res) {
console.log("onBLEConnectionStateChange:", res);
// 该方法回调中可以用于处理连接意外断开等异常情况
console.log(`device ${res.deviceId} state has changed, connected: ${res.connected}`)
if (res.connected == false) {
console.log("连接意外断开等****", _deviceId);
_deviceId = '';
if (flagFromTypes == 1 && flagFromTypes == 2) {
asddErrorCallback(1010, ""); //?
}
}
});

wx.getBLEDeviceServices({
deviceId: deviceId,
success: (res) => {
// console.log("获取蓝牙设备所有服务(service)", res);
for (let i = 0; i < res.services.length; i++) {
let tmpUuid = res.services[i].uuid;
if ((res.services[i].isPrimary) && (tmpUuid.indexOf(filterServiceUUID) != -1)) {
getBLEDeviceCharacteristics(deviceId, res.services[i].uuid)
return
}
}
},
fail: (res) => {
console.log('getBLEDeviceServices fail', res);
asddErrorCallback(res.errCode, "");
}
})
}


/**
* 获取蓝牙特征值
*/
function getBLEDeviceCharacteristics(deviceId, serviceId) {
wx.getBLEDeviceCharacteristics({
deviceId: deviceId,
serviceId: serviceId,
success: (res) => {
// console.log('蓝牙设备特征值信息:', res);
for (let i = 0; i < res.characteristics.length; i++) {
let item = res.characteristics[i]
var itemUUID = item.uuid.toUpperCase(); //转大写

if (item.properties.read && itemUUID == writeUUID) {
wx.readBLECharacteristicValue({
deviceId: deviceId,
serviceId: serviceId,
characteristicId: item.uuid,
})
}
if (item.properties.write && itemUUID == writeUUID) {
_deviceId = deviceId
_serviceId = serviceId
_characteristicId = item.uuid

//发送指令【说明:如需连接设备后发相关指令可以在这里调用】
if (flagFromTypes == 1) { //充电宝
powerBank.send(); //充电开机指令
} else if (flagFromTypes == 2) { //售卖机
vendingObj.checkEnKey(); //AES校验
}
}

if (notifyUUID == itemUUID) {
if (item.properties.notify || item.properties.indicate) {
console.log('调用notifyBLECharacteristicValueChange前', item.uuid);
wx.notifyBLECharacteristicValueChange({
deviceId: deviceId,
serviceId: serviceId,
characteristicId: item.uuid,
state: true,
success(res) {
console.log('notification通知数据', res);
status = true;
// wx.hideLoading();
},
fail(res) {
console.log('notifyBLECharacteristicValueChange fali', res);
}
})
}
}
}
},
fail: (res) => {
console.log('getBLEDeviceCharacteristics fail', res)
asddErrorCallback(res.errCode, "");
}
})


// 操作之前先监听,保证第一时间获取数据
wx.onBLECharacteristicValueChange(function(res) {
console.log(`characteristic ${res.characteristicId} has changed, now is ${res.value}`)
console.log("操作类型:" + action_type);

var resData = ab2hex(res.value);
console.log("设备返回数据--->", resData); //5d0000000001be304d

// 判断不同类型处理数据
if (flagFromTypes == 2) {
console.log('开始调用 自动售卖机====> 处理返回的数据');
vendingObj.handleResData(resData); //处理返回数据
}
})
}

/**
* 写入数据
*/
function writeData(hex, action = '') {
if (!status) {
return;
}

if (!_deviceId) {
asddWriteErrors('w');
return;
}

setTimeout(() => {
//这里使用`TypedArray视图`中`Uint8Array(无符号 8 位整数)`操作
var enDataBuf = new Uint8Array(hex);
var buffer1 = enDataBuf.buffer

wx.writeBLECharacteristicValue({
deviceId: _deviceId,
serviceId: _serviceId,
characteristicId: _characteristicId,
value: buffer1,
success: (res) => {
wx.hideLoading();
console.log("写数据返回结果", res.errMsg);
},
fail(res) {
console.log("写数据失败..", res);
asddErrorCallback(res.errCode, "");
}
})
}, 1000)
}

5. 处理设备返回数据操作指令(如何发指令?)

售卖机为例, 注意:指令格式根据文档说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
/**
* 售卖机
*/
var vendingObj = {
/**
* 校验AES**
* 字符、key文档有说明
*/
checkEnKey: function() {
status = true;
var strKey = [0xd0, 0xf0, 0xf0, 0x80, 0x50, 0xa0, 0x60, 0x10, 0x20, 0x50, 0xc0, 0xd0, 0x80, 0x80, 0x40, 0x90];
var key = [0xd0, 0xf0, 0xf0, 0x80, 0x50, 0xa0, 0x60, 0x10, 0x20, 0x50, 0xc0, 0xd0, 0x80, 0x80, 0x40, 0x90];
var cryptoKey = new Uint8Array(key);
enKEY = cryptoKey;
//得出加密后的指令, 十六进制的数据
var enHEX = cryptoService.updateEncrypt(strKey, enKEY);
writeData(enHEX);
},

/**
* 查询设备信息
*/
queryDeviceInfo: function() {
action_type = OptionEnum.ReadBattery; //改变操作类型
let hex = [0x69, 0xf2, 0x00, 0x89];
writeData(hex); //写入数据
},

/**
* 开锁指令
*/
openVendingLock: function(callBack) {
status = true;
action_type = OptionEnum.VendingOpenLock;
asddCallBack = callBack;
//获取当前开锁的编号及转换
let getCurrentVal = Number(currentSerialVal);
getCurrentVal = getCurrentVal.toString(16);
// let tempVal = '0x0' + getCurrentVal;
let tempVal = parseInt(getCurrentVal, 16);
console.log('====开锁编号===》', tempVal);
let hex = [0xfe, 0x4e, 0x30, 0x46, 0x4a, 0x00, tempVal, 0x00, 0x00, 0x3e];
writeData(hex);
},

/**
* 处理格子机查询信息电量回调
* 目的:获取到相关数据,发送给后端(查看设备电量)
*/
readBatteryCallBack: function(battery) {
console.log("=======>>>电量:", battery);
action_type = OptionEnum.None;

//这里获取到电量, 返回给index.js页面
if (asddCallBack != null) {
asddCallBack(battery, OptionEnum.ReadBattery);
} else {
console.log("是否为空=======标签 2");
}
},


/**
* 处理开锁成功回调
*/
openLockCallback: function(resData) {
var isOpenLock = false;
// 根据当前点击的柜子序号,截取对应的数据状态
var star = Number(currentSerialVal) * 2
var subSerial = resData.substring(star, star + 2);
if (subSerial.toUpperCase() == 'F0') {
isOpenLock = true;
}
console.log("=======>>>开锁:", isOpenLock ? "成功" : "失败");
action_type = OptionEnum.None;

if (asddCallBack != null) {
asddCallBack(isOpenLock, OptionEnum.VendingOpenLock);
} else {
console.log("是否为空=======标签 3");
}
},


/**
* 处理返回数据
* 例如: 00f05d09000001be304d
*/
handleResData: function (resData) {
let checkStatus = resData.substring(2, 4);

if (checkStatus.toUpperCase() == 'F0' && action_type == OptionEnum.Connection) { //校验状态
vendingObj.queryDeviceInfo(); //查询设备信息

} else if (action_type == OptionEnum.ReadBattery) { //操作的是获取设备电量

let batteryVal = resData.substring(6, 8);
batteryVal = parseInt(batteryVal, 16);
vendingObj.readBatteryCallBack(batteryVal);

} else if (action_type == OptionEnum.VendingOpenLock) { //操作的是 开锁

vendingObj.openLockCallback(resData);
}
}
}

6. 处理回调一些方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
//设置连接
function setConnectionActionType(callBack) {
action_type = OptionEnum.Connection;
asddCallBack = callBack;
}

//设置重连
function setReconnectionActionType() {
action_type = OptionEnum.Reconnection;
}

// 设置错误
function setAsddErrorCallback(callBack) {
asddErrorCallback = callBack;
}

//设置关闭
function setCloseActionType(callBack) {
action_type = OptionEnum.CloseLock;
asddCallBack = callBack;
}

//设置写入错误
function setWriteError(callBack) {
asddWriteErrors = callBack;
}

//清除
function clearCallBack() {
asddCallBack = null;
}

/**
* 清空loadding
*/
function hideLoading() {
wx.hideLoading();
}


/**
* 检查是否打开蓝牙
* 未连接设备前检测
*/
function checkIsOpenBluetooth(isEXec) {
wx.openBluetoothAdapter({
success: (res) => {
// console.log('openBluetoothAdapter 初始化蓝牙模块是否成功:', res);
isEXec(true);
},
fail: (res) => {
// console.log('初始化蓝牙失败', res);
wx.showModal({
title: '提示',
content: '请检查手机蓝牙是否打开',
showCancel: false
})

isEXec(false);
}
})
}

7. 蓝牙连接过程中错误码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
/**
* 蓝牙连接过程中错误码
* 10000 / 10006
*/
function bluetoothStatus(errorType) {
switch (errorType) {
case 10001:
wx.showModal({
title: '提示',
content: '请检查手机蓝牙是否打开',
showCancel: false
})
break;
case 10002:
wx.showToast({
title: '没有找到指定设备',
icon: 'none'
})
break;
case 10003:
wx.showToast({
title: '连接失败',
icon: 'none'
})
closeBLEConnection();
break;
case 10004:
wx.showToast({
title: '没有找到指定服务',
icon: 'none'
})
closeBLEConnection();
break;
case 10005:
wx.showToast({
title: '没有找到指定特征值',
icon: 'none'
})
closeBLEConnection();
break;
case 10007:
case 10008:
case 10013:
wx.showToast({
title: '设备启动失败,请重试',
icon: 'none'
})
break;
case 10009:
wx.showModal({
title: '提示',
content: '当前系统版本过低,请更新版本体验',
showCancel: false
})
break;
case 10012:
wx.showToast({
title: '连接超时',
icon: 'none'
})
break;
}
}

8. 导出方法

外部需使用的方法,注意导出。否则无法使用

1
2
3
4
5
6
7
8
9
10
11
12
13
module.exports = {
initBle: initBle,
clearCallBack: clearCallBack,
closeBLEConnection: closeBLEConnection,
setConnectionActionType: setConnectionActionType,
setReconnectionActionType: setReconnectionActionType,
setAsddErrorCallback: setAsddErrorCallback,
setCloseActionType: setCloseActionType,
setWriteError: setWriteError,
checkIsOpenBluetooth: checkIsOpenBluetooth,
bluetoothStatus: bluetoothStatus,
openVendingLock: vendingObj.openVendingLock,
}

二 、在需要页面中调用蓝牙

1. 引入文件

1
2
3
const app = getApp()
let commonBLE = require('../../../../../utils/fromCategory/commonBLE.js'); //公共BLE
let commonBLEDatas = null; //保存当前页面对象

2. 全局中定义处理回调函数

全局方法中如何给DATA中属性赋值
  • 定义一个全局变量,来保存当前页面对象
  • onLoad()中给变量赋值, 即commonBLEDatas = this
  • 使用: 通过变量名.data.属性,即commonBLEDatas.data.battery = obj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
/**
* 处理返回部分数据回调函数
* obj: 传过来的数据
* types: 当前操作类型 【可在`commBLE.js`查看定义的】
*/
let callBack = function(obj, types) {
console.log("index.js回调函数" + obj, types);
// commonBLE.clearCallBack();
if (commonBLEDatas.data.currentMeasureType == 4) { //售卖机
if (types == 10) {
//给电量赋值
commonBLEDatas.data.battery = obj;

//调用开锁指令
commonBLEDatas.successOpenLock();
}
}
}

/**
* 处理错误码回调
*/
let errorCallBack = function(errorType, errorMessage) {
// feedbackApi.hideTimerToast(); //清空loadding
commonBLEDatas.deviceConnectFail(); //展示 设备连接失败
if (errorType == 10000 || errorType == 10006) {
//连接蓝牙
commonBLE.setReconnectionActionType();
commonBLE.setAsddErrorCallback(errorCallBack);
commonBLE.setWriteError(writeError);
if (commonBLEDatas.data.currentMeasureType == 4) {
commonBLE.initBle(commonBLEDatas.data.macAdress, OptionEnumType.Vending, commonBLEDatas.data.currentSerial);
}
} else {
commonBLE.bluetoothStatus(errorType);
}
}


/**
* 写入失败回调
*/
let writeError = function(writeE) {
console.log('写入数据状态', writeE);
//写入错误页面展示状态
commonBLEDatas.setData({
connectStatus: 1,
isConnect: 2,
clickFlags: false
})

if (writeE == 'w') {
feedbackApi.hideTimerToast(); //清空loadding
clearInterval(commonBLEDatas.downSecondId); //清空倒计时

wx.showToast({
title: '连接失败,请再次尝试(0)',
icon: 'none'
})
commonBLE.closeBLEConnection();
}
}

3. 调用执行蓝牙方法

  • 点击购买商品按钮, 执行此方法。
  • 传参: 当前设备的mac地址(后台获取的)操作的产品类型操作序号(操作几号柜子)

说明: 柜子序号1,2,3,4…, 和页面展示商品对应的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
Page({

/**
* 页面的初始数据
*/
data: {
currentMeasureType: 2, //测试 当前类型售卖机
macAdress: '3b3533115510', //mac地址(这里后台保存时没有:号的,可自行处理)
currentSerial: '', //当前操作格子序号
},

/**
* 生命周期函数--监听页面加载
*/
onLoad: function(options) {
commonBLEDatas = this;
},

/**
* 连接BLE
*/
connectVendingBLE: function() {
let that = this;
commonBLE.setConnectionActionType(callBack); //连接后操作回调
commonBLE.setAsddErrorCallback(errorCallBack); //执行错误时错误码回调
commonBLE.setWriteError(writeError); //写入数据错误回调
if (that.data.currentMeasureType == 2) {
commonBLE.initBle(that.data.macAdress, OptionEnumType.Vending, that.data.currentSerial);
}
},

/**
* 设备连接失败
*/
deviceConnectFail: function () {
// feedbackApi.hideTimerToast(); //清空loadding
clearInterval(this.downSecondId); //清空倒计时
this.setData({
descTxt2: '设备连接失败',
connectStatus: 1,
isConnect: 2,
clickFlags: false
})
},
})

4. 执行开锁

如获取电量成功后, 执行开锁指令,成功后页面展示对应【具体流程根据需求来】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* 售卖机开锁成功后提交数据 并展示相关的ui
* 写在 Page({})中的, 这里为了说明写在外面
*/
successOpenLock: function() {
let that = this;
// 调用开锁及处理回调数据
commonBLE.openVendingLock(function(isOpenLock, obj) {
console.log('处理开锁指令回调====》' + isOpenLock);
commonBLE.clearCallBack();

var tempStr = "失败";
if (isOpenLock) {
tempStr = "成功";
// 提交数据并展示成功开锁后Ui
that.showSuccessBack();
}else {
wx.showToast({
title: "开锁" + tempStr,
duration: 3000,
image: '/page/common/libs/assets/images/error.png',
})

that.deviceConnectFail(); //展示 设备连接失败
//断开蓝牙连接
commonBLE.closeBLEConnection();
}
});
},

原文下载

原文下载请点击

版权声明:本文为Smile_ping原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

本文链接:https://blog.csdn.net/Smile_ping/article/details/103148061