极地沸点 發表於 2025-5-12 15:22:00

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

<h2 id="-uniapp-实现-app-连接低功耗蓝牙ble通讯">📱 UniApp 实现 App 连接低功耗蓝牙(BLE)通讯</h2>
<p>手头上有一个 uniapp 实现低功耗蓝牙通讯设备的项目,本来 Android 版本没问题已经上线,到了发布测试 iOS 出问题了,连接上了设备但是通讯失败,排查了下才发现是协议通讯中有包含六位 ID,也就是设备的 MAC 地址,因为设备主要标识符通常是设备 MAC,Android 能直接获取,但是喵的 iOS 不给,所以最终只能找硬件工程师进行协商找解决方案。</p>
<hr>
<h2 id="一ble-通讯协议说明这是本项目协议消息体构成">一、BLE 通讯协议说明(这是本项目协议消息体构成)</h2>
<h3 id="客户端发送消息体格式">客户端发送消息体格式:</h3>
<pre><code>1Byte(功能ID) + 6Byte(设备ID) + 1Byte(版本号) + nByte(功能内容)
</code></pre>
<h3 id="服务端回应消息体格式">服务端回应消息体格式:</h3>
<pre><code>1Byte(功能ID) + nByte(功能内容)
</code></pre>
<hr>
<h2 id="-二android-与-ios-的差异与解决方案">🔧 二、Android 与 iOS 的差异与解决方案</h2>
<h3 id="-android">🤖 Android</h3>
<ul>
<li><code>deviceId</code> 返回即为设备 <strong>Mac 地址</strong>,可直接进行数据通讯。</li>
</ul>
<h3 id="-ios">🍎 iOS</h3>
<ul>
<li><code>deviceId</code> 返回的是一个 <strong>UUID</strong>,并非真实 Mac 地址。</li>
</ul>
<h4 id="-uuid-是如何生成的">🔍 UUID 是如何生成的?</h4>
<ul>
<li>iOS 使用 <code>CBPeripheral.identifier</code> 生成一个临时 <strong>UUID</strong>。</li>
<li>此 UUID 是 iOS 系统基于 BLE 发现过程为每个设备 <strong>随机生成的唯一标识符</strong>。</li>
<li>与设备的真实物理地址无关,<strong>每次蓝牙重连或重新发现设备时可能会改变</strong>。</li>
<li>Apple 这么设计是为 <strong>保护用户隐私</strong>,避免开发者追踪设备或用户。</li>
</ul>
<h3 id="解决方案">解决方案:</h3>
<h4 id="方法一连接成功后监听设备主动返回-mac-地址">方法一:连接成功后监听设备主动返回 Mac 地址</h4>
<ul>
<li>设备会在连接 <strong>1 秒后主动发送 Mac 地址</strong>。</li>
<li>若无回应,设备会每 <strong>2 秒重发一次</strong>,直到收到。</li>
</ul>
<pre><code class="language-js">uni.onBLECharacteristicValueChange((res) =&gt; {
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');// 应答设备防止一直上报
}
});
</code></pre>
<h4 id="方法二通过广播数据-advertisdata-提取-mac-地址">方法二:通过广播数据 (advertisData) 提取 Mac 地址</h4>
<ul>
<li>每次扫描设备时,广播包中可能包含 Mac 地址字段。</li>
<li>若存在隐私顾虑,可对其 <strong>加密处理</strong>,连接设备前进行 <strong>解密并缓存</strong>。</li>
</ul>
<pre><code class="language-js">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) =&gt; {
      const advertisData = device.advertisData;
      const raw = ab2hex(advertisData);
      const encryptedMac = raw.slice(10, 42); // 示例中为16字节加密
      const mac = decryptMac(encryptedMac);
      console.log("解密出的Mac地址:", mac);
    });
},
});
</code></pre>
<hr>
<h2 id="三通讯流程简单示意">三、通讯流程简单示意</h2>
<pre><code class="language-js">async function connectBLE(device) {
await uni.createBLEConnection({ deviceId: device.deviceId });

// 监听特征值变化
uni.onBLECharacteristicValueChange((res) =&gt; 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: serviceUUID,
    characteristicId: writeUUID,
    value: str2ab(payload),
});
}
</code></pre>
<hr>
<h2 id="️-四安全建议">🛡️ 四、安全建议</h2>
<ul>
<li><strong>广播中的 Mac 地址应加密</strong>,防止泄露。</li>
<li><strong>通讯内容建议使用 AES 加密</strong>,提升安全等级。</li>
<li>使用 <strong>CRC16 或 hash</strong> 校验数据完整性。</li>
</ul>
<hr>
<h2 id="-五总结">📚 五、总结</h2>
<table>
<thead>
<tr>
<th>项目</th>
<th>Android</th>
<th>iOS</th>
</tr>
</thead>
<tbody>
<tr>
<td>deviceId</td>
<td>返回真实 Mac 地址 ✅</td>
<td>返回 UUID,需特殊处理 ⚠️</td>
</tr>
<tr>
<td>获取 Mac</td>
<td>可直接使用</td>
<td>连接后监听 / 广播包解析</td>
</tr>
<tr>
<td>广播数据</td>
<td>可用,可加密携带 Mac 地址</td>
<td>推荐使用,用于连接前标识设备</td>
</tr>
<tr>
<td>加密建议</td>
<td>AES 对称加密 / CRC16 校验</td>
<td>同 Android,建议一致处理方式</td>
</tr>
</tbody>
</table><br><br>
来源:https://www.cnblogs.com/zxlh1529/p/18872581
頁: [1]
查看完整版本: 解决uniapp实现ios系统中低功耗蓝牙通讯失败问题