You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 09:12:01