簡體   English   中英

每次我在 Swift 中使用它時都必須啟動一個 BLE 外圍設備

[英]have to initiate a BLE peripheral every time i use it in Swift

使用 CBCentralManager 我成功掃描多個 BLE UART 設備並連接到它們,de 設備存儲在數組 periphirals[] 中,但是當我想一個一個地向它們發送數據時,只有最后一個連接的我可以成功發送數據到,我在再次寫入數據之前通過調用 blePeripheral?.discoverServices([ParticlePeripheral.BLEService_UUID]) 解決了這個問題,但我認為這不是正確的解決方案,有人可以解釋我做錯了什么嗎?

在我使用的代碼下面,掃描和連接由 didload 和 startCyclus 啟動第一個設備,在輸入接收到的數據“didUpdateValueFor”后,獲取數據並直接發送數據到下一個外設

import Foundation
import UIKit
import CoreBluetooth

var txCharacteristic : CBCharacteristic?
var rxCharacteristic : CBCharacteristic?
var blePeripheral : CBPeripheral?
var characteristicASCIIValue = NSString()
var Running = false
var currentNode = 0;
var updateService = false
var maxNodes = Int()

class ViewController : UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate, UITableViewDelegate{

    //Data
    var centralManager : CBCentralManager!
    var RSSIs = [NSNumber]()
    var data = NSMutableData()
    var peripherals: [CBPeripheral] = []
    var characteristicValue = [CBUUID: NSData]()
    var timer = Timer()
    var characteristics = [String : CBCharacteristic]()
    var teller = 0

    //UI
    @IBOutlet weak var baseTableView: UITableView!
    @IBOutlet weak var refreshButton: UIBarButtonItem!

   @IBAction func naarRun(_ sender: Any) {
             print("naarRun")
        self.performSegue(withIdentifier:"naarRunView" , sender: self)
    }


    override func viewDidLoad() {
        super.viewDidLoad()
        centralManager = CBCentralManager(delegate: self, queue: nil)
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?)
    {
        if (segue.identifier == "naarRunView") {
            let vc = segue.destination as! ViewRun
            vc.peripherals = peripherals
        }
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        //print("View Cleared")
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        print("Stop Scanning")
        centralManager?.stopScan()
    }

    /*Okay, now that we have our CBCentalManager up and running, it's time to start searching for devices. You can do this by calling the "scanForPeripherals" method.*/

    func startScan() {
        peripherals = []
        print("Now Scanning...")
        self.timer.invalidate()
        centralManager?.scanForPeripherals(withServices: [ParticlePeripheral.BLEService_UUID] , options: [CBCentralManagerScanOptionAllowDuplicatesKey:false])
        Timer.scheduledTimer(withTimeInterval: 3, repeats: false) {_ in
            self.cancelScan()
            self.connectToAllDevices()
        }
    }

    /*We also need to stop scanning at some point so we'll also create a function that calls "stopScan"*/
    func cancelScan() {
        self.centralManager?.stopScan()
        print("Scan Stopped")
        print("Number of Peripherals Found: \(peripherals.count)")
    }

    func disconnectFromDevice () {
        if blePeripheral != nil {
            // We have a connection to the device but we are not subscribed to the Transfer Characteristic for some reason.
            // Therefore, we will just disconnect from the peripheral
            centralManager?.cancelPeripheralConnection(blePeripheral!)
        }
    }


    func restoreCentralManager() {
        //Restores Central Manager delegate if something went wrong
        centralManager?.delegate = self
    }

