У меня есть проект iOS Swift 5, в котором CBCentralManager
используется для подключения к устройству BLE
. Существует запись CBCharacteristic
, которая должна использоваться для записи определенного значения с устройства iOS, и после успешной записи устройство меняет свои службы, добавляя новую службу в свою таблицу GATT
. CoreBluetooth, похоже, не распознает обновленные сервисы. Устройство BLE сопряжено с iOS.
Что пыталось и что случилось:
- Если вы не внедрили
didModifyServices
из CBPeripheralDelegate
, CoreBluetooth жалуется, что службы изменен, но у вас нет метода для обработки этих изменений - Если вы реализуете
didModifyServices
, он никогда не будет вызван, и вышеупомянутое сообщение никогда не будет показано - Попытка ручного повторного обнаружения служб после успешного завершения пишите, но
CBPerpiheral
возвращает только старую службу, несмотря на то, что устройство BLE фактически имеет еще одну в таблице GATT
, как будто CoreBluetooth
кэширует старые службы и фактически не открывает их заново - Попытка отсоединения и отбрасывания экземпляра
CBPeripheral
для его повторного обнаружения и повторного подключения - в этом случае периферийное устройство никогда не переоткрывается снова после отключения.
Любые идеи, которые можно было бы сделать, чтобы иметь возможность восстановить новый сервис?
Соответствующий код CoreBluetooth:
enum BluetoothStateChange
{
case unknown
case unsupported
case unauthorized
case poweredOff
}
class BluetoothCentralService: NSObject
{
var onStateChange: ((BluetoothStateChange) -> Void)?
var onServicesScannedPeripheralsUpdate: ((_ peripherals: [UUID]) -> Void)?
var onCharacteristicsDiscovered: ((_ peripheral: UUID, _ service: CBUUID, _ characteristics: [CBUUID]) -> Void)?
var onCharacteristicWriteFinished: ((_ peripheral: UUID, _ characteristic: CBUUID, _ isSuccessful: Bool) -> Void)?
var onCharacteristicReadFinished: ((_ peripheral: UUID, _ characteristic: CBUUID, _ value: Data?) -> Void)?
private var connectingPeripherals = Set<CBPeripheral>()
private var connectedPeripherals = Set<CBPeripheral>()
private var servicesDiscoveredPeripherals = Set<CBPeripheral>()
private var ignoredPeripherals = Set<CBPeripheral>()
private let central: CBCentralManager
private let logger: LoggerProtocol
var rememberedDeviceUUID: UUID?
init(central: CBCentralManager, logger: LoggerProtocol, rememberedDeviceUUID: UUID? = nil)
{
self.central = central
self.logger = logger
self.rememberedDeviceUUID = rememberedDeviceUUID
super.init()
central.delegate = self
}
func ignore(_ uuid: UUID)
{
guard
let peripheral = connectedPeripherals.first(where: { $0.identifier == uuid })
?? servicesDiscoveredPeripherals.first(where: { $0.identifier == uuid })
else {
return
}
ignoredPeripherals.insert(peripheral)
servicesDiscoveredPeripherals.remove(peripheral)
central.cancelPeripheralConnection(peripheral)
}
func services(for uuid: UUID) -> [CBUUID]
{
return servicesDiscoveredPeripherals.first { $0.identifier == uuid }?.services?.map { $0.uuid } ?? []
}
func discoverCharacteristics(for peripheralUuid: UUID, service uuid: CBUUID)
{
guard
let peripheral = servicesDiscoveredPeripherals.first(where: { $0.identifier == peripheralUuid }),
let service = peripheral.service(with: uuid)
else {
return
}
peripheral.discoverCharacteristics(nil, for: service)
}
func write(
to peripheralUuid: UUID,
service serviceUuid: CBUUID,
characteristic characteristicUuid: CBUUID,
value: Data,
type: CBCharacteristicWriteType
){
guard
let peripheral = servicesDiscoveredPeripherals.first(where: { $0.identifier == peripheralUuid }),
let service = peripheral.service(with: serviceUuid),
let characteristic = service.characteristics?.first(where: { $0.uuid == characteristicUuid })
else {
return
}
peripheral.writeValue(value, for: characteristic, type: type)
}
func read(from peripheralUuid: UUID, service serviceUuid: CBUUID, characteristic characteristicUuid: CBUUID)
{
guard
let peripheral = servicesDiscoveredPeripherals.first(where: { $0.identifier == peripheralUuid }),
let service = peripheral.service(with: serviceUuid),
let characteristic = service.characteristics?.first(where: { $0.uuid == characteristicUuid })
else {
return
}
peripheral.readValue(for: characteristic)
}
}
extension BluetoothCentralService: CBCentralManagerDelegate
{
func centralManagerDidUpdateState(_ central: CBCentralManager)
{
switch central.state
{
case .resetting:
break
case .unknown:
onStateChange?(.unknown)
case .unsupported:
onStateChange?(.unsupported)
case .unauthorized:
onStateChange?(.unauthorized)
case .poweredOff:
onStateChange?(.poweredOff)
connectedPeripherals = []
servicesDiscoveredPeripherals = []
ignoredPeripherals = []
onServicesScannedPeripheralsUpdate?([])
case .poweredOn:
scanRemembered()
default:
logger.log(.warning, "\(#function): Unhandled state type.")
}
}
private func scanRemembered()
{
guard
let uuid = rememberedDeviceUUID,
let peripheral = central.retrievePeripherals(withIdentifiers: [uuid]).first
else {
scanConnected(); return
}
connect(peripheral)
}
private func scanConnected()
{
let connected = central.retrieveConnectedPeripherals(withServices: [])
connectedPeripherals.formUnion(connected)
connectedPeripherals.forEach { $0.discoverServices(nil) }
scanNew()
}
private func scanNew()
{
central.scanForPeripherals(withServices: nil)
}
private func connect(_ peripheral: CBPeripheral)
{
guard
!ignoredPeripherals.contains(peripheral),
!connectingPeripherals.contains(peripheral),
!connectedPeripherals.contains(peripheral),
!servicesDiscoveredPeripherals.contains(peripheral)
else {
return
}
connectingPeripherals.insert(peripheral)
peripheral.delegate = self
central.connect(peripheral)
}
func centralManager(
_ central: CBCentralManager,
didDiscover peripheral: CBPeripheral,
advertisementData: [String : Any],
rssi RSSI: NSNumber
){
guard
!ignoredPeripherals.contains(peripheral),
!connectingPeripherals.contains(peripheral),
!connectedPeripherals.contains(peripheral),
!servicesDiscoveredPeripherals.contains(peripheral)
else {
return
}
connect(peripheral)
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral)
{
connectingPeripherals.remove(peripheral)
connectedPeripherals.insert(peripheral)
peripheral.discoverServices(nil)
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?)
{
connectedPeripherals.remove(peripheral)
}
}
extension BluetoothCentralService: CBPeripheralDelegate
{
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?)
{
guard error == nil else { return }
servicesDiscoveredPeripherals.insert(peripheral)
connectedPeripherals.remove(peripheral)
onServicesScannedPeripheralsUpdate?(servicesDiscoveredPeripherals.map { $0.identifier })
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?)
{
onCharacteristicsDiscovered?(
peripheral.identifier,
service.uuid,
service.characteristics?.compactMap { $0.uuid } ?? []
)
}
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?)
{
onCharacteristicWriteFinished?(peripheral.identifier, characteristic.uuid, error == nil)
}
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?)
{
onCharacteristicReadFinished?(peripheral.identifier, characteristic.uuid, characteristic.value)
}
func peripheral(_ peripheral: CBPeripheral, didModifyServices invalidatedServices: [CBService])
{
// This never gets called if it's actually implemented here!
}
}