BLE设备重连后状态不同步问题求助

小程序开发相关产品技术讨论,包括面板、智能小程序、React Native、Ray跨端框架、Panel SDK、微信小程序、小程序开发工具(IDE)及其他开发技术相关等话题


Post Reply
17825290950
Posts: 8

BLE设备重连后状态不同步问题求助

问题描述

我在开发一个颈部按摩器的小程序面板时,遇到了BLE重连后设备状态不同步的问题。

问题场景

  1. 用户在面板点击开关,发送 switch: false 指令关闭设备
  2. 设备收到指令后关机并断开蓝牙连接
  3. 用户在设备端物理按键启动设备(约0.8-1秒后)
  4. 蓝牙自动重连成功
  5. 问题出现:设备上报的首包DP数据显示 switch: false(旧状态),但设备实际已开启
  6. 面板根据 switch: false 显示"Disconnected"并置灰所有控件
  7. 必须手动在面板上重新切换开关才能恢复正常

问题特征

  • 这是一个偶发性问题,不是每次都出现
  • 设备实际已开启(指示灯正常),但面板显示关闭状态
  • 设备在重连后只上报一次状态,不会主动上报最新的 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 拉取最新状态。但担心频繁轮询会影响性能和用户体验。

问题分析

我认为问题的根本原因是:

  1. 设备在蓝牙断开期间状态发生变化(关机 → 开机)
  2. 重连后设备只上报了一次状态(可能是缓存的旧状态)
  3. 设备固件没有检测到状态已变化,不会再次主动上报

求助问题

  1. 面板层面
    • 是否有更好的方式主动获取设备最新状态?
    • getDeviceStatusgetDeviceInfo 的区别是什么?哪个更适合这个场景?
    • 定时轮询的最佳实践是什么?频率和次数如何设置?

补充信息

  • 设备会等到蓝牙连接稳定后才上报状态
  • 重连后没有收到新的 switch: true 上报
  • 等待5-10秒页面不会自动恢复
  • 必须手动重新切换开关才能恢复正常
Post Reply