iOS BLE开发:CBPeripheralManager传输图片数据及updateValue方法问题
实现BLE图片数据传输(基于CBPeripheralManager.updateValue)
嘿,我来帮你搞定这个BLE图片传输的问题!你已经有了PeripheralManager的状态回调,接下来咱们一步步实现图片数据的分片发送逻辑:
1. 先准备BLE服务和可通知特征
首先得创建一个BLE服务,以及一个支持**通知(Notify)**的特征——因为图片数据通常比较大,用通知模式来推送分片数据是最合适的。在你的peripheralManagerDidUpdateState里补充.poweredOn的分支:
func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) { switch peripheral.state { case .unknown: print("central.state is .unknown") case .resetting: print("central.state is .resetting") case .unsupported: print("central.state is .unsupported") case .unauthorized: print("central.state is .unauthorized") case .poweredOff: print("central.state is .poweredOff...") case .poweredOn: // 当BLE开启时,创建服务和特征 let imageServiceUUID = CBUUID(string: "YOUR_CUSTOM_SERVICE_UUID") let imageCharacteristicUUID = CBUUID(string: "YOUR_CUSTOM_CHARACTERISTIC_UUID") // 创建可通知的特征,属性设为Notify,权限设为可读 let imageCharacteristic = CBMutableCharacteristic( type: imageCharacteristicUUID, properties: .notify, value: nil, permissions: .readable ) // 创建服务并添加特征 let imageService = CBMutableService(type: imageServiceUUID, primary: true) imageService.characteristics = [imageCharacteristic] // 添加服务到PeripheralManager peripheral.add(imageService) // 开始广播服务 peripheral.startAdvertising([CBAdvertisementDataServiceUUIDsKey: [imageServiceUUID]]) print("BLE开启,已启动服务广播") @unknown default: print("未知状态") } }
2. 处理图片数据的分片
BLE的MTU(最大传输单元)默认是20字节(实际可用数据是17字节,因为ATT层有3字节头部),所以需要把图片Data切成多个小分片:
// 全局或类成员变量,存储分片数据和当前发送索引 var imageDataChunks: [Data] = [] var currentChunkIndex = 0 var targetCharacteristic: CBMutableCharacteristic? var peripheralManager: CBPeripheralManager! // 确保你已经初始化了这个实例 // 拆分图片数据的方法 func splitImageData(_ imageData: Data) { let mtuSize = peripheralManager.maximumWriteValueLength(for: .withoutResponse) // 实际可用的分片大小:MTU减去ATT头的3字节 let chunkSize = mtuSize - 3 // 按chunkSize拆分数据 imageDataChunks.removeAll() for i in stride(from: 0, to: imageData.count, by: chunkSize) { let endIndex = min(i + chunkSize, imageData.count) let chunk = imageData[i..<endIndex] imageDataChunks.append(chunk) } currentChunkIndex = 0 print("图片已拆分为\(imageDataChunks.count)个分片") }
3. 用updateValue发送分片数据
当中心设备订阅了你的特征后,就可以开始发送分片。首先要监听特征的订阅事件:
func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didSubscribeTo characteristic: CBCharacteristic) { print("中心设备已订阅特征,开始发送图片分片") targetCharacteristic = characteristic as? CBMutableCharacteristic // 假设你已经把图片转成了Data,这里用示例图片做演示 if let imageData = UIImage(named: "yourImageName")?.pngData() { splitImageData(imageData) // 发送第一个分片 sendNextChunk() } } // 发送下一个分片的方法 func sendNextChunk() { guard let characteristic = targetCharacteristic, currentChunkIndex < imageDataChunks.count else { print("所有分片已发送完成") return } let chunk = imageDataChunks[currentChunkIndex] // 调用updateValue发送数据,第二个参数是目标特征 let success = peripheralManager.updateValue(chunk, for: characteristic, onSubscribedCentrals: nil) if success { currentChunkIndex += 1 print("已发送第\(currentChunkIndex)/\(imageDataChunks.count)个分片") // 继续发送下一个(如果队列没满的话) sendNextChunk() } else { print("当前发送队列已满,等待回调继续") } }
4. 处理发送队列满的情况
当updateValue返回false时,说明BLE发送队列已满,需要等待系统回调后再继续发送:
func peripheralManagerIsReadyToUpdateSubscribers(_ peripheral: CBPeripheralManager) { print("发送队列已就绪,继续发送分片") sendNextChunk() }
关键注意点
- MTU协商:如果想提升传输效率,可以和中心设备协商更大的MTU,这样能减少分片数量(最大MTU可达512字节)。
- 数据校验:建议在每个分片里加入分片索引、总片数等标识信息,让中心设备能正确拼接数据,避免丢片或顺序错乱。
- 异常处理:要处理中心设备取消订阅、连接断开等情况,及时停止发送并清理分片数据。
内容的提问来源于stack exchange,提问作者ironRoei
相关产品推荐
相关产品推荐

