订阅来自CBC特征的通知不起作用

时间:2015-07-07 17:04:07

标签: ios bluetooth bluetooth-lowenergy core-bluetooth ios-bluetooth

首先要做的事情:运行OSX 10.10.4,iOS 4,Xcode 6.3.2,iPhone 6,Swift

短篇小说:我在这里有一个蓝牙LE设备,我希望在特征值发生变化时接收通知,例如:通过用户输入。尝试订阅它不会成功,而是会产生错误Error Domain=CBATTErrorDomain Code=10 "The attribute could not be found."

长篇故事:所以,我有一个BluetoothManager课程,在$CBCentralManager.state .PoweredOn centralManager.scanForPeripheralsWithServices([ServiceUUID], options: nil) 后,我就开始扫描外围设备。这很容易,我甚至是一个好公民,专门为那些有我想要的服务的人扫描

func centralManager(central: CBCentralManager!, didDiscoverPeripheral peripheral: CBPeripheral!, advertisementData: [NSObject : AnyObject]!, RSSI: NSNumber!) {

    if *this is a known device* {
        connectToPeripheral(peripheral)
        return
    }

    [...] // various stuff to make something a known device, this works
}

希望这会成功,我实现了以下委托方法:

func connectToPeripheral(peripheral: CBPeripheral) {
    println("connecting to \(peripheral.identifier)")

    [...] // saving the peripheral in an array along the way so it is being retained

    centralManager.connectPeripheral(peripheral, options: nil)
}

所以继续前进,我们得到:

func centralManager(central: CBCentralManager!, didConnectPeripheral peripheral: CBPeripheral!) {        
    println("Connected \(peripheral.name)")

    peripheral.delegate = self

    println("connected to \(peripheral)")
    peripheral.discoverServices([BluetoothConstants.MY_SERVICE_UUID])
}

Yupp,这成功了,所以我得到了确认并开始发现服务:

func peripheral(peripheral: CBPeripheral!, didDiscoverServices error: NSError!) {

    if peripheral.services != nil {
        for service in peripheral.services {
            println("discovered service \(service)")
            let serviceObject = service as! CBService

            [...] // Discover the Characteristic to send controls to, this works
            peripheral.discoverCharacteristics([BluetoothConstants.MY_CHARACTERISTIC_NOTIFICATION_UUID], forService: serviceObject)

           [...] // Some unneccessary stuff about command caches
        }
    }
}

哪个也有效,因为该委托方法也被调用:

func peripheral(peripheral: CBPeripheral!, didDiscoverCharacteristicsForService service: CBService!, error: NSError!) {
    for characteristic in service.characteristics {
        let castCharacteristic = characteristic as! CBCharacteristic

        characteristics.append(castCharacteristic) // Retaining the characteristic in an Array as well, not sure if I need to do this

        println("discovered characteristic \(castCharacteristic)")
        if *this is the control characteristic* {
            println("control")
        } else if castCharacteristic.UUID.UUIDString == BluetoothConstants.MY_CHARACTERISTIC_NOTIFICATION_UUID.UUIDString {
            println("notification")
            peripheral.setNotifyValue(true, forCharacteristic: castCharacteristic)
        } else {
            println(castCharacteristic.UUID.UUIDString) // Just in case
        }
        println("following properties:")

        // Just to see what we are dealing with
        if (castCharacteristic.properties & CBCharacteristicProperties.Broadcast) != nil {
            println("broadcast")
        }
        if (castCharacteristic.properties & CBCharacteristicProperties.Read) != nil {
            println("read")
        }
        if (castCharacteristic.properties & CBCharacteristicProperties.WriteWithoutResponse) != nil {
            println("write without response")
        }
        if (castCharacteristic.properties & CBCharacteristicProperties.Write) != nil {
            println("write")
        }
        if (castCharacteristic.properties & CBCharacteristicProperties.Notify) != nil {
            println("notify")
        }
        if (castCharacteristic.properties & CBCharacteristicProperties.Indicate) != nil {
            println("indicate")
        }
        if (castCharacteristic.properties & CBCharacteristicProperties.AuthenticatedSignedWrites) != nil {
            println("authenticated signed writes ")
        }
        if (castCharacteristic.properties & CBCharacteristicProperties.ExtendedProperties) != nil {
            println("indicate")
        }
        if (castCharacteristic.properties & CBCharacteristicProperties.NotifyEncryptionRequired) != nil {
            println("notify encryption required")
        }
        if (castCharacteristic.properties & CBCharacteristicProperties.IndicateEncryptionRequired) != nil {
            println("indicate encryption required")
        }

        peripheral.discoverDescriptorsForCharacteristic(castCharacteristic) // Do I need this?
    }
}

