最近在做一个蓝牙售货机的项目,放在酒店房间内,哈哈,里边是什么自行脑补。
因为需要适配微信小程序和支付宝小程序,所以最后选用uni-app来开发,省时省力更省钱。
第一次对接蓝牙,做一次记录,话不多说,开始上代码。
*uni-app蓝牙连接的整个流程
*1、初始化蓝牙 uni.openBluetoothAdapter(OBJECT)
*2、开始搜索蓝牙设备 uni.startBluetoothDevicesDiscovery(OBJECT)
*3、发现外围设备 uni.onBluetoothDeviceFound(CALLBACK)
*4、停止搜寻附近的蓝牙外围设备 uni.stopBluetoothDevicesDiscovery(OBJECT)
*5、连接低功耗蓝牙设备 uni.createBLEConnection(OBJECT)
*6、获取蓝牙设备所有服务 uni.getBLEDeviceServices(OBJECT)
*7、获取蓝牙特征 uni.getBLEDeviceCharacteristics(OBJECT)
*8、启用蓝牙设备特征值变化时的 notify 功能 uni.notifyBLECharacteristicValueChange(OBJECT)
*9、监听低功耗蓝牙设备的特征值变化 uni.onBLECharacteristicValueChange(CALLBACK)
*10、对需要操作的特征值进行读、写操作
import store from '@/store/index.js'
let _self;
export default class deviceClass {
constructor() {
_self = this;
_self.deviceId = "";
_self.isConnect = false; //是否已连接
_self.isStop = false; //是否停止搜所
_self.UUID_SERVICE = '0000FEE0-0000-1000-8000-00805F9B34FB'; //服务UUID,设备方提供
_self.UUID_CHAR = '0000FEE1-0000-1000-8000-00805F9B34FB'; //特征值UUID,设备方提供
}
/* 初始化并搜索蓝牙操作 */
initEventDevice() {
uni.showLoading({
title: "蓝牙连接中"
})
/* 1、初始化蓝牙 uni.openBluetoothAdapter(OBJECT) */
uni.openBluetoothAdapter({
complete: () => {
/* 监听蓝牙是否开启*/
uni.onBluetoothAdapterStateChange(function(res) {
//res.available 蓝牙适配器是否可用
//res.discovering 蓝牙适配器是否处于搜索状态
console.log('适配器状态变化', res)
if(!res.available && !res.discovering){
_self.isStop = false;
uni.showToast({
duration: 2500,
title: "请打开手机蓝牙",
icon: 'error',
})
store.commit('updateStatus', false);
} else if(_self.isStop==false && res.available && !res.discovering){
console.log("蓝牙已开启,可以执行搜索操作")
_self.initEventDevice();
} else if(res.available && res.discovering){
console.log("蓝牙已开启,并且正在搜索中")
}
})
},
success: (res) => {
/* 初始化蓝牙成功 */
/* 2、开始搜索蓝牙设备 uni.startBluetoothDevicesDiscovery(OBJECT) */
/* 监听蓝牙适配器状态变化事件 可以检测当前设备的蓝牙的断开或者连接的情况*/
_self.startBluetoothDevicesDiscovery();
_self.connectTimeout();
},
fail: (res) => {
console.log("初始化蓝牙失败:" + JSON.stringify(res))
uni.hideLoading();
uni.showToast({
duration: 2500,
title: "请打开手机蓝牙",
icon: 'error',
})
}
})
}
/*搜索蓝牙*/
startBluetoothDevicesDiscovery() {
uni.startBluetoothDevicesDiscovery({
allowDuplicatesKey: false, //是否允许同一设备多次上报,但RSSI值会有所不同
success: (res) => {
console.log("搜索成功:" + JSON.stringify(res))
_self.onBluetoothDeviceFound();
},
fail: (res) => {
uni.showModal({
title: '蓝牙连接失败了哦',
content: `
1、请查看周围是否有连接此设备的蓝牙;
2、请先关闭手机蓝牙在打开一次;
3、请检查位置信息是否打开;
4、退出小程序重新扫码进入。`,
showCancel: false,
confirmText: "我知道了",
success: () => {}
})
console.log("搜索失败:" + JSON.stringify(res))
}
})
}
/* 搜索蓝牙设备回调 */
onBluetoothDeviceFound() {
uni.onBluetoothDeviceFound(function(CALLBACK) {
console.log('搜索蓝牙设备回调===' + JSON.stringify(CALLBACK))
/* 每次遍历指定设备,一旦搜索到指定设备就结束搜索*/
let bluename = JSON.parse(uni.getStorageSync('deviceInfo')).bluename; //需要搜索的设备名称
/* 搜索过程中如果搜索到需要的设备,就停止搜索 */
CALLBACK.devices.forEach(device => {
/* 筛选需要的deviceId */
if (device.deviceId === bluename) {
//找到需要的设备之后,停止搜索
console.log("搜索到指定设备=="+device.deviceId)
_self.isConnect = true;
/*停止搜索*/
_self.stopBluetoothDevicesDiscovery();
/* 执行连接 */
_self.connectDevice(device.deviceId);
}
})
})
}
/*设置蓝牙搜索时间30秒, 如果超时没有搜索到就停止搜索*/
connectTimeout() {
setTimeout(() => {
if(!_self.isConnect) {
uni.hideLoading();
_self.stopBluetoothDevicesDiscovery();
uni.showModal({
title: '蓝牙连接失败了哦',
content: `
1、请查看周围是否有连接此设备的蓝牙;
2、请先关闭手机蓝牙在打开一次;
3、请检查位置信息是否打开;
4、退出小程序重新扫码进入。`,
showCancel: false,
confirmText: "我知道了",
success: () => {}
})
}
}, 30000)
}
/* 停止搜索 */
stopBluetoothDevicesDiscovery() {
uni.stopBluetoothDevicesDiscovery({
complete: () => {
_self.isStop = true;
},
success: (res) => {
console.log("停止搜索成功")
},
fail: (res) => {
console.log("停止失败")
uni.hideLoading();
}
})
}
/* 连接低功耗蓝牙 */
connectDevice(deviceId) {
console.log("执行到这里了")
uni.createBLEConnection({
deviceId, //点击的DeviceId
timeout: 5000,
success: (res) => {
console.log("连接成功")
_self.deviceId = deviceId;
/* 这边获取全部的服务,并筛选出当前需要的*/
_self.getBLEDeviceServices(deviceId)
},
fail: (error) => {
/* 连接失败 */
uni.hideLoading();
console.log("连接失败")
console.log(error);
uni.showModal({
title: '蓝牙连接失败了哦',
content: `
1、请查看周围是否有连接此设备的蓝牙;
2、请先关闭手机蓝牙在打开一次;
3、请检查位置信息是否打开;
4、退出小程序重新扫码进入。`,
showCancel: false,
confirmText: "我知道了",
success: () => {}
})
}
})
/* 监听蓝牙设备的连接状态 */
uni.onBLEConnectionStateChange(function(res) {
console.log(res.deviceId + "连接状态:" + res.connected)
})
}
/* 获取所有的服务*/
getBLEDeviceServices(deviceId) {
console.log("开始获取服务")
//连接成功之后需要延时,继续操作才不会出问题,延时时间短一点都没关系,我这边用了1秒
setTimeout(() => {
uni.getBLEDeviceServices({
// 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
deviceId,
success: (res) => {
console.log('device services:', res)
res.services.forEach((item) => {
//这里微信小程序获取的服务ID是大写,支付宝是小写,所以统一转换为大写
let serviceId = item.uuid.toUpperCase();
if (serviceId == _self.UUID_SERVICE) {
console.log('serverId:', serviceId)
/* 进入特征值 */
_self.getBLEDeviceCharacteristics(deviceId, serviceId);
}
})
},
fail: (error) => {
console.log("获取服务失败")
console.log(error)
uni.hideLoading();
}
})
}, 1000)
}
/* 获取所有特征值 */
getBLEDeviceCharacteristics(deviceId, serviceId) {
console.log("开始获取特征")
setTimeout(() => {
uni.getBLEDeviceCharacteristics({
deviceId,
serviceId,
success: (res) => {
console.log(res)
res.characteristics.forEach((item) => {
console.log(item)
//这里跟服务ID一样,支付宝需要转换为大写
//#ifdef MP-ALIPAY
let characterId = item.characteristicId.toUpperCase();
//#endif
//#ifdef MP-WEIXIN
let characterId = item.uuid;
//#endif
/* 只要用到的唤醒即可 */
if (characterId == _self.UUID_CHAR) {
console.log('characteristicId:', characterId)
_self.notifyBLECharacteristicValueChange(deviceId, serviceId, characterId)
}
})
},
fail: (res) => {
uni.hideLoading();
console.log(res)
}
})
}, 1000)
}
/* 启用蓝牙设备特征值变化时的 notify 功能 */
notifyBLECharacteristicValueChange(deviceId, serviceId, characteristicId) {
console.log("开始唤醒")
uni.notifyBLECharacteristicValueChange({
state: true, // 启用 notify 功能
// 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
deviceId,
// 这里的 serviceId 需要在 getBLEDeviceServices 接口中获取
serviceId,
// 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取
characteristicId,
success: (res) => {
uni.hideLoading();
/* 连接成功 */
uni.showToast({
title: "连接成功",
icon: 'success'
});
//储存蓝牙连接状态
store.commit('updateStatus', true);
console.log('notifyBLECharacteristicValueChange success', res.errMsg)
/* 连接成功以后,执行设置的操作,并且打开返回值的监听开关并监听 */
//获取设备信息
let DeviceCommand = "01,02,03,04," + func("01020304") + ",1E";
let DeviceArrayBuffer = string2buffer(DeviceCommand);
_self.writeDevice(DeviceArrayBuffer);
//出货后关门延时设置
let delayedCommand = "01,02,03,04,0A," + func("010203040A") + ",1E";
let delayedArrayBuffer = string2buffer(delayedCommand);
_self.writeDevice(delayedArrayBuffer);
/* 监听低功耗蓝牙设备的特征值变化 */
uni.onBLECharacteristicValueChange(function(CALLBACK) {
let result = blueTool.ab2hex(CALLBACK.value);
console.log("收到的数据值==", result);
// 这里做一些具体的业务逻辑
//我这里是监听低功耗蓝牙设备的特征值变化,然后调后台接口获取设备的电量,展示在页面
if ("73080182" === result.substring(0, 8)) {
var o = result.substring(8, 10);
o = parseInt(o, 16);
let dl = "";
if (o >= 41) {
dl = 100;
} else if (o <= 37) {
dl = 0;
} else {
dl = (o - 37) * 100 / (41 - 37);
}
//获取设备电量
uni.request({
url: baseUrl+"/api/*****",
method: "POST",
data: {
key: value
},
header: {
"Content-Type": "application/x-www-form-urlencoded"
},
success: function(t) {
store.commit('setDeviceBattery', t.data.data);
console.log("设备上传回调" + JSON.stringify(t.data));
},
fail: function(t) {}
});
}
})
},
fail: (res) => {
uni.hideLoading();
console.log('notifyBLECharacteristicValueChange success', res.errMsg)
}
})
}
/* 执行写入命令操作 */
writeDevice(_Buffer) {
uni.writeBLECharacteristicValue({
deviceId: _self.deviceId,
serviceId: _self.UUID_SERVICE,
characteristicId: _self.UUID_CHAR,
value: _Buffer,
success: (res) => {
/* 发送成功 */
console.log('最后一次写入成功', res)
},
fail: (error) => {
/* 发送失败 */
console.log('最后一次写入失败', error)
}
})
}
/* 获取本机蓝牙适配器状态 */
getBluetoothState() {
uni.getBluetoothAdapterState({
success: (res) => {
console.log("状态" + JSON.stringify(res))
}
})
}
/* 关闭蓝牙适配器*/
closeBlueAdapter() {
uni.closeBluetoothAdapter({
success() {
console.log("关闭蓝牙适配器成功")
}
})
}
/* 断开低功耗蓝牙的连接 */
disconnectBle() {
uni.closeBLEConnection({
deviceId: _self.deviceId,
success: (res) => {
console.log("断开成功" + res)
store.commit('updateStatus', false);
store.commit('setDeviceBattery', 0);
}
})
},
/* 具体的命令BCC校验*/
func(t) {
for (var e = t, a = [], i = 0; i < e.length - 1; i += 2) a.push(e.substring(i, i + 2));
for (var o = a[0], s = 0; s < a.length - 1; s++) o ^= a[s + 1];
return o;
},
/* 字符串转arraybuffer */
string2buffer: function(str) {
// 首先将字符串转为16进制
let val = str
console.log(val)
// 将16进制转化为ArrayBuffer
return new Uint8Array(val.match(/[\da-f]{2}/gi).map(function(h) {
return parseInt(h, 16)
})).buffer
},
}