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

如何用CoreBluetooth仅扫描Beacon?添加指定服务后无外设发现排查

解决CoreBluetooth扫描Beacon无回调的问题

嘿,这个问题我之前帮人排查过好几次,核心误区其实是你把「连接后能看到的服务」和「广播出来的服务」搞混啦!你在LightBlue里查到的那5个服务(180A、FFA0等),是连接到Beacon之后才能发现的私有/通用服务,但绝大多数Beacon并不会把这些服务UUID主动广播到空中——CoreBluetooth用服务UUID扫描时,只会响应那些在广播包里明确声明了对应UUID的设备,自然就触发不了didDiscover回调了。

接下来给你一步步的排查和解决方法:

1. 先做无过滤扫描,摸清Beacon的真实广播内容

先暂时去掉服务过滤,用最基础的扫描方式,把所有能扫到的设备的广播数据打印出来,搞清楚你的Beacon到底在对外广播什么:

// 开启无过滤扫描,允许重复扫描避免漏设备
centralManager.scanForPeripherals(withServices: nil, options: [CBCentralManagerScanOptionAllowDuplicatesKey: true])

然后在didDiscover回调里打印关键广播字段:

func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
    print("设备名称: \(peripheral.name ?? "未知设备")")
    // 重点关注这两个核心字段:
    print("广播的服务UUID列表: \(advertisementData[CBAdvertisementDataServiceUUIDsKey] ?? "无")")
    print("制造商自定义数据: \(advertisementData[CBAdvertisementDataManufacturerDataKey] ?? "无")")
}

通过这一步,你就能明确:你的Beacon到底有没有广播你指定的服务UUID,还是只广播了制造商专属数据(绝大多数Beacon都是后者)。

2. 根据广播内容调整扫描策略

情况A:Beacon确实广播了服务UUID

如果打印结果里,CBAdvertisementDataServiceUUIDsKey字段包含你要的16位UUID,那要注意:CoreBluetooth要求用完整的128位标准UUID来做扫描过滤,16位短码必须补全成通用格式。比如:

  • 180A对应的完整UUID:0000180A-0000-1000-8000-00805F9B34FB
  • FFA0对应的完整UUID:0000FFA0-0000-1000-8000-00805F9B34FB

把这些完整UUID转成CBUUID对象后再传入扫描方法:

let targetServiceUUIDs = [
    CBUUID(string: "0000180A-0000-1000-8000-00805F9B34FB"),
    CBUUID(string: "0000FFA0-0000-1000-8000-00805F9B34FB"),
    // 另外三个服务的完整UUID按相同规则补全
]
centralManager.scanForPeripherals(withServices: targetServiceUUIDs, options: nil)

情况B:Beacon只广播了制造商数据(绝大多数场景)

如果CBAdvertisementDataManufacturerDataKey有值,而服务UUID列表为空,那你就不能用服务UUID过滤,而是要在didDiscover回调里手动过滤制造商数据。举个例子,如果是苹果iBeacon,它的制造商ID是0x4C00(小端序),可以这么过滤:

func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
    guard let manufacturerData = advertisementData[CBAdvertisementDataManufacturerDataKey] as? Data else {
        return
    }
    // 读取前2字节的制造商ID(注意字节序)
    let manufacturerID = manufacturerData.subdata(in: 0..<2).withUnsafeBytes { $0.load(as: UInt16.self) }
    // 匹配苹果iBeacon的制造商ID
    if manufacturerID == 0x4C00 {
        // 这里可以进一步解析Beacon的UUID、Major、Minor等信息
        print("找到目标Beacon: \(peripheral.name ?? "未知Beacon")")
        // 你的业务处理逻辑...
    }
}

如果是其他厂商的Beacon,你可以根据无过滤扫描得到的制造商ID和数据格式,调整过滤规则。

3. 排查其他容易踩的坑

  • 蓝牙权限:iOS 13及以上必须在Info.plist里添加NSBluetoothAlwaysUsageDescription(iOS 12及以下用NSBluetoothPeripheralUsageDescription),否则系统会限制扫描权限,didDiscover根本不会触发。
  • 蓝牙状态检查:一定要确保centralManager的状态是.poweredOn之后再开始扫描,否则扫描会无声失败。可以在状态回调里处理:
func centralManagerDidUpdateState(_ central: CBCentralManager) {
    switch central.state {
    case .poweredOn:
        // 蓝牙正常开启,开始扫描
        central.scanForPeripherals(withServices: nil, options: nil)
    case .poweredOff:
        print("请先开启蓝牙")
    default:
        print("蓝牙不可用")
    }
}
  • 扫描选项:如果Beacon广播频率较低,记得添加CBCentralManagerScanOptionAllowDuplicatesKey: true,避免因为系统去重而漏扫设备。

内容的提问来源于stack exchange,提问作者tchka

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 07:54:30