Проблема
Я использую Core-Bluetooth для передачи данных датчика (массив 64-128 байт) на iPad (6-го поколения) с частотой 6 Гц. В ios 12. все работало нормально. После обновления до ios 13 соединение Bluetooth стало невероятно ненадежным. Соединение будет передавать только 20 байтов за раз, и оно будет часто отключаться (вызывается centralManager(_ central: CBCentralManager, didDisconnectPeripheral...
) с ошибкой Code=0 "Unknown error." UserInfo={NSLocalizedDescription=Unknown error.}
.
Глядя на экран iPad во время отладки, я замечал каждый раз, когда центральное соединение подключаетсяи мой сервис обнаружен, на полсекунды появляется сообщение о безопасности "Bluetooth Pairing Request: "<device-name>" would like to pair with your iPad. [Cancel] [Pair]
" до отключения, описанного выше.
Так что может показаться, что по какой-то причине ядро Bluetooth вызывает запрос безопасности и(я предполагаю) задержка вызывает сбой соединения? Странно то, что это приглашение происходит как 19/20 раз, но иногда соединение проходит без запуска запроса, и перед отключением принимается пара буферов.
Связанные проблемы
Существует другой пост о чем-то похожем: как предотвратить запрос разрешения в ios 13, но похоже, что это больше связано с периферийным устройством, основанным на сообщении об ошибке.
Есть пост детализация пропущенного значения в словаре CBCentralManager didDiscover peripheral
, но я все равно не использую это значение.
Я определенно храню жесткую ссылку на периферию, так что здесь проблема не в этом. Я также открыл другие приложения, которые позволяют просматривать периферийные серверы Bluetooth 4.0. LightBlue, BLE Scanner и BLE Hero - все показывают сообщение о подключении по Bluetooth, за которым следует отключение от периферийных устройств на моем iPad 6-го поколения.
Моя реализация
Этот проект является частью школьного класса. У меня есть весь код на gitlab. Ниже приведен критический код Bluetooth ios, и проект находится на gitlab .
import Foundation
import CoreBluetooth
struct Peripheral {
var uuid: String
var name: String
var rssi: Double
var peripheral: CBPeripheral
}
class SensorsComputer: BLECentral {
// just a rename of below...
}
class BLECentral: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate {
var manager: CBCentralManager!
var availablePeripherals: [String: Peripheral]
var sensorsConnected: Bool
var ref: CBPeripheral?
var sensorServiceUUIDs: [String]
var bleSensorComputerUUID: String
var data: [String:[UInt8]]
var dataNames: [String:[String:String]]
override init() {
data = [:]
availablePeripherals = [:]
sensorsConnected = false
ref = nil
sensorServiceUUIDs = ["5CA4"]
//bleSensorComputerUUID = "096A1CFA-C6BC-A00C-5A68-3227F2C58C06" // builtin is shit
bleSensorComputerUUID = "33548EF4-EDDE-6E6D-002F-DEEFC0A7AF99" // usb dongle
dataNames = [
// service uuids
"5CA4": [
// characteristic uuids
"0000": "LidarRanges",
],
]
super.init()
manager = CBCentralManager(delegate: self, queue: nil)
self.connect(uuid:bleSensorComputerUUID)
}
func scan(){
let services = dataNames.map { CBUUID(string:$0.key) }
manager.scanForPeripherals(withServices: services, options: nil)
}
func connect_internal_(uuid: String) -> Bool{ // TODO known bt device uuids.
if self.sensorsConnected {
// do not try to connect if already connected
return true
} else {
print(self.availablePeripherals.count)
if let found = self.availablePeripherals[uuid] {
manager.stopScan()
manager.connect(found.peripheral, options: nil)
return true
} else {
if availablePeripherals.count == 0 {
scan()
}
print("Error! no peripheral with \(uuid) found!")
return false
}
}
}
func connect(uuid: String){
let queue = OperationQueue()
queue.addOperation() {
// do something in the background
while true {
//usleep(100000)
sleep(1)
OperationQueue.main.addOperation {
self.connect_internal_(uuid: self.bleSensorComputerUUID)
}
}
}
}
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .poweredOn:
print("The central is powered on!")
scan() // automatically start scanning for BLE devices
default:
print("The centraol is NOT powered on (state is \(central.state))")
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
var name = ""
if let name_ = peripheral.name {
name = name_
}
let uuid = peripheral.identifier.uuidString
let rssi = Double(truncating: RSSI)
availablePeripherals[peripheral.identifier.uuidString] = Peripheral(uuid: uuid, name: name, rssi: rssi, peripheral: peripheral)
print(uuid, rssi)
}
func getSorted(uuids:Bool = false) -> [Peripheral] {
let peripherals = Array(availablePeripherals.values)
return peripherals.sorted(by:) {$0.rssi >= $1.rssi}
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("Central connected!")
sensorsConnected = true
peripheral.delegate = self
var cbuuids:[CBUUID] = []
for id in sensorServiceUUIDs {
cbuuids.append(CBUUID(string: id))
}
peripheral.discoverServices(cbuuids) // TODO store service uuids somewhere nicer
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
if let e = error {
print("Central disconnected because \(e)")
} else {
print("Central disconnected! (no error)")
}
sensorsConnected = false
availablePeripherals = [:]
}
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
print("Central failed to connect...")
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
if let error = error {
print("Peripheral could not discover services! Because: \(error.localizedDescription)")
} else {
peripheral.services?.forEach({(service) in
print("Service discovered \(service)")
// TODO characteristics UUIDs known
peripheral.discoverCharacteristics(nil, for: service)
})
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
if let error = error {
print("Could not discover characteristic because error: \(error.localizedDescription)")
} else {
service.characteristics?.forEach({ (characteristic) in
print("Characteristic: \(characteristic)")
if characteristic.properties.contains(.notify){
peripheral.setNotifyValue(true, for: characteristic)
}
if characteristic.properties.contains(.read){
peripheral.readValue(for: characteristic)
}
if characteristic.properties.contains(.write){
peripheral.writeValue(Data([1]), for: characteristic, type: .withResponse)
}
})
}
}
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if let error = error {
print("error after didUpdateValueFor characteristic \(characteristic): \(error.localizedDescription)")
} else {
let sname = characteristic.service.uuid.uuidString
let cname = characteristic.uuid.uuidString
guard let _dname = dataNames[sname], let dataName = _dname[cname] else {
return
}
//print("value of characteristic \(cname) in service \(sname) was updated.")
if let value = characteristic.value {
let value_arr = Array(value)
//print(" The new data value is \(value_arr.count) bytes long")
//print("dataname is \(dataName)")
self.data[dataName] = value_arr
}
}
}
}
Периферийный код BLE - это проект узла, основанный на bleno и rosnodejs для данных датчика. Периферийный код также находится на gitlab .
Я откатился на несколько известных рабочих версий проекта в git, и после обновления до ios 13 ничего не работает. Я собираюсь попробоватьзапустите код corebluetooth в проекте Mac, чтобы проверить, будет ли он там работать.
Вопрос
Итак, после обновления до ios 13 с ios 12.4 при подключении к периферийному устройству BLE,открывается запрос на соединение Bluetooth, хотя связь Bluetooth 4.0 / BLE. Наряду с этим приглашением соединение прерывается, как только оно запускается, иногда давая мне несколько частичных буферов данных, а иногда ничего не давая.
Соединение BLE не правильно инициировано, и если приглашение для соединения работало правильно, это помешало бы моей способности автоматически подключаться к устройству (датчикам роботов) без ввода данных пользователем.
Если кто-нибудь знает какой-либо параметр в info.plist, чтобы предотвратить появление этого разрываемого соединения, это было бы здорово,Я в недоумении, есть ли простой способ откатить версии IOS? Или каким-то образом заставить Apple INC выпустить аварийный патч, предотвращающий всплывающее окно с низким энергопотреблением core-bluetooth? Или каким-то образом обойти всплывающее окно и позволить пользователю правильно проходить аутентификацию, не нарушая протокол BLE?
Спасибо всем, кто взглянул на это.