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

首先要做的事:运行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."

$CBCentralManager.state :所以,我有一个BluetoothManager类,在我的$CBCentralManager.state$CBCentralManager.state ,我开始扫描Peripherals。 这很容易,我甚至是一个好公民,专门为有我想要的服务的人扫描

 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) } 

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

 func centralManager(central: CBCentralManager!, didConnectPeripheral peripheral: CBPeripheral!) { println("Connected \(peripheral.name)") peripheral.delegate = self println("connected to \(peripheral)") peripheral.discoverServices([BluetoothConstants.MY_SERVICE_UUID]) } 

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

 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  discovered service  [...] discovered characteristic  control following properties: read write [...] discovered characteristic  notification following properties: read write notify [...] discovered DescriptorsForCharacteristic [] updateNotification: false 

嘿! 它说updateNotificationfalse 。 它来自哪里? 为什么,这是我对setNotify...的回调setNotify...

 func peripheral(peripheral: CBPeripheral!, didUpdateNotificationStateForCharacteristic characteristic: CBCharacteristic!, error: NSError!) { println("updateNotification: \(characteristic.isNotifying)") } 

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

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

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

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

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

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