uniapp 获取蓝牙广播包数据避坑:3 个常见错误一次讲清

编程狮(w3cschool.cn) 2026-11-12 07:03:40 浏览数 (52)
反馈

在 uniapp 里读取蓝牙广播包数据,核心是在 uni.startBluetoothDevicesDiscovery 发现设备之后,从 onBluetoothDeviceFound 回调的 advertisData 字段里解析出原始字节。新手最常踩的坑有三类:把 deviceId 误当成广播内容、用错 base64 解码方式、以及忽略 iOS 与 Android 在广播长度与字段顺序上的差异。本文用一段最小可运行示例,把这三处错误和对应的正确写法一次讲清,让你拿到的是真实广播负载,而不是一串看起来像数据其实只是设备地址的字符串。下面先把最小场景与边界讲清楚,再展开三个典型错误与校验方法。

uniapp 获取蓝牙广播包数据避坑

先看结论

读取蓝牙广播包数据只需要三步:先打开适配器,再开始发现设备,最后在发现回调里对 advertisData 做 base64 解码。最容易出错的地方不在 API 调用,而在解码环节——advertisData 是 ArrayBuffer 经过 base64 编码后的字符串,必须先用 uni.base64ToArrayBuffer 还原,再按你的广播协议逐字节读取;直接把 deviceId 或 advertisData 原文打印出来,只会得到设备标识,不是业务数据。下面先给出最小场景,再展开正确写法与三个典型错误。边界情况是:H5 与小程序端蓝牙 API 不完整,本文所有示例以 App 端为准。

一、最小场景与预期行为

假设你有一台蓝牙温湿度传感器,它每隔一秒广播一次,广播包里前两个字节是温度(放大 100 倍的整数),第三个字节是湿度。你的目标是在 uniapp 页面里实时显示这两个数值。

最小前置条件:

  • 真机运行(iOS 与 Android 的蓝牙行为差异很大,H5 与小程序端 API 也不完全一致,本文以 App 端为准)。
  • 已申请蓝牙权限(Android 需要 android.permission.BLUETOOTH 与定位权限,因为旧版 Android 把蓝牙扫描归在定位里)。
  • 设备确实在广播,并且你能在系统蓝牙设置里看到它。

预期输出是一行日志:温度=23.45 湿度=60,而不是 deviceId=XX:XX:XX... 或一段 base64 乱码。配置好权限后,先运行一次发现设备,确认能拿到 deviceId,再开始做解码,避免把权限问题误判成解析问题。

二、先用最稳的写法解析广播包

下面这一段是跑得通的核心逻辑,建议先照抄跑通,再替换成你自己的协议。

// pages/ble/ble.js
export default {
  data() {
    return { temperature: '', humidity: '' }
  },
  onLoad() {
    this.startScan()
  },
  methods: {
    startScan() {
      // 1. 初始化蓝牙适配器
      uni.openBluetoothAdapter({
        success: () => {
          // 2. 开始发现设备
          uni.startBluetoothDevicesDiscovery({
            success: () => {
              // 3. 监听发现事件
              uni.onBluetoothDeviceFound(this.handleFound)
            }
          })
        },
        fail: (e) => console.error('打开适配器失败', e)
      })
    },
    handleFound(res) {
      const list = res.devices || []
      for (const d of list) {
        // advertisData 是 base64 字符串,需要还原成 ArrayBuffer
        if (!d.advertisData) continue
        const buf = uni.base64ToArrayBuffer(d.advertisData)
        const view = new Uint8Array(buf)
        if (view.length < 3) continue
        const temp = (view[0] * 256 + view[1]) / 100
        const hum = view[2]
        console.log('温度=' + temp + ' 湿度=' + hum)
        this.temperature = temp
        this.humidity = hum
      }
    }
  }
}

这段代码的关键是 uni.base64ToArrayBuffer(d.advertisData):它把回调里那段 base64 还原为字节数组,之后你才能按协议下标取值。如果你用可视化低代码工具做过类似功能,原理也是同一套——例如在 App Inventor 教程 里同样要先拿到 AdvertisementData 再做字节解析,只是图形化积木把 base64 步骤藏起来了,反而更容易让人以为"拿到对象就完事"。

uniapp 蓝牙广播包解析的正确日志与错误日志对比

三、三个典型错误

下面把三处最可能出错的环节拆开讲,每处都给出失败表现、根因和修复方法。

错误一:把 deviceId 当成广播内容

第一个错误最常见。onBluetoothDeviceFound 的回调里同时有 deviceId、name、advertisData、RSSI 等字段,很多人图省事直接打印 deviceId,然后疑惑"为什么我的温湿度数据全是 MAC 地址"。

deviceId 是系统给这台设备的标识,iOS 上还是随机化的隐私地址,每次扫描都可能不一样,它和你的业务广播数据没有任何关系。真正的数据在 advertisData 里,而且只有在你调用发现时携带了正确参数、设备也确实广播了对应字段时才会存在。

修复方法:始终从 advertisData 解析,并对空值做保护(if (!d.advertisData) continue)。如果你的传感器把数据放在「广播数据」而不是「扫描响应」里,还要注意 uni.startBluetoothDevicesDiscovery 默认就会同时拿到这两部分,不必额外请求扫描响应。

错误二:用错 base64 解码方式

第二个错误是解码环节。advertisData 是 base64 编码的字符串,但 uniapp 的 uni.base64ToArrayBuffer 才是官方提供的还原方法。有人会自己写 atob 再转字节,或者在前端用 Buffer(uni-app 的 App 端没有 Node 的 Buffer),结果要么拿到错误的字节,要么直接抛异常。

更隐蔽的一种情况是:你以为 advertisData 已经是 ArrayBuffer,于是对它直接 new Uint8Array(advertisData),但在 App 端它其实是字符串,这样取出来的字节全是 NaN。务必先用 uni.base64ToArrayBuffer 过一道。

校验方法很简单:打印 view.length 和 view[0] 的十进制值,看看是否落在你协议规定的范围内(比如温度整数不会是负数、湿度不会超过 100)。如果数值明显离谱,大概率是解码环节错了,而不是设备没发对。运行后对比预期输出,能快速定位是解码错还是协议错。

错误三:忽略 iOS 与 Android 的广播差异

第三个错误属于"在 Android 上好好的,iOS 上一片空白"。两家系统在蓝牙广播上有两处关键差异:

第一,iOS 对单包广播长度限制更严,超长的自定义字段会被截断或干脆不广播,所以协议设计时要控制总长度。第二,iOS 出于隐私,会在回调里把部分字段归一化,某些厂商自定义的 serviceData 在 iOS 上拿不到,需要改用标准的 manufacturerData 通道。Android 则相对宽松,几乎原样回传。

处理建议:在解析前同时检查 advertisData 是否为空,并且为 iOS 单独准备一套更短的广播协议;在页面里打印 plus.os.name 或 uni.getSystemInfoSync().platform 来判断平台,针对性地做兼容分支。这是典型的平台边界问题,不能只在 Android 上验证就上线。

如果你把广播数据画成可视化结构图来核对字段顺序,会比纯看十六进制直观得多;相关接口字段的图形化对照,可以参考 SVG 速查手册 里的数据格式可视化思路。

iOS 与 Android 蓝牙广播差异怎么选?

常见问题

Q:为什么 onBluetoothDeviceFound 一直不触发?

先确认 uni.openBluetoothAdapter 是否成功,再确认手机蓝牙和定位(Android)是否已开启。另外真机才能拿到真实广播,模拟器或 H5 端这个回调基本不会触发。

Q:advertisData 打印出来是空怎么办?

说明该设备在广播里没有携带你的自定义数据,或数据放在了扫描响应而非广播包。尝试在 startBluetoothDevicesDiscovery 里把 interval 调小、多扫几轮,同时核对传感器端的广播配置。

Q:iOS 上能发现设备但读不到温度?

多半是协议超长被 iOS 截断,或你依赖的字段在 iOS 不可见。把广播协议缩短到 20 字节以内,并改用 manufacturerData 承载业务数据再试。

总结

读取 uniapp 蓝牙广播包数据,本质是「开适配器 → 发现设备 → 对 advertisData 做 base64 还原并按下标取值」三步。三个常见错误分别是:误读 deviceId、解码用错方法、忽视 iOS 与 Android 的平台差异。只要坚持从 advertisData 解析、统一用 uni.base64ToArrayBuffer、并为 iOS 单独做短协议兼容,就能稳定拿到真实业务数据。运行前确认权限与平台,失败按"回调是否触发→字段是否为空→解码是否正确"的顺序排查,基本都能定位。

延伸学习

0 人点赞