Skip to content

uni-app蓝牙踩坑小记

约 1477 字大约 5 分钟

项目小记uni-app蓝牙

2025-01-31

之前接了个与硬件建立蓝牙通讯的项目,学到了一些uni-app蓝牙操作的知识,也踩了一些坑,特此记录一下防止忘记。

在蓝牙模块调用时,采用的是Promise链式调用的形式,因此下文所提供的代码片段均为Promise形式。

超绝promise链

文章参考:uni-app官网蓝牙低功耗蓝牙

Step1 初始化蓝牙适配器

使用uni.openBluetoothAdapter()函数初始化蓝牙适配器,uni.getBluetoothAdapterState()函数获取蓝牙适配器状态。

// Step1 初始化蓝牙
const initBlue = () => {
	return new Promise((resolve, reject) => {
		uni.openBluetoothAdapter({
			success(res) {
				console.log('初始化蓝牙成功')
				uni.getBluetoothAdapterState({
					success(r) {
						console.log('蓝牙状态', r.available);
						if (r.available) {
							resolve();
						} else {
							uni.showToast({
								title: "请开启蓝牙",
								icon: 'error',
								duration: 2000,
							});
							reject();
						}
					},
					fail(err) {
						uni.showToast({
							title: "请开启蓝牙",
							icon: 'error',
							duration: 2000,
						});
						reject();
					}
				});
			},
			fail(err) {
				console.log('初始化蓝牙失败')
				console.error(err)
				uni.showToast({
					title: "请开启蓝牙",
					icon: 'error',
					duration: 2000,
				});
				reject();
			}
		});
	});
};

Step2 开始搜索附近的蓝牙设备

使用uni.startBluetoothDevicesDiscovery()函数启动搜索附近的蓝牙设备,uni.onBluetoothDeviceFound()函数定义搜索到新设备时的回调函数。

// Step2 开始搜寻附近设备
const discovery = () => {
	return new Promise((resolve, reject) => {
		uni.startBluetoothDevicesDiscovery({
			success(res) {
				uni.showLoading({
					title: "正在搜索设备",
					icon: "none",
				});
				// 开启监听回调
				uni.onBluetoothDeviceFound(function(devices) {
					let obj = devices.devices[0];
					console.log(obj.name)
					if (obj.name === conf.deviceName) {
						// 找到目标设备
						// 设置设备ID到同步缓存中
						uni.setStorageSync('deviceId', obj.deviceId);
						uni.hideLoading();
						resolve();
					}
				});
				console.log('搜索蓝牙外围设备完成', res);
			},
			fail(err) {
				console.log(err);
			}
		});
	});
};

Step3 连接蓝牙设备

使用uni.createBLEConnection(deviceId)函数连接到目标蓝牙设备。

// Step3 连接设备
const connect = () => {
	return new Promise((resolve, reject) => {
		uni.createBLEConnection({
			deviceId: uni.getStorageSync('deviceId'), // 设备id
			success(res) {
				console.log('连接成功');
				console.log(res);
				uni.showToast({
					title: '连接成功',
					icon: 'success'
				});
				// Step4 蓝牙连接成功后关闭蓝牙搜索
				stopDiscovery();
				resolve();
			},
			fail(err) {
				uni.showToast({
					title: '连接失败',
					icon: 'error'
				});
				console.log("蓝牙连接失败");
				console.log(err);
				reject();
			}
		});
	});
};

重要

由于Setp2中搜索蓝牙设备的功能比较耗费系统资源,因此在搜索并连接到设备后务必停止搜索

Step4 停止搜索

使用uni.stopBluetoothDevicesDiscovery()函数停止搜索附近的蓝牙设备。

// Step4 停止搜索
const stopDiscovery = () => {
	uni.stopBluetoothDevicesDiscovery({
		success(res) {
			console.log('停止成功');
			console.log(res);
		},
		fail(err) {
			console.log('停止失败');
			console.error(err);
		}
	});
};

Step5 获取蓝牙设备所有服务

使用uni.getBLEDeviceServices(deviceId)函数获取蓝牙设备所有服务。

// Step5 获取蓝牙设备所有服务
const getServices = () => {
	return new Promise((resolve, reject) => {
		uni.getBLEDeviceServices({
			deviceId: uni.getStorageSync('deviceId'),
			success(res) {
				console.log(res)
				resolve();
			},
			fail(err) {
				console.error(err)
				reject();
			}
		})
	});
};

