Core Bluetooth读取HM-10 Arduino数据失败,求解决及回显示例
问题描述
我搭建了一个基础Arduino项目,搭配HM-10 BLE模块用于发送字符串形式的温度测量数据,该项目在iOS应用BluetoothForArduino上可正常运行。我希望在macOS上获取这些数据,因此基于AsyncBluetooth库编写了以下代码片段:
func test() async throws { let centralManager = CentralManager() try await centralManager.waitUntilReady() let serviceUUID = CBUUID(string: "FFE0") let characteristicsUUID = CBUUID(string: "FFE1") let deviceName = "HMSoft" let scanDataStream = try await centralManager.scanForPeripherals(withServices: [serviceUUID]) if let foundDevice = await scanDataStream.first( where: { $0.peripheral.name?.hasPrefix(deviceName) == true } ) { await centralManager.stopScan() let peripheral = foundDevice.peripheral print("Found device : \(peripheral.name ?? \"no name\"), RSSI = \(foundDevice.rssi)") print("Connecting...") try await centralManager.connect(peripheral) print("Ok") try await peripheral.discoverServices(nil) if let service = peripheral.discoveredServices?.first { try await peripheral.discoverCharacteristics(nil, for: service) service.discoveredCharacteristics?.forEach{ print($0) } print("Services UUIDs discovered : \(service.uuid)") if let characteristics = service.discoveredCharacteristics? .first(where: { $0.uuid == characteristicsUUID }) { print("Characteristics UUIDs discovered : \(characteristics.uuid)") try await peripheral.setNotifyValue( true, forCharacteristicWithCBUUID: characteristicsUUID, ofServiceWithCBUUID : serviceUUID ) print("Notifications enabled") // try await peripheral.writeValue( // "s".data(using: .ascii)!, // for: characteristics, // type: .withoutResponse // ) if let data: Data = try await peripheral.readValue( forCharacteristicWithCBUUID: characteristicsUUID, ofServiceWithCBUUID: serviceUUID ) { data.forEach { print($0) } } } } print("Disconnecting...") try await centralManager.cancelPeripheralConnection(foundDevice.peripheral) print("Ok") } } try await test()
但问题在于,我始终从设备获取到相同的6字节数据(同时我能正确识别服务及其特征)。请问我哪里出错了?能否分享一个基于Core Bluetooth的简单回显示例代码?
问题原因分析
- 过早断开连接:代码在启用通知后立刻执行
readValue,随即断开连接,完全没有等待HM-10推送实时温度数据。HM-10这类模块的核心数据推送方式是通知(Notification),而非主动读取。 - readValue的局限性:
readValue仅能获取特征当前的缓存数据,若模块未实时更新该缓存,就会持续返回旧数据。要获取实时推送的新数据,必须依赖通知监听机制。 - AsyncBluetooth API使用错误:启用通知后未设置监听回调,仅做单次读取,自然无法捕获模块主动发送的新数据。
基于Core Bluetooth的macOS BLE数据接收示例
以下是直接使用Core Bluetooth框架实现的HM-10数据接收示例,可持续接收温度数据:
import Cocoa import CoreBluetooth class BLECentralManager: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate { private var centralManager: CBCentralManager! private var targetPeripheral: CBPeripheral? private let serviceUUID = CBUUID(string: "FFE0") private let characteristicUUID = CBUUID(string: "FFE1") private let deviceNamePrefix = "HMSoft" override init() { super.init() // 初始化中心管理器,指定代理和主队列 centralManager = CBCentralManager(delegate: self, queue: DispatchQueue.main) } // MARK: - CBCentralManagerDelegate func centralManagerDidUpdateState(_ central: CBCentralManager) { switch central.state { case .poweredOn: print("BLE已开启,开始扫描目标设备...") // 扫描指定服务的设备,禁止重复扫描同一设备 centralManager.scanForPeripherals(withServices: [serviceUUID], options: [CBCentralManagerScanOptionAllowDuplicatesKey: false]) case .poweredOff: print("BLE模块已关闭,请开启后重试") case .unauthorized: print("BLE权限未授权,请在系统设置中开启权限") default: print("BLE状态异常: \(central.state.rawValue)") } } func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) { guard let peripheralName = peripheral.name, peripheralName.hasPrefix(deviceNamePrefix) else { return } print("发现目标设备: \(peripheralName),信号强度: \(RSSI)") centralManager.stopScan() targetPeripheral = peripheral targetPeripheral?.delegate = self // 连接设备 centralManager.connect(peripheral, options: nil) } func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) { print("设备连接成功,开始发现服务...") // 发现指定服务 peripheral.discoverServices([serviceUUID]) } func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) { print("设备断开连接,重新扫描...") targetPeripheral = nil centralManager.scanForPeripherals(withServices: [serviceUUID], options: [CBCentralManagerScanOptionAllowDuplicatesKey: false]) } // MARK: - CBPeripheralDelegate func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) { guard let services = peripheral.services else { print("发现服务失败: \(error?.localizedDescription ?? "未知错误")") return } for service in services { if service.uuid == serviceUUID { print("发现目标服务: \(service.uuid.uuidString)") // 发现指定特征 peripheral.discoverCharacteristics([characteristicUUID], for: service) } } } func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { guard let characteristics = service.characteristics else { print("发现特征失败: \(error?.localizedDescription ?? "未知错误")") return } for characteristic in characteristics { if characteristic.uuid == characteristicUUID { print("发现目标特征: \(characteristic.uuid.uuidString)") // 启用通知,接收设备主动推送的数据 peripheral.setNotifyValue(true, for: characteristic) } } } func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) { guard let data = characteristic.value else { print("接收数据失败: \(error?.localizedDescription ?? "无数据")") return } // 将Data转换为字符串(HM-10默认使用ASCII编码,若Arduino端修改过编码需对应调整) if let temperatureString = String(data: data, encoding: .ascii) { print("收到温度数据: \(temperatureString)") } else { print("数据解码失败,原始字节: \(data)") } } } // 启动BLE管理器 let bleManager = BLECentralManager() // 保持主循环运行,避免程序退出 RunLoop.main.run()
关键注意事项
- 依赖通知机制:HM-10通过特征通知推送数据,必须调用
setNotifyValue(true, for: characteristic),并实现didUpdateValueFor回调接收实时数据,不要依赖readValue获取新数据。 - 保持连接状态:不要过早断开连接,示例会持续监听直到设备离线或手动断开。
- 编码一致性:确保Arduino发送字符串的编码与macOS解码编码一致(HM-10默认ASCII,若修改过需同步调整)。
内容的提问来源于stack exchange,提问作者Dmitry
相关产品推荐
相关产品推荐

