本文将提供一个从零开始、端到端的微信小程序BLE开发流程,包含完整的代码示例和注意事项。
1. 准备工作:权限配置
在调用任何蓝牙API之前,必须在小程序的全局配置文件 app.json
中声明所需权限。
json
{
"permission": {
"scope.bluetooth": {
"desc": "需要使用蓝牙功能连接您的设备"
},
"scope.userLocation": {
"desc": "需要您的位置信息以搜索蓝牙设备"
}
}
}
注意 :在Android系统上,搜索蓝牙设备需要用户授权地理位置信息,因此建议同时声明 scope.userLocation
权限。
2. 核心开发流程与代码示例
以下是标准的BLE通信流程,建议将相关逻辑封装在独立的模块中以便于管理和复用。
第1步:初始化蓝牙模块
这是所有操作的起点。调用 wx.openBluetoothAdapter
来初始化小程序与手机蓝牙模块的会话。
ts
// bluetooth.js
function initBluetooth() {
wx.openBluetoothAdapter({
success: (res) => {
console.log('初始化蓝牙模块成功', res);
// 成功后,可以开始搜索设备
startDeviceDiscovery();
},
fail: (res) => {
console.error('初始化蓝牙模块失败', res);
if (res.errCode === 10001) { // 常见错误:蓝牙未开启
wx.showToast({
title: '请开启手机蓝牙',
icon: 'none',
duration: 2000
});
// 可以监听蓝牙适配器状态,在用户开启后自动重试
wx.onBluetoothAdapterStateChange(function(res) {
if (res.available) {
initBluetooth();
}
});
}
}
});
}
第2步:开始搜索周边设备
初始化成功后,调用 wx.startBluetoothDevicesDiscovery
开始扫描。
ts
function startDeviceDiscovery() {
wx.showLoading({ title: '正在搜索设备...' });
wx.startBluetoothDevicesDiscovery({
// services: ['FEE7'], // 可选:根据主服务UUID过滤,可提升效率
allowDuplicatesKey: false, // 不允许重复上报同一个设备
success: (res) => {
console.log('开始搜索成功', res);
// 监听新发现的设备
onDeviceFound();
},
fail: (err) => {
console.error('搜索设备失败', err);
}
});
}
最佳实践 :搜索操作非常耗电。一旦找到目标设备并开始连接,应立即调用 wx.stopBluetoothDevicesDiscovery()
停止搜索。
第3步:监听并获取发现的设备
通过 wx.onBluetoothDeviceFound
监听扫描到的新设备,并维护一个设备列表。
ts
let foundDevices = []; // 用于存储和去重
function onDeviceFound() {
wx.onBluetoothDeviceFound((res) => {
res.devices.forEach(device => {
// 过滤掉无名称或信号弱的设备
if (!device.name && !device.localName) return;
const isExist = foundDevices.some(item => item.deviceId === device.deviceId);
if (!isExist) {
foundDevices.push(device);
// 更新UI,展示设备列表给用户选择
// getApp().globalData.devices = foundDevices;
console.log('发现新设备:', device.name || device.localName, device.deviceId);
}
});
});
}
第4步:连接选定的设备
用户选择设备后,使用其 deviceId
调用 wx.createBLEConnection
发起连接。
ts
function connectToDevice(deviceId) {
wx.showLoading({ title: '正在连接...' });
// 连接前务必停止搜索
wx.stopBluetoothDevicesDiscovery();
wx.createBLEConnection({
deviceId: deviceId,
timeout: 10000, // 超时时间10秒
success: (res) => {
console.log('设备连接成功', res);
wx.hideLoading();
// 连接成功,下一步是获取设备的服务
getDeviceServices(deviceId);
},
fail: (err) => {
console.error('设备连接失败', err);
wx.hideLoading();
wx.showToast({ title: '连接失败,请重试', icon: 'none' });
}
});
}
第5步:获取设备的所有服务(Service)
连接成功后,必须先获取设备提供的服务列表,才能进行后续操作。
ts
function getDeviceServices(deviceId) {
wx.getBLEDeviceServices({
deviceId: deviceId,
success: (res) => {
console.log('获取设备服务成功:', res.services);
// 通常需要根据业务协议,找到特定的服务UUID
const targetService = res.services.find(service => service.uuid.includes('YOUR_SERVICE_UUID'));
if (targetService) {
getDeviceCharacteristics(deviceId, targetService.uuid);
} else {
wx.showToast({ title: '未找到目标服务', icon: 'none' });
}
},
fail: (err) => {
console.error('获取设备服务失败', err);
}
});
}
第6步:获取服务下的特征值(Characteristic)
获取到服务后,再获取该服务下的所有特征值,这些特征值是数据读写的真正端点。
ts
let writeCharacteristicId = ''; // 用于写入的特征值ID
let notifyCharacteristicId = ''; // 用于接收通知的特征值ID
function getDeviceCharacteristics(deviceId, serviceId) {
wx.getBLEDeviceCharacteristics({
deviceId: deviceId,
serviceId: serviceId,
success: (res) => {
console.log('获取特征值成功:', res.characteristics);
for (let char of res.characteristics) {
// 根据特征值的属性(properties)区分其功能
if (char.properties.write) {
writeCharacteristicId = char.uuid;
console.log('找到写特征值:', writeCharacteristicId);
}
if (char.properties.notify || char.properties.indicate) {
notifyCharacteristicId = char.uuid;
console.log('找到通知特征值:', notifyCharacteristicId);
// 关键一步:启用通知
enableNotifications(deviceId, serviceId, notifyCharacteristicId);
}
}
},
fail: (err) => {
console.error('获取特征值失败', err);
}
});
}
第7步:数据通信
a. 启用特征值通知(接收数据准备)
要接收设备主动推送的数据,必须先订阅该特征值的通知。
ts
function enableNotifications(deviceId, serviceId, characteristicId) {
wx.notifyBLECharacteristicValueChange({
state: true, // true为启用
deviceId: deviceId,
serviceId: serviceId,
characteristicId: characteristicId,
success: (res) => {
console.log('启用通知成功', res);
// 监听数据回调
onCharacteristicValueChange();
},
fail: (err) => {
console.error('启用通知失败', err);
}
});
}
b. 监听特征值变化(接收数据)
启用通知后,通过 wx.onBLECharacteristicValueChange
即可接收到设备上报的数据。
ts
function onCharacteristicValueChange() {
wx.onBLECharacteristicValueChange((res) => {
// res.value 是一个 ArrayBuffer,需要根据协议解析
const buffer = res.value;
const dataView = new DataView(buffer);
// 示例:读取第一个字节为无符号8位整数
// const value = dataView.getUint8(0);
console.log('收到设备数据:', buffer);
});
}
c. 向设备写入数据
使用 wx.writeBLECharacteristicValue
发送指令或数据。注意,发送的数据必须是 ArrayBuffer
格式。
ts
function writeData(deviceId, serviceId, characteristicId, commandHex) {
// 将16进制字符串命令转换为ArrayBuffer
const buffer = new ArrayBuffer(commandHex.length / 2);
const dataView = new DataView(buffer);
for (let i = 0; i < commandHex.length / 2; i++) {
dataView.setUint8(i, parseInt(commandHex.substring(i * 2, i * 2 + 2), 16));
}
wx.writeBLECharacteristicValue({
deviceId: deviceId,
serviceId: serviceId,
characteristicId: characteristicId,
value: buffer,
success: (res) => {
console.log('写入数据成功', res);
},
fail: (err) => {
console.error('写入数据失败', err);
}
});
}
第8步:断开连接与释放资源
当不再需要与设备通信时(例如退出页面),应主动断开连接并关闭蓝牙适配器,以释放系统资源。
ts
function disconnectAndClose(deviceId) {
wx.closeBLEConnection({
deviceId: deviceId,
success: (res) => {
console.log('断开连接成功', res);
}
});
wx.closeBluetoothAdapter({
success: (res) => {
console.log('关闭蓝牙模块成功', res);
}
});
}
Tips
- 及时停止搜索 :
startBluetoothDevicesDiscovery
是一个高耗能操作,在发起连接前或页面隐藏时,务必调用stopBluetoothDevicesDiscovery
停止搜索。 - 资源释放 :在页面
onUnload
或onHide
生命周期中,调用closeBLEConnection
和closeBluetoothAdapter
来断开连接和释放资源,避免内存泄漏和不必要的电量消耗。 - 数据格式 :所有写入和收到的数据都是
ArrayBuffer
格式,需要根据硬件设备的协议进行精确的序列化和反序列化。