Step6 获取蓝牙设备某个服务中所有特征值

使用uni.getBLEDeviceCharacteristics(deviceId, serviceId)函数获取蓝牙设备某个服务中所有特征值。

// Step6 获取蓝牙设备某个服务中所有特征值
const getCharacteristics = () => {
	return new Promise((resolve, reject) => {
		uni.getBLEDeviceCharacteristics({
			deviceId: uni.getStorageSync('deviceId'),
			serviceId: conf.serviceId,
			success(res) {
				console.log(res)
				resolve();
			},
			fail(err) {
				console.error(err)
				reject();
			}
		})
	})
};

Step7 开启消息监听

使用uni.notifyBLECharacteristicValueChange(deviceId, serviceId, characteristicId, ...)函数开启消息监听。

// Step7 开启消息监听
const notify = (handleFunc) => { // 这里传入一个回调函数,用于处理接收到的数据。
	uni.notifyBLECharacteristicValueChange({
		deviceId: uni.getStorageSync('deviceId'), // 设备id
		serviceId: conf.serviceId, // 监听指定的服务
		characteristicId: conf.characteristicId, // 监听对应的特征值
		state: true,
		success(res) {
			console.log(res)
			listenValueChange(handleFunc); // 开启监听消息变化,并传入回调函数。
		},
		fail(err) {
			console.error(err)
			uni.showToast({
				title: '监听失败',
				icon: 'error'
			})
		}
	})
};

Step8 监听消息变化

使用uni.onBLECharacteristicValueChange()函数监听消息变化并根据需求做相应处理。

// Step8 监听消息变化
const listenValueChange = (handleFunc) => { // 这里传入一个回调函数,用于处理接收到的数据。
	uni.onBLECharacteristicValueChange(res => {
		let resArray = ab2array(res.value); // 拿到数字数组
        // Something to do...
	})
};
// ArrayBuffer 转数组
const ab2array = (buffer) => {
    return Array.from(new Uint8Array(buffer));
};

Step9 发送数据

使用uni.writeBLECharacteristicValue(deviceId, serviceId, characteristicId, value)函数向蓝牙设备发送数据。

// Step9 发送数据
const sendCommand = (data) => {
	uni.writeBLECharacteristicValue({
		deviceId: uni.getStorageSync('deviceId'),
		serviceId: conf.serviceId,
		characteristicId: conf.characteristicId,
        // 注意:value 是ArrayBuffer类型,需要进行转换。
		value: array2ab(data),
		success(res) {
			console.log('写入成功' + res.errMsg);
		},
		fail(err) {
			console.error(err)
			uni.showToast({
				title: '指令发送失败',
				icon: 'error'
			});
		}
	})
};
// 数组转Arraybuffer
const array2ab = (numbers) => {
    // 创建一个ArrayBuffer,其大小等于数字数组的长度
    const buffer = new ArrayBuffer(numbers.length);
    // 创建一个Uint8Array视图来操作buffer
    const uint8View = new Uint8Array(buffer);
    // 将数字填充到Uint8Array中
    for (let i = 0; i < numbers.length; i++) {
        uint8View[i] = numbers[i];
    }
    // 返回ArrayBuffer
    return buffer;
};

Step10 断开蓝牙连接

使用uni.closeBLEConnection(deviceId)函数断开蓝牙连接。

// Step10 断开蓝牙连接
const stopConnection = () => {
	return new Promise((resolve, reject) => {
		uni.closeBLEConnection({
			deviceId: uni.getStorageSync('deviceId'), // 设备id
			success(r) {
				resolve();
			},
			fail(r) {
				console.log(r);
				reject();
			}
		});
	});
};

踩过的坑

为什么收到的数据是乱码(不符合预期)?

  1. 协议文档写错了:可能是文档看错了或者硬件那边把协议改了却没有更新文档。
  2. 数据类型不对:可能是收到的数据没有进行转换,直接在ArrayBuffer上面进行了数据解析操作。
  3. 硬件波特率设置有问题:可能是硬件那边的波特率设置有问题,导致数据收发不正常。经过测试踩坑,波特率设置成9600时, 数据收发正常。

为什么采用Promise链的形式?

  1. 逻辑清晰:Promise链的形式可以让代码看起来更加清晰,易于理解。
  2. 避免上下文影响 :一些函数的调用会影响到下文,若只是按顺序结构编写逻辑则有可能发生获取服务 先于蓝牙连接的情况,导致程序报错。