Swift开发欧姆龙BP7000 BLE设备:血压数据读取及时间读写问题求助
解决欧姆龙BP7000 BLE血压数据读取及时间写入错误(CBATTErrorDomain Code:128)
问题概述
已成功配对欧姆龙BP7000,能读取电池等其他服务数据,但读写当前时间特征、读取血压测量特征时触发CBATTErrorDomain Code:128错误,didUpdateValueFor无法获取血压数据。
核心问题分析与修复方案
1. 移除BLE操作并发冲突(触发128错误的主要原因)
在didUpdateValueFor的currentTimeCBUUID分支中,你在收到特征更新后立即发起readValue和writeValue操作,会导致BLE命令并发冲突,设备无法处理直接返回128错误。
修复代码:
移除peripheral.readValue(for: characteristic),且不在didUpdateValueFor回调内执行写操作。建议在发现特征后直接处理,比如在didDiscoverCharacteristicsFor代理方法中操作:
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { guard let characteristics = service.characteristics else { return } for char in characteristics { switch char.uuid { case currentTimeCBUUID: // 发现当前时间特征后直接写入时间 writeCurrentTime(to: char, in: peripheral) case bloodPressureMeasurementCBUUID: // 开启血压特征的通知(血压数据为主动推送) peripheral.setNotifyValue(true, for: char) case batteryLevelCBUUID: peripheral.readValue(for: char) default: break } } }
2. 修正当前时间特征的写入格式(格式错误导致写入失败)
你的writeCurrentTime方法存在多处不符合蓝牙**Current Time Characteristic(UUID:2A2B)**规范的错误,设备拒绝写入并返回128错误:
- 多余的空数据写入
value.append(Data(bytes: "", count: 0)) - Year字节序处理错误,未使用大端字节序
- 误将Fractions of a second设为2字节(规范为1字节的Fractions256)
修复后的writeCurrentTime方法:
func writeCurrentTime(to characteristic: CBCharacteristic, in peripheral: CBPeripheral) { let currentDate = Date() let calendar = Calendar.current let components = calendar.dateComponents([.year, .month, .day, .hour, .minute, .second, .weekday], from: currentDate) guard let year = components.year, let month = components.month, let day = components.day, let hour = components.hour, let minute = components.minute, let second = components.second, let weekday = components.weekday else { print("Failed to get date components") return } var value = Data() // Year: 2字节大端 let yearBigEndian = year.bigEndian value.append(Data(bytes: &yearBigEndian, count: 2)) // Month: 1字节 value.append(UInt8(month)) // Day: 1字节 value.append(UInt8(day)) // Hours: 1字节 value.append(UInt8(hour)) // Minutes: 1字节 value.append(UInt8(minute)) // Seconds: 1字节 value.append(UInt8(second)) // Day of Week: 1字节(1=周日,7=周六) value.append(UInt8(weekday)) // Fractions256: 1字节(当前秒的1/256,取0即可) value.append(UInt8(0)) peripheral.writeValue(value, for: characteristic, type: .withResponse) }
3. 修复血压数据的读取与解码逻辑
关键前提:必须开启血压特征的通知
血压测量特征(UUID:2A35)通过通知主动推送数据,不是主动读取,确保在发现特征后调用peripheral.setNotifyValue(true, for: char)(参考步骤1代码)。
修正解码逻辑(适配Flags字节的动态偏移)
你的解码逻辑忽略了Flags字节的影响,且血压值为SFloat格式(16位有符号,乘以1/10),直接相加会得到错误值:
func decodeBloodPressureMeasurementData(from characteristic: CBCharacteristic) { guard let data = characteristic.value else { print("No blood pressure data") return } let flags = data[0] let isKpa = (flags & 0b00000001) != 0 // Bit0: 0=mmHg, 1=kPa let hasTimestamp = (flags & 0b00000010) != 0 // Bit1: 是否包含时间戳 let hasPulseRate = (flags & 0b00001000) != 0 // Bit3: 是否包含脉搏率 var offset = 1 // 跳过Flags字节 // 解码血压值(SFloat格式) let systolicRaw = UInt16(data[offset]) | (UInt16(data[offset+1]) << 8) let systolic = Float(Int16(bitPattern: systolicRaw)) / 10.0 offset += 2 let diastolicRaw = UInt16(data[offset]) | (UInt16(data[offset+1]) << 8) let diastolic = Float(Int16(bitPattern: diastolicRaw)) / 10.0 offset += 2 let meanRaw = UInt16(data[offset]) | (UInt16(data[offset+1]) << 8) let mean = Float(Int16(bitPattern: meanRaw)) / 10.0 offset += 2 // 跳过时间戳(如果存在) if hasTimestamp { offset += 7 // 时间戳固定7字节 } // 解码脉搏率(如果存在) var pulseRate: Float? if hasPulseRate { let pulseRaw = UInt16(data[offset]) | (UInt16(data[offset+1]) << 8) pulseRate = Float(Int16(bitPattern: pulseRaw)) / 10.0 } let unit = isKpa ? "kPa" : "mmHg" print("收缩压: \(systolic) \(unit)") print("舒张压: \(diastolic) \(unit)") print("平均动脉压: \(mean) \(unit)") if let rate = pulseRate { print("脉搏率: \(rate) bpm") } }
4. 完善错误处理
在didUpdateValueFor开头添加错误判断,及时定位问题:
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) { if let error = error { print("特征更新错误: \(error)") return } // 后续的特征判断逻辑... }
内容的提问来源于stack exchange,提问作者Kshitij876
相关产品推荐
相关产品推荐