你知道什么:发现了 的特征!

connected to <CBPeripheral: 0x1740fc780, identifier = $FOO, name = $SomeName, state = connected>
discovered service <CBService: 0x170272c80, isPrimary = YES, UUID = $BAR>
[...]
discovered characteristic <CBCharacteristic: 0x17009f220, UUID = $BARBAR properties = 0xA, value = (null), notifying = NO>
control
following properties:
read
write
[...]
discovered characteristic <CBCharacteristic: 0x17409d0b0, UUID = $BAZBAZ, properties = 0x1A, value = (null), notifying = NO>
notification
following properties:
read
write
notify
[...]
discovered DescriptorsForCharacteristic
[]
updateNotification: false

现在控制台输出到此为止:

updateNotification

喂!它说falsesetNotify...。它来自哪里?为什么,这是我对func peripheral(peripheral: CBPeripheral!, didUpdateNotificationStateForCharacteristic characteristic: CBCharacteristic!, error: NSError!) { println("updateNotification: \(characteristic.isNotifying)") } 的回调:

(lldb) po error
Error Domain=CBATTErrorDomain Code=10 "The attribute could not be found." UserInfo=0x17026eac0 {NSLocalizedDescription=The attribute could not be found.}

是什么给出的?我告诉它要通知!为什么不通知?让我们在println的行中设置一个断点并检查错误对象:

{{1}}
好的,所以这让我没有想法。我无法找到有关该错误代码的相关线索。自从我尝试为之前发现的特性设置通知以来,我无法理解描述本身,因此必须存在,对吧?此外,在Android上似乎可以订阅通知,所以我想我可以排除设备的问题......或者我可以吗?关于这一点的任何线索都非常感谢!

3 个答案:

答案 0 :(得分:3)

从制造商那里收到更多信息后,我收集到设备已连接并发送通知,无论通信状态如何通知。这意味着即使peripheral(_, didUpdateNotificationStateForCharacteristic, error)告诉我特征没有通知,peripheral(_, didUpdateValueForCharacteristic, error)也会在设备发送时调用并传送数据。我以前没有实现过回调,因为我不希望在这种状态下发送数据。

所以基本上,我的问题似乎已经解决了。然而,如果设备的行为符合蓝牙LE规范,我仍然感兴趣;我没有深入了解那些较低级别的实现,但是假设Apple出于某种原因编写了他们的文档,至少强烈暗示特征状态的变化将在接收任何数据之前。

答案 1 :(得分:3)

对我来说问题是我使用另一台Android设备作为外围设备并需要实现配置描述符。看这里: https://stackoverflow.com/a/25508053/599743

答案 2 :(得分:0)

尽管这是一个古老的问题,并且仍然可以解决,但我还是要提供我的意见和建议。

首先,BLE Peripheral负责定义其特性的行为。外围设备具有定义“特性”属性的选项。 Apple文档here中列出了特性的各种受支持的属性。

现在让我们集中讨论您的问题,您正在尝试听特征值更改,但是订阅失败。

  • 我在您的代码中看到的问题是,无论Feature是否支持它,都进行了预订。仅在受支持的情况下订阅特征。请参考以下代码块,该代码块显示了如何识别特性的受支持属性并基于该属性执行操作。

    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
    
        // Check if characteristics found for service.
        guard let characteristics = service.characteristics, error == nil else {
            print("error: \(String(describing: error))")
            return
        }
    
        // Loop over all the characteristics found.
        for characteristic in characteristics {
            if characteristic.properties.contains([.notify, .notifyEncryptionRequired]) {
                peripheral.setNotifyValue(true, for: characteristic)
            } else if characteristic.properties.contains([.read]) {
                peripheral.readValue(for: characteristic)
            } else if characteristic.properties.contains([.write]) {
                // Perform write operation
            }
        }
    }
    

如果Characteristic支持notify属性,并且您已订阅该属性。成功订阅后,将调用以下委托,您可以在其中验证订阅状态。

func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) { 
    // Check subscription here
    print("isNotifying: \(characteristic.isNotifying)")
}

如果预订成功,则只要特征更新,就会使用新值调用以下委托方法。

func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
    // read updated value for characteristic.
}

几乎没有什么可以检查的

  • 请与外围设备制造商联系,以了解特征实现及其是否支持必要的属性。
  • 检查实施情况,并确保您已按照所有必要步骤进行操作。

我已尝试在所有方面尽可能多地回答。希望能帮助到你。编码愉快:)