CBPeripheral WriteValue无断点时不调用代理方法的问题求助
问题分析与解决方案
你遇到的是典型的蓝牙异步写入的速率控制问题——调试时断点给了外设足够的时间处理每个固件块,但正常运行时你可能在连续快速调用makeFWBLOCK,而CBPeripheral.writeValue(_:for:type:)是异步操作,外设还没处理完上一个包就收到了下一个,导致丢包或处理失败。
核心问题拆解
蓝牙BLE的withResponse写入需要外设返回确认包后,才算完成一次写入。你的代码目前没有等待这个确认,而是连续触发写入,外设无法跟上这个速率,自然会失败;而断点相当于强制暂停了发送节奏,给了外设足够的处理时间,所以调试时能成功。
具体修复方案
1. 引入写入状态控制与回调驱动的发送逻辑
你需要实现回调触发下一次写入的机制,确保只有当上一个固件块写入成功并收到外设确认后,再发送下一个块。
首先,在你的类中添加几个状态变量:
// 标记当前是否正在进行蓝牙写入 private var isWritingFirmware = false // 当前要发送的固件位置 private var currentFWPos = 0
2. 修改makeFWBLOCK为可触发单次写入的方法
调整方法,去掉自动累加位置的逻辑,改为依赖回调更新位置:
func sendFWBlock(at position: Int) { guard !isWritingFirmware else { // 如果正在写入,直接返回避免并发 return } isWritingFirmware = true let totalCallCount = (position / 128) + 1 print("total Call Count ----- ", totalCallCount) var txlen = 128 if (byteArray.count - position) < 128 { txlen = byteArray.count - position } // 以下是你原有的数据包构建逻辑,保留不变 let dataForPayloadSize = NSData(bytes: [6 + txlen], length: 2) var resultPayloadSize = [UInt8](repeating: 0, count: 2) dataForPayloadSize.getBytes(&resultPayloadSize, length: 2) let dataForMajor = NSData(bytes: [4], length: 1) var resultMajor = [UInt8](repeating: 0, count: 1) dataForMajor.getBytes(&resultMajor, length: 1) let dataForMinor = NSData(bytes: [5], length: 1) var resultMinor = [UInt8](repeating: 0, count: 1) dataForMinor.getBytes(&resultMinor, length: 1) let dataForSuffix = NSData(bytes: [65], length: 1) var resultSuffix = [UInt8](repeating: 0, count: 1) dataForSuffix.getBytes(&resultSuffix, length: 1) let numberOfChunks = (txlen / 64) + (((txlen % 64) > 0) ? 1 : 0) let dataForNumberOfChunks = NSData(bytes: [numberOfChunks], length: 1) var resultNumberOfChunks = [UInt8](repeating: 0, count: 1) dataForNumberOfChunks.getBytes(&resultNumberOfChunks, length: 1) let offset = position / 64 let dataForOffset = NSData(bytes: [offset], length: 2) var resultOffset = [UInt8](repeating: 0, count: 2) dataForOffset.getBytes(&resultOffset, length: 2) var value: [UInt8] = [0x02] value.append(resultPayloadSize[0]) value.append(resultPayloadSize[1]) value.append(resultMajor[0]) value.append(resultMinor[0]) value.append(resultSuffix[0]) value.append(resultNumberOfChunks[0]) value.append(resultOffset[0]) value.append(resultOffset[1]) let start = offset * 64 let end = start + txlen - 1 value.append(contentsOf: byteArray[start...end]) let remaining = (byteArray.count - 1) - end print("remaining:", remaining) let data = Data(bytes: value) self.pheripharal_To_Connect?.writeValue(data, for: BluetoothManager.SharedInstance.characteristic!, type: .withResponse) }
3. 实现CBPeripheralDelegate的写入回调
在你的蓝牙代理类中,实现didWriteValueFor方法,处理写入结果并触发下一次写入:
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) { guard characteristic == BluetoothManager.SharedInstance.characteristic else { return } isWritingFirmware = false if let error = error { // 写入失败,这里可以添加重试逻辑 print("Firmware block write failed: \(error.localizedDescription)") // 可选:重试当前块 sendFWBlock(at: currentFWPos) return } // 写入成功,更新位置并发送下一个块 currentFWPos += 128 if currentFWPos < byteArray.count { sendFWBlock(at: currentFWPos) } else { // 所有块发送完成,固件更新结束 print("Firmware update completed successfully!") } }
4. 启动固件更新的入口方法
添加一个启动方法,初始化状态并发送第一个块:
func startFirmwareUpdate() { currentFWPos = 0 isWritingFirmware = false sendFWBlock(at: currentFWPos) }
额外注意事项
- 确保你的类已经设置为
CBPeripheral的代理(peripheral.delegate = self),否则无法收到写入回调。 - 可以添加超时机制:如果一段时间内没收到写入回调,主动重试当前块。
- 外设端也要确保能正确处理每个块并返回确认,如果你能修改外设固件,也要检查其接收速率和响应逻辑。
内容的提问来源于stack exchange,提问作者Nikh1414
相关产品推荐
相关产品推荐