    /*
     Called when the central manager discovers a peripheral while scanning. Also, once peripheral is connected, cancel scanning.
     */
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral,advertisementData: [String : Any], rssi RSSI: NSNumber) {

        blePeripheral = peripheral
        teller+=1
        self.peripherals.append(peripheral)
        self.RSSIs.append(RSSI)
        peripheral.delegate = self
        //self.baseTableView.reloadData()
        //if blePeripheral == nil {
            print("Found new pheripheral devices with services")
            print("Peripheral name: \(String(describing: peripheral.name))")
            print("**********************************")
            print ("Advertisement Data : \(advertisementData)")

        //}
    }

    //Peripheral Connections: Connecting, Connected, Disconnected

    //-Connection
    func connectToDevice () {
        centralManager?.connect(blePeripheral!, options: nil)
    }

    func connectToAllDevices(){
        var seconds = 0.0
        for per in peripherals
        {
            DispatchQueue.main.asyncAfter(deadline: .now() + seconds) {
                blePeripheral = per
                print("Connecting naar " + (blePeripheral?.name!)!)
                self.connectToDevice ()
            }
            seconds = seconds + 0.5;
        }
    }

    /*
     Invoked when a connection is successfully created with a peripheral.
     This method is invoked when a call to connect(_:options:) is successful. You typically implement this method to set the peripheral’s delegate and to discover its services.
     */
    //-Connected
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
        print("*****************************")
        print("Connection complete")
        print("Peripheral info: \(String(describing: blePeripheral))")

         //Erase data that we might have
        data.length = 0

        //Discovery callback
        peripheral.delegate = self
        //Only look for services that matches transmit uuid
        peripheral.discoverServices([ParticlePeripheral.BLEService_UUID])


       /*
        //Once connected, move to new view controller to manager incoming and outgoing data
        let storyboard = UIStoryboard(name: "Main", bundle: nil)

        let uartViewController = storyboard.instantiateViewController(withIdentifier: "UartModuleViewController") as! UartModuleViewController

        uartViewController.peripheral = peripheral

        navigationController?.pushViewController(uartViewController, animated: true)
 */
    }

    /*
     Invoked when the central manager fails to create a connection with a peripheral.
     */

    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
        if error != nil {
            print("Failed to connect to peripheral")
            return
        }
    }

    func disconnectAllConnection() {
        centralManager.cancelPeripheralConnection(blePeripheral!)
    }

    /*
     Invoked when you discover the peripheral’s available services.
     This method is invoked when your app calls the discoverServices(_:) method. If the services of the peripheral are successfully discovered, you can access them through the peripheral’s services property. If successful, the error parameter is nil. If unsuccessful, the error parameter returns the cause of the failure.
     */
    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
        print("*******************************************************")

        if ((error) != nil) {
            print("Error discovering services: \(error!.localizedDescription)")
            return
        }

        guard let services = peripheral.services else {
            return
        }

        //We need to discover the all characteristic
        for service in services {

            peripheral.discoverCharacteristics(nil, for: service)
            // bleService = service
        }
        print("Discovered Services: \(services)")

    }

    /*
     Invoked when you discover the characteristics of a specified service.
     This method is invoked when your app calls the discoverCharacteristics(_:for:) method. If the characteristics of the specified service are successfully discovered, you can access them through the service's characteristics property. If successful, the error parameter is nil. If unsuccessful, the error parameter returns the cause of the failure.
     */

    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {

        print("*******************************************************")

        if ((error) != nil) {
            print("Error discovering services: \(error!.localizedDescription)")
            return
        }

        guard let characteristics = service.characteristics else {
            return
        }

        print("Found \(characteristics.count) characteristics!")

        for characteristic in characteristics {
            //looks for the right characteristic

            if characteristic.uuid.isEqual(ParticlePeripheral.BLE_Characteristic_uuid_Rx)  {
                rxCharacteristic = characteristic

                //Once found, subscribe to the this particular characteristic...
                peripheral.setNotifyValue(true, for: rxCharacteristic!)
                // We can return after calling CBPeripheral.setNotifyValue because CBPeripheralDelegate's
                // didUpdateNotificationStateForCharacteristic method will be called automatically
                peripheral.readValue(for: characteristic)
                print("Rx Characteristic: \(characteristic.uuid)")
            }
            if characteristic.uuid.isEqual(ParticlePeripheral.BLE_Characteristic_uuid_Tx){
                txCharacteristic = characteristic
                print("Tx Characteristic: \(characteristic.uuid)")
            }
            peripheral.discoverDescriptors(for: characteristic)
        }

    }




    func peripheral(_ peripheral: CBPeripheral, didDiscoverDescriptorsFor characteristic: CBCharacteristic, error: Error?) {
        print("*******************************************************")

        if error != nil {
            print("\(error.debugDescription)")
            return
        }
        guard let descriptors = characteristic.descriptors else { return }

        descriptors.forEach { descript in
            print("function name: DidDiscoverDescriptorForChar \(String(describing: descript.description))")
            print("Rx Value \(String(describing: rxCharacteristic?.value))")
            print("Tx Value \(String(describing: txCharacteristic?.value))")

        }

    }


    func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
        print("*******************************************************")

        if (error != nil) {
            print("Error changing notification state:\(String(describing: error?.localizedDescription))")

        } else {
            print("Characteristic's value subscribed")
        }

        if (characteristic.isNotifying) {
            print ("Subscribed. Notification has begun for: \(characteristic.uuid)")
        }
    }



    func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
        print("Disconnected")
    }


    func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
        guard error == nil else {
            print("Error discovering services: error")
            return
        }
        print("Message sent")
    }

    func peripheral(_ peripheral: CBPeripheral, didWriteValueFor descriptor: CBDescriptor, error: Error?) {
        guard error == nil else {
            print("Error discovering services: error")
            return
        }
        print("Succeeded!")
    }

    // MARK: - Getting Values From Characteristic
    /** After you've found a characteristic of a service that you are interested in, you can read the characteristic's value by calling the peripheral "readValueForCharacteristic" method within the "didDiscoverCharacteristicsFor service" delegate.
     */
    func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
        guard characteristic == rxCharacteristic,
            let characteristicValue = characteristic.value,
            let ASCIIstring = NSString(data: characteristicValue,
                                       encoding: String.Encoding.utf8.rawValue)
            else { return }

        characteristicASCIIValue = ASCIIstring
        let ontvangen = String(characteristicASCIIValue)
        let waarde = haalwaarde(recieved: ontvangen)
        print("Value Recieved: " + String(characteristicASCIIValue) )
        NotificationCenter.default.post(name:NSNotification.Name(rawValue: "Notify"), object: self)
        if(updateService){updateService=false; return}
        if(!waarde.isNumber){print("waarde is no number");return}

        if(Running ){Vervolg()}

    }


    func haalNode(recieved:String)->String{
        //nodenr
        if(recieved.count<10){return "fout";}
        let temp = recieved
        let lengte = recieved.count;
        let start = temp.index(temp.startIndex, offsetBy: 7)
        let end = temp.index(temp.endIndex, offsetBy: -(lengte-8))
        let range = start..<end
        let nodeNr = String(temp[range])
        //print("nodeNr=", nodeNr)
        return nodeNr
    }
    func haalwaarde(recieved:String)->String{
        //Reactietijd
        if(recieved.count<10){return "fout"};
        let temp = recieved
        let lengte = recieved.count;
        let start = temp.index(temp.startIndex, offsetBy: 1)
        let end = temp.index(temp.endIndex, offsetBy: -(lengte-7))
        let range = start..<end
        let Reactietijd = String(temp[range])
        //print("\nreactietijd=", Reactietijd)
        return Reactietijd
    }

    func startCyclus(){
        currentNode = 0
        if maxNodes == 0 {return}
        Running = true
        blePeripheral = peripherals[0]
        updateService = true
        blePeripheral?.discoverServices([ParticlePeripheral.BLEService_UUID])
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
            self.writeValue(data: "N  5000G55")
            currentNode += 1
        }

    func Vervolg(){
        print("vervolg")
        if currentNode == maxNodes {Running = false;return}
        blePeripheral = peripherals[currentNode]
        updateService = true
        blePeripheral?.discoverServices([ParticlePeripheral.BLEService_UUID])
        DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(10)) {
            self.writeValue(data: "N  5000G55")
            currentNode += 1
        }
    }


    // Write functions
    func writeValue(data: String){
        print("schrijfdata  " + String(currentNode))
        let valueString = (data as NSString).data(using: String.Encoding.utf8.rawValue)
        //change the "data" to valueString
        if let blePer = blePeripheral{
            if let txCharacteristic = txCharacteristic {
                blePer.writeValue(valueString!, for: txCharacteristic, type: CBCharacteristicWriteType.withoutResponse)
            }
        }
    }




    /*
     Invoked when the central manager’s state is updated.
     This is where we kick off the scan if Bluetooth is turned on.
     */
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        if central.state == CBManagerState.poweredOn {
            // We will just handle it the easy way here: if Bluetooth is on, proceed...start scan!
            print("Bluetooth Enabled")
            startScan()

        } else {
            //If Bluetooth is off, display a UI alert message saying "Bluetooth is not enable" and "Make sure that your bluetooth is turned on"
            print("Bluetooth Disabled- Make sure your Bluetooth is turned on")

            let alertVC = UIAlertController(title: "Bluetooth is not enabled", message: "Make sure that your bluetooth is turned on", preferredStyle: UIAlertControllerStyle.alert)
            let action = UIAlertAction(title: "ok", style: UIAlertActionStyle.default, handler: { (action: UIAlertAction) -> Void in
                self.dismiss(animated: true, completion: nil)
            })
            alertVC.addAction(action)
            self.present(alertVC, animated: true, completion: nil)
        }
    }
}

extension String {
    var isNumber: Bool {
        let characters = CharacterSet.decimalDigits.inverted
        return !self.isEmpty && rangeOfCharacter(from: characters) == nil
    }
}

我認為這是您的日志記錄隱藏了真正發生的事情。

您在每次連接之前重新分配blePeripheral ,但如果其中任何一個花費的時間超過 0.5 秒,則在調用didConnect時,已連接的設備將不再是blePeripheral引用的設備。

我建議完全刪除blePeripheral ,並將CBPeripheral object 顯式傳遞給connectToDevice 此外,在didConnect中,使用傳入的peripheral參數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM