iOS Core Bluetooth не может передавать данные через характеристики - PullRequest
0 голосов
/ 06 декабря 2018

Я создаю iOS-приложение, которое на данный момент должно передавать текстовые сообщения между двумя iOS-устройствами.Я хочу / должен использовать Core Bluetooth для этого.Одно устройство становится периферийным, а другое центральным.

Настройка периферийного устройства с одним сервисом, содержащим две характеристики (1 чтение, 1 запись), не является проблемой.Я использовал приложение LightBlue для подключения к периферийному устройству, и я также вижу его характеристики.

Когда я использую центральное устройство роли, я нахожу периферийное устройство, могу подключиться и также вижу его характеристики.Я сохраняю периферию и характеристику как в собственных переменных, так и позже использую их для вызова writeValue (_ data: Data, для характеристики: CBCharacteristic, тип: CBCharacteristicWriteType) хранимого периферийного устройства, которое не вызывает ошибок.Но периферийное устройство, похоже, не получает этого сообщения, потому что не вызывается ни один из его методов didWriteValueFor или didUpdateValueFor .Центральная также не может подписаться на характеристики.

Кроме того, когда я использую периферийное устройство для обновления значения своей собственной характеристики, моя проверка с приложением LightBlue не проходит успешно, поскольку значение всегда остается пустым, а центральное не 'Конечно, я тоже получаю уведомления.

Похоже, что все, что выходит из приложения, не работает.Кажется, работают только входящие данные (например, из приложения LightBlue).

У кого-нибудь когда-нибудь была подобная проблема?Я использовал много разных учебных пособий и делал в точности то, что описано там (потому что это была почти одинаковая процедура везде).

Заранее спасибо!


Я использую XCode Version 10.1, кодв Swift и используйте для тестирования iPhone XS Max (iOS 12.1) и iPad Air 2 (iOS 12.0).


Вот мой периферийный класс:

import CoreBluetooth
import UIKit

class BLEPeripheralManager: NSObject {

    //MARK:- Properties

    static let shared = BLEPeripheralManager()

    //just some delegates for other classes
    var peripheralDataReceiver: PeripheralDataReceiver?
    var peripheralChatDataReceiver: PeripheralChatDataReceiver?

    var peripheralManager: CBPeripheralManager

    var subscribedCentrals: [CBCentral] = []

    var readCharacteristic: CBMutableCharacteristic = {
        let uuidStringChar1 = UUIDs.characteristicUUID1
        let uuidChar1 = CBUUID(string: uuidStringChar1)
        let char = CBMutableCharacteristic(type: uuidChar1, properties: .read, value: nil, permissions: .readable)
        return char
    }()
    var writeCharacteristic: CBMutableCharacteristic = {
        let uuidStringChar2 = UUIDs.characteristicUUID2
        let uuidChar2 = CBUUID(string: uuidStringChar2)
        let char = CBMutableCharacteristic(type: uuidChar2, properties: .write, value: nil, permissions: .writeable)
        return char
    }()


    //MARK:- Private Methods

    private override init() {
        self.peripheralManager = CBPeripheralManager(delegate: nil, queue: nil)
        super.init()
        self.peripheralManager.delegate = self
    }

    private func setupManager() {
        let uuidStringServ = UUIDs.serviceUUID
        let uuidServ = CBUUID(string: uuidStringServ)

        let transferService = CBMutableService(type: uuidServ, primary: true)
        transferService.characteristics = [self.readCharacteristic, self.writeCharacteristic]

        self.peripheralManager.add(transferService)
    }

    private func teardownServices() {
        self.peripheralManager.removeAllServices()
    }

    private func clearSubscribers() {
        self.subscribedCentrals.removeAll()
    }

    //MARK:- Public Methods

    public func sendMessage(fromPeripheral peripheral: String, text: String) {
        if text.isEmpty { return }
        let chatMessage = ChatMsg(messageText: text, fromDevice: peripheral)
        let encoder = JSONEncoder()
        do {
            let data = try encoder.encode(chatMessage)
            print(self.readCharacteristic.uuid)
            if self.peripheralManager.updateValue(data, for: self.readCharacteristic, onSubscribedCentrals: nil) == false {
                print("Update from Peripheral failed (ReadCharacteristic)")
            } else {
                print("Message sent (ReadCharacteristic)")
            }
            if self.peripheralManager.updateValue(data, for: self.writeCharacteristic, onSubscribedCentrals: nil) == false {
                print("Update from Peripheral failed (WriteCharacteristic)")
            } else {
                print("Message sent (WriteCharacteristic)")
            }
        } catch {
            print("Error in encoding data")
        }
    }

    func startAdvertising() {
        let services = [CBUUID(string: UUIDs.serviceUUID)]
        let advertisingDict = [CBAdvertisementDataServiceUUIDsKey: services]

        self.peripheralManager.startAdvertising(advertisingDict)
    }

    public func stopAdvertising() {
        self.peripheralManager.stopAdvertising()
    }

    public func checkIfAdvertising() -> Bool {
        return self.peripheralManager.isAdvertising
    }
}


extension BLEPeripheralManager: CBPeripheralManagerDelegate {

    func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
        switch peripheral.state {
        case .poweredOff:
            print("Peripheral powered off")
            self.teardownServices()
            self.clearSubscribers()
        case .poweredOn:
            print("Peripheral powered on")
            self.setupManager()
        case .resetting:
            print("Peripheral resetting")
        case .unauthorized:
            print("Unauthorized Peripheral")
        case .unknown:
            print("Unknown Peripheral")
        case .unsupported:
            print("Unsupported Peripheral")
        }
    }

    //doesn`t get called
    func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveWrite requests: [CBATTRequest]) {
        for request in requests {
            if let value = request.value {
                if let messageText = String(data: value, encoding: String.Encoding.utf8) {
                    //
                }
            }
        }
    }

    //doesn`t get called
    func peripheralManager(_ peripheral: CBPeripheralManager, central: CBCentral, didSubscribeTo characteristic: CBCharacteristic) {
        var shouldAdd: Bool = true
        for sub in self.subscribedCentrals {
            if sub == central {
                shouldAdd = false
            }
        }
        if shouldAdd { self.subscribedCentrals.append(central) }
    }

}

Вотмой центральный класс:

import CoreBluetooth
import UIKit


class BLECentralManager: NSObject {

    static let shared = BLECentralManager()

    //just some delegates for other classes
    var centralDataReceiver: CentralDataReceiver?
    var centralChatDataReceiver: CentralChatDataReceiver?

    var centralManager: CBCentralManager

    var peripheralArray: [CBPeripheral] = []

    var writeTransferPeripheral: (peripheral: CBPeripheral, characteristic: CBCharacteristic)?
    var readTransferPeripheral: (peripheral: CBPeripheral, characteristic: CBCharacteristic)?

    private override init() {
        self.centralManager = CBCentralManager(delegate: nil, queue: nil, options: nil)
        super.init()
        self.centralManager.delegate = self
    }

    private func startScan() {
        self.centralManager.scanForPeripherals(withServices: [CBUUID(string: UUIDs.serviceUUID)], options: nil)
        //self.centralManager.scanForPeripherals(withServices: nil, options: nil)
    }

    public func connectTo(index: Int) {
        self.centralManager.connect(self.peripheralArray[index], options: nil)
    }

    public func sendMessage(fromPeripheral peripheral: String, text: String) {
        let chatMsg = ChatMsg(messageText: text, fromDevice: peripheral)
        let encoder = JSONEncoder()

        do {
            let data = try encoder.encode(chatMsg)
            self.writeTransferPeripheral?.peripheral.writeValue(data, for: (self.writeTransferPeripheral?.characteristic)!, type: .withoutResponse)
        } catch {
            print("Error in encoding data")
        }
    }

    public func getActiveConnections() -> String {
        var connString: String = ""
        let conns = self.centralManager.retrieveConnectedPeripherals(withServices: [CBUUID(string: UUIDs.serviceUUID)])
        for peri in conns {
            if connString == "" {
                connString = "\(peri)"
            } else {
                connString = "\(connString), \(peri)"
            }
        }
        return connString
    }

    public func getMessages() {
        self.readTransferPeripheral?.peripheral.readValue(for: (self.readTransferPeripheral?.characteristic)!)
    }

    public func lookForPeripherals() {
        self.peripheralArray.removeAll()
        self.startScan()
    }

    public func getPeripherals() -> [CBPeripheral] {
        return self.peripheralArray
    }

    private func getNameOfPeripheral(peripheral: CBPeripheral) -> String {
        if let name = peripheral.name {
            return name
        } else {
            return "Device"
        }
    }
}


extension BLECentralManager: CBCentralManagerDelegate, CBPeripheralDelegate {
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        switch central.state {
        case .poweredOff:
            print("BLE has powered off")
            centralManager.stopScan()
        case .poweredOn:
            print("BLE is now powered on")
            self.startScan()
        case .resetting:
            print("BLE is resetting")
        case .unauthorized:
            print("Unauthorized BLE state")
        case .unknown:
            print("Unknown BLE state")
        case .unsupported:
            print("This platform does not support BLE")
        }
    }

    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
        self.peripheralArray.append(peripheral)
    }

    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
        self.centralDataReceiver?.connectionEstablished(peripheral: peripheral)
        print("Connection Established")
        peripheral.delegate = self
        peripheral.discoverServices(nil)
    }

    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
        self.centralDataReceiver?.connectionTornDown()
    }

    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
        for service in peripheral.services! {
            peripheral.discoverCharacteristics(nil, for: service)
        }
    }

    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
        for characteristic in service.characteristics! {
            print(characteristic.uuid)
            let char = characteristic as CBCharacteristic
            if char.uuid.uuidString == UUIDs.characteristicUUID2 {
                self.writeTransferPeripheral?.peripheral = peripheral
                self.writeTransferPeripheral?.characteristic = char
                self.writeTransferPeripheral?.peripheral.setNotifyValue(true, for: (self.writeTransferPeripheral?.characteristic)!)
            } else if char.uuid.uuidString == UUIDs.characteristicUUID1 {
                self.readTransferPeripheral?.peripheral = peripheral
                self.readTransferPeripheral?.characteristic = char
                self.readTransferPeripheral?.peripheral.setNotifyValue(true, for: (self.readTransferPeripheral?.characteristic)!)
            }
        }
    }

    //doesn`t get called
    func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
        if let data = characteristic.value {
            if data.isEmpty { return }
            if let text = String(data: characteristic.value!, encoding: String.Encoding.utf8) {
                self.centralChatDataReceiver?.receiveMessage(fromPeripheral: self.getNameOfPeripheral(peripheral: peripheral), text: text)
            }
        }
    }

    //doesn`t get called
    func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
        if let data = characteristic.value {
            if data.isEmpty { return }
            if let text = String(data: characteristic.value!, encoding: String.Encoding.utf8) {
                self.centralChatDataReceiver?.receiveMessage(fromPeripheral: self.getNameOfPeripheral(peripheral: peripheral), text: text)
            }
        }
    }

    //doesn`t get called
    func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
        print("Notification state changed")
    }

}

1 Ответ

0 голосов
/ 11 декабря 2018

Я выяснил, что не так на центральной стороне:

После обнаружения я сохраняю периферию и ее характеристики в двух переменных / кортежах (writeTransferPeripheral и readTransferPeripheral).Я сделал это неправильно:

self.writeTransferPeripheral?.peripheral = peripheral
self.writeTransferPeripheral?.characteristic = char

Таким образом, переменная все еще была нулевой, когда позже использовала ее для записи значения.Похоже, вы должны установить значение кортежа следующим образом:

self.writeTransferPeripheral = (peripheral, char)
...