BLE设备重连后状态不同步问题求助
问题描述
我在开发一个颈部按摩器的小程序面板时,遇到了BLE重连后设备状态不同步的问题。
问题场景:
- 用户在面板点击开关,发送
switch: false指令关闭设备 - 设备收到指令后关机并断开蓝牙连接
- 用户在设备端物理按键启动设备(约0.8-1秒后)
- 蓝牙自动重连成功
- 问题出现:设备上报的首包DP数据显示
switch: false(旧状态),但设备实际已开启 - 面板根据
switch: false显示"Disconnected"并置灰所有控件 - 必须手动在面板上重新切换开关才能恢复正常
问题特征:
- 这是一个偶发性问题,不是每次都出现
- 设备实际已开启(指示灯正常),但面板显示关闭状态
- 设备在重连后只上报一次状态,不会主动上报最新的
switch: true - 等待5-10秒也不会自动恢复,必须手动操作
开发环境
- 框架:Tuya Ray 小程序
- 设备类型:BLE颈部按摩器
- 开发工具:TuyaMiniAppIDE
- SDK版本:@ray-js/ray, @ray-js/panel-sdk
相关代码
1. 蓝牙连接管理(useBluetoothStatus.ts)
Code: Select all
// 主动断开并重连
const disconnectAndReconnect = () => {
const deviceId = getCurrentDeviceId();
if (!deviceId) return;
console.log('[useBluetoothStatus] 主动断开并准备重连');
disconnectBLEDevice({ deviceId });
};2. 面板首页逻辑(home/index.tsx)
Code: Select all
// switch 从 true → false 时主动断开 BLE
const prevSwitchRef = useRef<boolean | undefined>(undefined);
const userTriggeredSwitchRef = useRef<boolean>(false);
useEffect(() => {
if (
prevSwitchRef.current === true &&
props.switch === false &&
userTriggeredSwitchRef.current
) {
console.log('[home] 用户触发 switch false,主动断开');
disconnectAndReconnect();
userTriggeredSwitchRef.current = false;
}
prevSwitchRef.current = props.switch;
}, [props.switch, disconnectAndReconnect]);
// 连接后主动拉取一次 DP
useEffect(() => {
if (!isConnected) {
setHasInitialDp(false);
return;
}
const timer = setTimeout(() => {
const { query } = getLaunchOptionsSync();
const deviceId = query?.deviceId;
if (!deviceId) return;
const pullStatus = (device as any).getDeviceStatus || (device as any).getDeviceInfo;
if (typeof pullStatus !== 'function') {
console.warn('[home] 未找到设备状态拉取接口');
return;
}
pullStatus({
deviceId,
success: () => {
console.log('[home] 拉取状态成功,等待设备上报 DP');
},
fail: (err: any) => {
console.warn('[home] 主动拉取设备状态失败:', err);
},
});
}, 700);
return () => clearTimeout(timer);
}, [isConnected]);
// 监听首包 DP
useEffect(() => {
if (!isConnected) {
return;
}
const hasDp =
typeof props.switch !== 'undefined' ||
typeof props.mode === 'string' ||
typeof props.countdown_left === 'number';
if (hasDp && !hasInitialDp) {
console.log('[home] 收到首包 DP', {
switch: props.switch,
mode: props.mode,
heat: props.heat,
countdown_left: props.countdown_left,
});
setHasInitialDp(true);
}
}, [isConnected, props.switch, props.mode, props.countdown_left, hasInitialDp]);已尝试的方案
方案1:指令缓存队列
在设备未就绪时缓存用户操作,连接成功后延迟执行。但这个方案只能解决用户在面板操作的场景,无法解决用户在设备端操作的情况。
方案2:定时轮询
收到首包 switch: false 后,定时调用 getDeviceStatus 拉取最新状态。但担心频繁轮询会影响性能和用户体验。
问题分析
我认为问题的根本原因是:
- 设备在蓝牙断开期间状态发生变化(关机 → 开机)
- 重连后设备只上报了一次状态(可能是缓存的旧状态)
- 设备固件没有检测到状态已变化,不会再次主动上报
求助问题
- 面板层面:
- 是否有更好的方式主动获取设备最新状态?
getDeviceStatus和getDeviceInfo的区别是什么?哪个更适合这个场景?- 定时轮询的最佳实践是什么?频率和次数如何设置?
补充信息
- 设备会等到蓝牙连接稳定后才上报状态
- 重连后没有收到新的
switch: true上报 - 等待5-10秒页面不会自动恢复
- 必须手动重新切换开关才能恢复正常