如何通过BLE广播超过31字节的字符串,解决广播启动报错问题
问题原因
你遇到的错误码1是标准BLE传统广播的固有限制导致的:BLE 4.x及更早版本的传统广播包单次最大载荷仅为31字节,还要扣除每个AD结构的长度、类型字段开销,你当前代码使用了128位随机UUID作为服务标识,仅UUID就占用了16字节,剩余留给有效载荷的空间不足15字节,自然无法承载你解码后长达32字节的TEK数据。
可行解决方案
- 方案1:使用BLE扩展广播
BLE 5.0之后新增的扩展广播特性支持最大255字节的广播载荷,只要你的发播设备和扫描设备都支持BLE 5.0、Android系统版本在API 26以上,只需要在AdvertiseSettings中添加setLegacyMode(false)配置即可直接传输完整数据,兼容性受设备硬件限制。 - 方案2:拆分数据到主广播包+扫描响应包
传统广播支持同时发送主广播包和扫描响应包,两个包各有31字节载荷,总可用空间可达62字节,完全可以容纳32字节的TEK数据,扫描设备主动发起扫描请求即可拿到两部分数据拼接还原,该方案兼容性最好,支持所有BLE 4.0以上设备。 - 方案3:压缩载荷体积
你当前传入的TEK是Base64编码字符串,Base64编码会比原始二进制数据多占用33%的体积,直接传输解码后的原始二进制字节即可省出大量空间;另外将随机128位服务UUID替换为16位官方注册UUID,可再省14字节的UUID开销,优化后单包即可放下32字节TEK。 - 方案4:多包轮换广播
如果上述方案都不符合你的需求,可以将长数据拆分为带序号的分片,循环广播不同分片的内容,扫描端收集全部分片后按序号拼接还原,缺点是扫描端需要停留一段时间才能收全数据。
优化代码示例(主广播+扫描响应兼容方案)
private fun advertise(tekBase64: String) { val advertiser = BluetoothAdapter.getDefaultAdapter().bluetoothLeAdvertiser ?: return // 解码Base64得到原始TEK二进制,减少33%体积 val tekRaw = Base64.decode(tekBase64, Base64.NO_WRAP) // 用16位预定义UUID代替随机128位UUID,省14字节开销 val serviceUuid = ParcelUuid.fromString("0000FFF0-0000-1000-8000-00805F9B34FB") // 拆分数据:前20字节放主广播包 val mainChunk = tekRaw.copyOfRange(0, 20) val mainAdData = AdvertiseData.Builder() .setIncludeDeviceName(false) // 关闭设备名广播省空间 .addServiceData(serviceUuid, mainChunk) .build() // 剩余字节放扫描响应包 val scanChunk = tekRaw.copyOfRange(20, tekRaw.size) val scanResponse = AdvertiseData.Builder() .setIncludeDeviceName(false) .addServiceData(serviceUuid, scanChunk) .build() val settings = AdvertiseSettings.Builder() .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED) .setConnectable(true) .setTimeout(0) .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM) .build() val advertiseCallback = object : AdvertiseCallback() { override fun onStartSuccess(settingsInEffect: AdvertiseSettings) { Log.d(TAG, "BLE Advertising start") super.onStartSuccess(settingsInEffect) } override fun onStartFailure(errorCode: Int) { Log.d(TAG, "BLE Advertising onStartFailure: $errorCode") super.onStartFailure(errorCode) } } // 传入扫描响应参数启动广播 advertiser.startAdvertising(settings, mainAdData, scanResponse, advertiseCallback) finish() }
内容的提问来源于stack exchange,提问作者Hally
相关产品推荐
相关产品推荐

