目录
在物联网(IoT)设备开发中,蓝牙功能的测试至关重要。为了帮助开发者更高效、便捷地进行蓝牙设备测试,我们可以自己开发了一款专用的蓝牙微信小程序测试工具。用作开发测试或者量产测试。
一、蓝牙微信小程序测试工具应用场景
适用场景:
- 物联网设备开发者:用于测试蓝牙设备的各项功能和性能。
- 嵌入式系统开发者:用于调试蓝牙模块和协议。
- 移动应用开发者:用于验证蓝牙功能的集成和交互。
- 对蓝牙设备有兴趣的爱好者。
二、蓝牙微信小程序测试工具开发实现思路
1.检查蓝牙适配器状态,请求授权。
2.初始化蓝牙适配器,开始扫描设备。
3.在扫描过程中收集设备信息,包括RSSI。
4.展示设备列表,点击后连接设备。
5.连接后获取服务,找到特征值,订阅通知或读取数据。
6.定时更新信号强度,或者通过监听设备更新来获取最新RSSI。
7.处理断开连接和错误情况。
三、需要处理的问题
-
设备重复出现在列表中,需要根据deviceId去重。
-
扫描过程中如何处理设备更新,可能需要使用数组更新并重新渲染。
-
连接设备时的异步操作,需要正确处理回调或Promise。
-
信号强度的更新频率,避免过于频繁导致性能问题。
-
用户界面友好,显示连接状态和日志信息。
四、微信小程序蓝牙API
微信小程序提供了丰富的蓝牙 API,用于搜索、连接和操作蓝牙设备。详细的 API 文档是开发的基础。
重点关注以下 API:
-
wx.openBluetoothAdapter
:初始化蓝牙模块。 -
wx.startBluetoothDevicesDiscovery
:开始搜索附近的蓝牙设备。 -
wx.stopBluetoothDevicesDiscovery
:停止搜索。 -
wx.createBLEConnection
:连接到指定的蓝牙设备。 -
wx.closeBLEConnection
:断开连接。 -
wx.getBLEDeviceServices
:获取蓝牙设备的服务。 -
wx.getBLEDeviceCharacteristics
:获取蓝牙服务的特征值。 -
wx.readBLECharacteristicValue
:读取特征值。 -
wx.writeBLECharacteristicValue
:写入特征值。 -
wx.notifyBLECharacteristicValueChange
:启用特征值变化通知。
五、代码示例
以下是一个基于微信小程序的蓝牙测试工具示例代码,包含蓝牙设备扫描、信号强度检测和基础连接功能:
// app.json 配置
{
"pages": ["index/index"],
"window": {
"navigationBarTitleText": "蓝牙信号测试"
},
"permission": {
"scope.userLocation": {
"desc": "需要获取位置权限以使用蓝牙功能"
}
}
}
// index/index.js
Page({
data: {
devices: [], // 发现的设备列表
connected: false, // 连接状态
logs: [], // 操作日志
scanning: false // 扫描状态
},
onLoad() {
this.initBluetooth();
},
// 初始化蓝牙适配器
initBluetooth() {
const that = this;
wx.openBluetoothAdapter({
success() {
that.log('蓝牙适配器初始化成功');
wx.getBluetoothAdapterState({
success(res) {
that.log(`当前蓝牙状态:${res.available ? '可用' : '不可用'}`);
}
});
},
fail(err) {
that.log('蓝牙初始化失败: ' + JSON.stringify(err));
}
});
},
// 开始扫描设备
startScan() {
const that = this;
if (this.data.scanning) return;
this.setData({ scanning: true });
this.log('开始扫描蓝牙设备...');
wx.startBluetoothDevicesDiscovery({
allowDuplicatesKey: true,
success() {
that.log('扫描启动成功');
wx.onBluetoothDeviceFound((res) => {
res.devices.forEach(device => {
if (!device.name && !device.localName) return;
that.updateDeviceList(device);
});
});
}
});
},
// 更新设备列表
updateDeviceList(device) {
const devices = this.data.devices;
const index = devices.findIndex(d => d.deviceId === device.deviceId);
const info = {
name: device.name || device.localName,
deviceId: device.deviceId,
rssi: device.RSSI,
advertisData: device.advertisData
};
if (index === -1) {
devices.push(info);
} else {
devices[index] = info;
}
this.setData({ devices });
},
// 停止扫描
stopScan() {
const that = this;
wx.stopBluetoothDevicesDiscovery({
success() {
that.setData({ scanning: false });
that.log('已停止扫描');
}
});
},
// 连接设备
connectDevice(e) {
const deviceId = e.currentTarget.dataset.deviceId;
const device = this.data.devices.find(d => d.deviceId === deviceId);
const that = this;
this.log(`正在连接 ${device.name}...`);
wx.createBLEConnection({
deviceId,
success() {
that.setData({ connected: true });
that.log('连接成功');
that.getBLEDeviceServices(deviceId);
},
fail(err) {
that.log('连接失败: ' + JSON.stringify(err));
}
});
},
// 获取设备服务
getBLEDeviceServices(deviceId) {
const that = this;
wx.getBLEDeviceServices({
deviceId,
success(res) {
res.services.forEach(serviceId => {
if (serviceId.isPrimary) {
that.getBLEDeviceCharacteristics(deviceId, serviceId.uuid);
}
});
}
});
},
// 获取特征值
getBLEDeviceCharacteristics(deviceId, serviceId) {
wx.getBLEDeviceCharacteristics({
deviceId,
serviceId,
success(res) {
res.characteristics.forEach(characteristic => {
if (characteristic.properties.notify) {
// 启用特征值订阅
wx.notifyBLECharacteristicValueChange({
deviceId,
serviceId,
characteristicId: characteristic.uuid,
state: true
});
}
});
}
});
},
// 断开连接
disconnect() {
const that = this;
wx.closeBLEConnection({
deviceId: this.data.currentDeviceId,
success() {
that.setData({ connected: false });
that.log('已断开连接');
}
});
},
// 日志记录
log(msg) {
const logs = this.data.logs;
logs.unshift(msg);
this.setData({ logs: logs.slice(0, 50) }); // 最多保留50条
},
onUnload() {
this.stopScan();
if (this.data.connected) this.disconnect();
wx.closeBluetoothAdapter();
}
})
<!-- index/index.wxml -->
<view class="container">
<view class="controls">
<button
type="primary"
bindtap="startScan"
disabled="{{scanning}}"
>开始扫描</button>
<button
type="warn"
bindtap="stopScan"
disabled="{{!scanning}}"
>停止扫描</button>
<button
type="{{connected ? 'warn' : 'default'}}"
bindtap="disconnect"
disabled="{{!connected}}"
>{{connected ? '断开连接' : '未连接'}}</button>
</view>
<view class="device-list">
<view wx:for="{{devices}}"
wx:key="deviceId"
class="device-item"
bindtap="connectDevice"
data-device-id="{{item.deviceId}}">
<text class="device-name">{{item.name}}</text>
<view class="device-info">
<text>信号强度: {{item.rssi}} dBm</text>
<text>MAC: {{item.deviceId}}</text>
</view>
</view>
</view>
<view class="log-area">
<text class="log-title">操作日志:</text>
<scroll-view scroll-y style="height: 200px;">
<text wx:for="{{logs}}" class="log-item">{{item}}\n</text>
</scroll-view>
</view>
</view>
/* index/index.wxss */
.container {
padding: 20rpx;
}
.controls {
margin-bottom: 20rpx;
display: flex;
justify-content: space-between;
}
.device-item {
padding: 20rpx;
border-bottom: 1rpx solid #eee;
}
.device-name {
font-weight: bold;
display: block;
margin-bottom: 10rpx;
}
.device-info {
display: flex;
justify-content: space-between;
color: #666;
font-size: 24rpx;
}
.log-area {
margin-top: 40rpx;
border-top: 1rpx solid #eee;
padding-top: 20rpx;
}
.log-title {
font-weight: bold;
display: block;
margin-bottom: 10rpx;
}
.log-item {
font-size: 24rpx;
color: #666;
display: block;
line-height: 1.6;
}
六、主要功能说明
1.蓝牙信号检测:
-
通过RSSI值显示信号强度(单位dBm)
-
数值越接近0表示信号越强(通常-50dBm为优良信号,-90dBm以下为弱信号)
2.核心功能:
-
蓝牙设备扫描与展示
-
实时信号强度显示
-
设备连接/断开
-
服务与特征值发现
-
数据通信支持
3.使用注意事项:
-
需要用户授权蓝牙和位置权限
-
iOS可能需要位置权限才能扫描BLE设备
-
不同设备RSSI测量方式可能有差异
-
保持设备在有效通信范围内(通常10米内)
4.测试扩展方向:
-
增加信号强度变化折线图
-
添加数据收发测试功能
-
实现自动信号质量评估
-
增加设备过滤功能
-
支持信号强度历史记录
七、优化后代码
// app.js
App({
onLaunch() {
console.log('App Launching...');
}
});
// pages/index/index.js
Page({
data: {
devices: [], // 存储扫描到的蓝牙设备
connectedDeviceId: null, // 已连接的设备ID
serviceId: null, // 选定的服务ID
characteristicId: null, // 选定的特征值ID
receivedData: '', // 接收到的数据
deviceFilter: '' // 设备名称过滤关键字
},
onLoad() {
this.checkBluetoothPermission(); // 检查并请求蓝牙权限
},
// 检查并请求蓝牙权限
checkBluetoothPermission() {
wx.getSetting({
success: res => {
if (!res.authSetting['scope.bluetooth']) {
wx.authorize({
scope: 'scope.bluetooth',
success: this.openBluetooth,
fail: () => {
console.error('Bluetooth permission denied');
wx.showModal({
title: 'Permission Required',
content: 'This app requires Bluetooth access. Please enable it in settings.',
showCancel: false
});
}
});
} else {
this.openBluetooth();
}
}
});
},
// 初始化蓝牙适配器
openBluetooth() {
wx.openBluetoothAdapter({
success: this.startDiscovery,
fail: err => console.error('Bluetooth initialization failed', err)
});
},
// 开始扫描蓝牙设备
startDiscovery() {
wx.startBluetoothDevicesDiscovery({
allowDuplicatesKey: false,
success: this.getDevices,
fail: err => console.error('Discovery failed', err)
});
},
// 获取扫描到的蓝牙设备
getDevices() {
wx.onBluetoothDeviceFound(res => {
const newDevices = res.devices
.filter(device => device.RSSI > -70)
.filter(device => !this.data.deviceFilter || (device.name && device.name.includes(this.data.deviceFilter)))
.map(device => ({
name: device.name || 'Unknown',
deviceId: device.deviceId,
RSSI: device.RSSI
}));
this.setData({ devices: [...this.data.devices, ...newDevices] });
});
},
// 设置设备过滤关键字
setDeviceFilter(event) {
this.setData({ deviceFilter: event.detail.value });
},
// 连接选中的蓝牙设备
connectDevice(event) {
const deviceId = event.currentTarget.dataset.id;
wx.createBLEConnection({
deviceId,
success: () => {
console.log('Connected to', deviceId);
this.setData({ connectedDeviceId: deviceId });
this.getServices(deviceId);
},
fail: err => console.error('Connection failed', err)
});
},
// 获取设备的所有服务
getServices(deviceId) {
wx.getBLEDeviceServices({
deviceId,
success: res => {
if (res.services.length > 0) {
this.getCharacteristics(deviceId, res.services[0].uuid);
}
}
});
},
// 获取设备服务的所有特征值
getCharacteristics(deviceId, serviceId) {
wx.getBLEDeviceCharacteristics({
deviceId,
serviceId,
success: res => {
if (res.characteristics.length > 0) {
this.setData({
serviceId: serviceId,
characteristicId: res.characteristics[0].uuid
});
this.enableNotify(deviceId, serviceId, res.characteristics[0].uuid);
}
}
});
},
// 发送数据到蓝牙设备
sendData() {
const { connectedDeviceId, serviceId, characteristicId } = this.data;
if (!connectedDeviceId || !serviceId || !characteristicId) return;
const buffer = new ArrayBuffer(1);
new DataView(buffer).setUint8(0, 1);
wx.writeBLECharacteristicValue({
deviceId: connectedDeviceId,
serviceId: serviceId,
characteristicId: characteristicId,
value: buffer,
success: () => console.log('Data sent successfully'),
fail: err => console.error('Failed to send data', err)
});
},
// 开启 notify 监听特征值变化
enableNotify(deviceId, serviceId, characteristicId) {
wx.notifyBLECharacteristicValueChange({
deviceId,
serviceId,
characteristicId,
state: true,
success: () => console.log('Notify enabled for', characteristicId),
fail: err => console.error('Enable notify failed', err)
});
wx.onBLECharacteristicValueChange(res => {
const receivedData = new Uint8Array(res.value).join(',');
this.setData({ receivedData });
console.log('Received data:', receivedData);
});
}
});
八、开发与测试建议
- 真机测试:
- 蓝牙功能的测试必须在真机上进行,微信开发者工具的模拟器可能无法提供准确的结果。
- 在不同型号的手机上进行测试,确保兼容性。
- 调试技巧:
- 利用微信开发者工具的调试功能,查看蓝牙通信的日志和数据。
- 使用第三方蓝牙调试工具,辅助分析蓝牙通信协议。
- 用户体验优化:
- 提供清晰的加载提示和操作反馈,避免用户感到困惑。
- 优化蓝牙连接和数据传输的性能,减少延迟。
扩展阅读: