解决uniapp实现ios系统中低功耗蓝牙通讯失败问题

解决uniapp实现ios系统中低功耗蓝牙通讯失败问题

📱 UniApp 实现 App 连接低功耗蓝牙(BLE)通讯

手头上有一个 uniapp 实现低功耗蓝牙通讯设备的项目,本来 Android 版本没问题已经上线,到了发布测试 iOS 出问题了,连接上了设备但是通讯失败,排查了下才发现是协议通讯中有包含六位 ID,也就是设备的 MAC 地址,因为设备主要标识符通常是设备 MAC,Android 能直接获取,但是喵的 iOS 不给,所以最终只能找硬件工程师进行协商找解决方案。

一、BLE 通讯协议说明(这是本项目协议消息体构成)

客户端发送消息体格式:

1Byte(功能ID) + 6Byte(设备ID) + 1Byte(版本号) + nByte(功能内容)

服务端回应消息体格式:

1Byte(功能ID) + nByte(功能内容)

🔧 二、Android 与 iOS 的差异与解决方案

🤖 Android

deviceId 返回即为设备 Mac 地址,可直接进行数据通讯。

🍎 iOS

deviceId 返回的是一个 UUID,并非真实 Mac 地址。

🔍 UUID 是如何生成的?

iOS 使用 CBPeripheral.identifier 生成一个临时 UUID。

此 UUID 是 iOS 系统基于 BLE 发现过程为每个设备 随机生成的唯一标识符。

与设备的真实物理地址无关,每次蓝牙重连或重新发现设备时可能会改变。

Apple 这么设计是为 保护用户隐私,避免开发者追踪设备或用户。

解决方案:

方法一:连接成功后监听设备主动返回 Mac 地址

设备会在连接 1 秒后主动发送 Mac 地址。

若无回应,设备会每 2 秒重发一次,直到收到。

uni.onBLECharacteristicValueChange((res) => {

  const value = ab2hex(res.value);

  if (value.startsWith(“01”)) {

    const mac = value.slice(2, 14); // 截取6字节mac

    uni.setStorageSync('deviceMac', mac) // 存储mac用于后续通讯,有用 vuex 或者 pinia 等其他方法也可以

    await this.send('11'); // 应答设备防止一直上报

  }

});

方法二:通过广播数据 (advertisData) 提取 Mac 地址

每次扫描设备时,广播包中可能包含 Mac 地址字段。

若存在隐私顾虑,可对其 加密处理,连接设备前进行 解密并缓存。

import CryptoJS from “crypto-js”;

function decryptMac(cipherHex) {

  const key = CryptoJS.enc.Hex.parse(“00112233445566778899aabbccddeeff”); // 16字节密钥

  const iv = CryptoJS.enc.Hex.parse(“00000000000000000000000000000000”); // 默认全0 IV

  const encrypted = CryptoJS.enc.Hex.parse(cipherHex);

  const encryptedBase64 = CryptoJS.enc.Base64.stringify(encrypted);

  const decrypted = CryptoJS.AES.decrypt(encryptedBase64, key, {

    iv,

    mode: CryptoJS.mode.CBC,

    padding: CryptoJS.pad.Pkcs7,

  });

  return decrypted.toString(CryptoJS.enc.Hex);

}

uni.startBluetoothDevicesDiscovery({

  success() {

    uni.onBluetoothDeviceFound((device) => {

      const advertisData = device.advertisData;

      const raw = ab2hex(advertisData);

      const encryptedMac = raw.slice(10, 42); // 示例中为16字节加密

      const mac = decryptMac(encryptedMac);

      console.log(“解密出的Mac地址:”, mac);

    });

  },

});

三、通讯流程简单示意

async function connectBLE(device) {

  await uni.createBLEConnection({ deviceId: device.deviceId });

  // 监听特征值变化

  uni.onBLECharacteristicValueChange((res) => handleResponse(res));

  // 启用 notify 监听

  await uni.notifyBLECharacteristicValueChange({

    deviceId: device.deviceId,

    serviceId: serviceUUID,

    characteristicId: notifyUUID,

    state: true,

  });

  // 发送数据

  const platform = uni.getSystemInfoSync().platform;

  const macAddress = uni.getStorageSync(“deviceMac”);

  const payload = buildPayload(

    “01”,

    platform == “ios” ? macAddress : device.deviceId,

    “01”,

    content

  );

  await uni.writeBLECharacteristicValue({

    deviceId: device.deviceId,

    serviceId: servi

ceUUID,

    characteristicId: writeUUID,

    value: str2ab(payload),

  });

}

🛡️ 四、安全建议

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容