本文是一个完整的对接设备,发送不同指令监听不同返回的完整示例,可根据实际项目按需更改。
注:app.showModal
为在app.js中封装的showModal方法,then(()=>{})
代表用户点击confirm
,可用wx.showModal
代替。
公用方法
function inArray(arr, key, val) {
for (let i = 0; i < arr.length; i++) {
if (arr[i][key] === val) {
return i;
}
}
return -1;
}
function split_array(arr, len) {
var a_len = arr.length;
var result = [];
for (var i = 0; i < a_len; i += len) {
result.push(arr.slice(i, i + len));
}
return result;
}
function calcCrc(dataView) {
//计算指令的合取最低8位,是因为这个设备的指令是这个需求,实际按设备对接文档来
let crc = 0;
for (let i = 0; i < 15; i++) {
crc += dataView.getUint8(i);
}
return crc
}
/**
*ArrayBuffer转16进制字符串
* @param {buffer} buffer
*/
function ab2hex(buffer) {
let hexArr = Array.prototype.map.call(
new Uint8Array(buffer),
function (bit) {
return ('00' + bit.toString(16)).slice(-2)
}
)
return hexArr.join('');
},
/**
* 16进制字符串转字节数组
* @param {string} str
*/
function str2Bytes(str) {
let pos = 0;
let len = str.length;
if (len % 2 != 0) {
return null;
}
len /= 2;
let hexA = new Array();
for (let i = 0; i < len; i++) {
let s = str.substr(pos, 2);
let v = s; //处理
hexA.push(v);
pos += 2;
}
return hexA;
},
/**
* 反转
* @param {string} num
*/
function reverse(num) {
return num.split(',').reverse().join('');
},
请求设备列表
getList() {
let ble = wx.getStorageSync('bleLink') || false; //设备是否连接中的变量,跳转其他页面也可使用
wx.showLoading({
title: '加载中...'
});
app.get("/getDevice").then(res => {
if (res.data.data.length > 0) {
console.log('有设备')
let data = res.data.data[0];
data.createTime = util.datatotime(data.createTime);
this.setData({
deviceList: [data],
deviceId: data.deviceId,
deviceName: data.deviceName,
hasDevice: true,
ble
})
this._discoveryStarted = false;
this._deviceId = data.deviceId;
console.log('--------- getList', this.data.deviceList)
if (!ble) {
console.log('已绑定设备但是未连接')
this.settingBlue();
} else {
console.log('已绑定设备但是已连接')
this.getBLEDeviceServices(this._deviceId);
}
} else {
console.log('暂无设备')
this._discoveryStarted = false;
}
})
},
1. 判断是否有蓝牙权限
/**
* 开始蓝牙
*/
settingBlue() {
wx.showLoading({
title: '获取蓝牙中...',
})
wx.getSetting({
success: (res) => {
wx.hideLoading()
if (res.authSetting.hasOwnProperty('scope.bluetooth')) {
//'scope.bluetooth'属性存在,且为false
if (!res.authSetting['scope.bluetooth']) {
//拒绝授权 弹窗授权
app.showModal({
content: '您还未授权使用蓝牙,请点击“确定”开启蓝牙授权~'
}).then(() => {
wx.openSetting({
success(res) {
//监听到用户同意授权蓝牙
this.openBluetoothAdapter();
}
})
})
} else {
this.openBluetoothAdapter();
}
} else
//'scope.bluetooth'属性不存在,需要授权
wx.authorize({
scope: 'scope.bluetooth',
success() {
this.openBluetoothAdapter();
}
})
}
})
}
2. 初始化蓝牙
openBluetoothAdapter() {
let _this = this;
wx.showLoading({
title: '连接中...',
})
wx.openBluetoothAdapter({
success: (res) => {
console.log('蓝牙初始化成功', res)
wx.hideLoading();
this.startBluetoothDevicesDiscovery()
},
fail: (res) => {
wx.hideLoading();
//针对一些特殊的错误返回进行不同的操作及提示,详细请看下文超链接
if (res.errCode === 10001) {
app.showModal({
content: '您当前未开启蓝牙,请打开手机蓝牙后再试~',
showCancel: 2 // 代表只有一个确认按钮
}).then(res => {
wx.onBluetoothAdapterStateChange(function (res) {
// 监听蓝牙状态变化
if (res.available) {
wx.showToast({
title: '蓝牙已开启',
icon: 'none',
duration: 3000,
})
this.startBluetoothDevicesDiscovery()
} else {
wx.showToast({
title: '蓝牙未开启',
icon: 'none',
duration: 3000,
})
}
})
});
} else {
app.showModal({
content: res.errMsg,
showCancel: 2
}).then(res => {
});
}
}
})
},
wx.openBluetoothAdapter 错误代码详细文章来源:https://www.toymoban.com/news/detail-841798.html
3. 开始搜寻附近的蓝牙外围设备及停止搜索设备的api
<van-popup show="{{ show }}" position="bottom" round closeable bind:close="onClosePopup">
<view class="devices_summary">
<view>
已发现 {{devices.length}} 个设备:
</view>
</view>
<view class="list_loading" wx:if="{{devices.length == 0 && loading}}">
<van-loading type="spinner"></van-loading>蓝牙搜索中...
</view>
<view class="null" wx:if="{{devices.length == 0 && !loading}}">
<image src="/images/null.png" mode="widthFix" />
<view>搜索不到设备,请把设备靠近手机再试哦~</view>
</view>
<view class="list_loading" wx:if="{{devices.length == 0 && !loading}}" bindtap="settingBlue">
<van-icon name="replay" />重新搜索
</view>
<scroll-view class="device_list" scroll-y scroll-with-animation>
<view wx:for="{{devices}}" wx:key="idx" data-device-id="{{item.deviceId}}" data-name="{{item.name || item.localName}}" bindtap="createBLEConnection" class="device_item" hover-class="device_item_hover">
<view> {{item.name}} </view>
<view class="btn"> 连 接 </view>
<!-- <view style="font-size: 10px">信号强度: {{item.RSSI}}dBm ({{tools.max(0, item.RSSI + 100)}}%)</view>
<view style="font-size: 10px">UUID: {{item.deviceId}}</view>
<view style="font-size: 10px">Service数量: {{tools.len(item.advertisServiceUUIDs)}}</view> -->
</view>
</scroll-view>
</van-popup>
startBluetoothDevicesDiscovery() {
if (this._discoveryStarted) {
//正在搜索
return
}
this._discoveryStarted = true
wx.showLoading({
title: '连接中...',
})
wx.startBluetoothDevicesDiscovery({
allowDuplicatesKey: true,
success: (res) => {
console.log('startBluetoothDevicesDiscovery success', res)
if (this._deviceId == '') {
console.log('没有绑定设备,重新搜索')
wx.hideLoading()
this.onBluetoothDeviceFound()
} else {
//已有设备的话直接连接
console.log('已有设备的话直接连接')
wx.hideLoading()
this.createBLEConnection({
currentTarget: {
dataset: {
deviceId: this._deviceId,
name: this.data.deviceName
}
}
})
}
},
})
},
//停止蓝牙搜索外围设备
stopBluetoothDevicesDiscovery() {
wx.stopBluetoothDevicesDiscovery()
},
//监听搜索到新设备
onBluetoothDeviceFound() {
this.setData({
show: true, //展示搜索到的设备列表
loading: true //搜索加载状态
})
wx.onBluetoothDeviceFound((res) => {
res.devices.forEach(device => {
if (!device.name && !device.localName) {
return
}
//只显示包含 HDS 的设备(按实际设备名称更改)
if ((device.name && device.name.includes('HDS')) || (device.localName && device.localName.includes('HDS'))) {
const foundDevices = this.data.devices
const idx = inArray(foundDevices, 'deviceId', device.deviceId)
const data = {};
if (idx === -1) {
//不存在时新增
data[`devices[${foundDevices.length}]`] = device
} else {
//设备列表已存在,替换
data[`devices[${idx}]`] = device
}
this.setData(data)
}
setTimeout(() => {
//搜索设备超时,停止搜索
this._discoveryStarted = false
if (this.data.loading) {
this.setData({
loading: false
})
if (this.data.devices.length == 0) {
wx.closeBluetoothAdapter()
}
}
}, 15000)
})
})
},
4. 绑定设备及连接设备
/**
* 绑定设备接口
*/
createBLEConnection(e) {
const {
deviceId,
name
} = e.currentTarget.dataset;
console.log('createBLEConnection: ', name);
wx.showLoading({
title: '连接中...',
})
// 如果请求设备列表接口,这个设备已经在接口返回中,则不需要请求新增设备的接口
if (this.data.hasDevice) {
console.log('createBLEConnection----已绑定设备,直连')
this.createBLEConnectionFn(deviceId, name);
} else {
console.log('createBLEConnection----没有绑定设备,新增')
let param = {
deviceId,
deviceName: name
};
app.post('/addDevice', param).then(res => {
wx.showLoading({
title: '连接中...',
})
this.setData({
show: false,
})
//重新请求设备列表
this.getList();
});
}
},
/**
* 连接设备蓝牙
*/
createBLEConnectionFn(deviceId, name) {
let deviceList = this.data.deviceList,
obj = {
deviceName: name,
deviceId
};
wx.createBLEConnection({
deviceId,
success: (res) => {
console.log('createBLEConnection success:', res);
obj.addTime = util.datatotime(new Date());
if (deviceList.length == 0) deviceList.push(obj);
this.setData({
show: false,
ble: true,
deviceList
})
wx.hideLoading()
wx.setStorageSync('bleLink', true);
wx.setStorageSync('deviceId', deviceId);
wx.setStorageSync('deviceName', name);
setTimeout(() => {
this.getBLEDeviceServices(deviceId);
}, 300)
},
fail: (err) => {
console.log('createBLEConnection fail:', err)
wx.hideLoading()
if (err.errCode === 10002) {
app.showModal({
content: '未搜索到设备,请您把设备靠近手机后重新添加设备~',
showCancel: 2
}).then(res => {
// this.destoryDevice();
});
} else if (err.errCode === 10003) {
app.showModal({
content: '连接失败,请您把设备靠近手机后重新添加设备~',
showCancel: 2
}).then(res => {
// this.destoryDevice();
});
} else if (err.errCode === 10012) {
app.showModal({
content: '连接超时,请重试',
showCancel: 2
}).then(res => {
// this.destoryDevice();
});
} else {
app.showModal({
content: err.errMsg,
showCancel: 2
}).then(res => {
// this.destoryDevice();
});
}
}
})
this.stopBluetoothDevicesDiscovery()
},
5. 连接上某个设备后获取特征值
/**
* 获取服务
* @param {*} deviceId
*/
getBLEDeviceServices(deviceId) {
wx.showLoading({
title: '加载中...',
})
wx.getBLEDeviceServices({
deviceId,
success: (res) => {
for (let i = 0; i < res.services.length; i++) {
//根据设备对接文档提供的Service UUID进行判断
if (res.services[i].isPrimary && res.services[i].uuid.substr(0, 8) ==
'0000FFF0') {
this.getBLEDeviceCharacteristics(deviceId, res.services[i].uuid)
return
}
}
},
complete(err) {
wx.hideLoading();
console.log('getBLEDeviceServices:', err)
}
})
},
/**
* 获取特征值
* @param {*} deviceId
* @param {*} serviceId
*/
getBLEDeviceCharacteristics(deviceId, serviceId) {
let self = this;
wx.getBLEDeviceCharacteristics({
deviceId,
serviceId,
success: (res) => {
console.log('获取服务的信息包括特征值', res)
for (let i = 0; i < res.characteristics.length; i++) {
const item = res.characteristics[i];
if ((item.properties.notify || item.properties.indicate) && item.uuid.substr(0, 8) == '0000FFF7') {
//监听指令返回的判断,根据设备对接文档提供的可读特征UUID进行判断
wx.notifyBLECharacteristicValueChange({
deviceId: deviceId,
serviceId: serviceId,
characteristicId: item.uuid,
state: true,
success: (res) => {
console.log('notifyBLECharacteristicValueChange success--', res);
this.openCall();
wx.showLoading({
title: '获取数据中...',
})
this.getDatas();
},
fail: (err) => {
console.error('notifyBLECharacteristicValueChange error--', err);
}
})
}
//用于发送指令,根据设备对接文档提供的可写特征UUID进行判断
if (item.properties.write && item.uuid.substr(0, 8) == '0000FFF6') {
this._deviceId = deviceId;
this._serviceId = serviceId;
this._characteristicId = item.uuid;
}
}
}
})
},
6. 发送指令(对,终于到这一步了)
/**
* 发送指令列表
*/
getDatas() {
this.getEleNum();
this.getInfo();
setInterval(() => {
this.getEleNum();
}, 60 * 10 * 1000)
},
/**
* 电量
*/
getEleNum() {
/**
文档提供的命令格式为0x01 AA 00 00 00 00 00 00 00 00 00 00 00 00 00 CRC
AA:0X99,现在进行一次电量检测
*/
let buffer = new ArrayBuffer(16);
let dataView = new DataView(buffer);
dataView.setUint8(0, 0x01);
dataView.setUint8(1, 0x99);
dataView.setUint8(15, calcCrc(dataView));
this.writeBLE(buffer)
},
/**
* 版本号
*/
getInfo() {
/**
文档提供的命令格式为0x02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 CRC
*/
let buffer = new ArrayBuffer(16);
let dataView = new DataView(buffer);
dataView.setUint8(0, 0x02);
dataView.setUint8(15, 0x02); //只有一个0x02时无需计算
this.writeBLE(buffer)
},
/**
* 写入
*/
writeBLE(buffer) {
wx.writeBLECharacteristicValue({
deviceId: this._deviceId,
serviceId: this._serviceId,
characteristicId: this._characteristicId,
value: buffer,
success: (res) => {
},
fail: (err) => {
console.log("写入失败--", err);
if (err.errCode === 10002) {
app.showModal({
content: '未搜索到设备,请您把设备靠近手机后重新添加设备~',
showCancel: 2
}).then(res => {
});
} else if(err.errCode == 10006){
// 重连
wx.showToast({
title: '已断开连接,重连中...',
})
this.settingBlue();
}
}
})
},
7. 监听发送指令的回调
openCall() {
//监听命令返回事件
let _this = this,
deviceList = this.data.deviceList;
wx.onBLECharacteristicValueChange(function (res) {
wx.hideLoading()
let dataList = str2Bytes(ab2hex(res.value));
// console.log({dataList})
if (dataList[0] == '01') {
//电量
let eleNum = parseInt('0x' + dataList[1])
deviceList[0].eleNum = eleNum;
_this.updateDevice({ eleNum });
}
if (dataList[0] == '02') {
// 设备基础参数
let deviceCode = 'v' + parseInt(dataList[1]) + '.' + parseInt(dataList[2]) + '.' + parseInt(dataList[3]) + '.' + parseInt(dataList[4]) + ' 20' + parseInt(dataList[5]) + '-' + parseInt(dataList[6]) + '-' + parseInt(dataList[7])
deviceList[0].deviceCode = deviceCode;
_this.updateDevice({ deviceCode });
}
}
_this.setData({
deviceList
})
})
},
updateDevice(obj){
let param = {
id: this.data.deviceList[0].id //数据库中的设备id,用于更新设备信息
};
param = {
...param,
...obj
};
//请求接口保存修改设备
}
补充一点:当小程序离开当前页面的时候停止监听蓝牙以及返回页面的时候继续监听
/**
* 结束监听蓝牙蓝牙
*/
offBLECharacteristicValueChange() {
wx.offBLECharacteristicValueChange()
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
if (this._deviceId && wx.getStorageSync('bleLink')) this.getBLEDeviceServices(this._deviceId);
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
this.offBLECharacteristicValueChange();
},
注意事项
如果是进入其他页面,也可以复制以上代码进行监听及发送指令
以及需要按需求在离开小程序或者解绑设备的时候,关闭蓝牙连接释放设备文章来源地址https://www.toymoban.com/news/detail-841798.htmlcloseBLE(){ if(this._deviceId){ wx.closeBLEConnection({ deviceId: this._deviceId }) } wx.closeBluetoothAdapter(); wx.setStorageSync('bleLink', false); }
到了这里,关于保姆级微信小程序对接蓝牙设备教程。微信小程序发送不同蓝牙指令(定时发送,断开重连,判断是否有蓝牙权限等)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!