iOS之CoreBluetooth

  • 思路

    手機與設備間的通訊方式CoreBluetooth是比較常見且通用的。在iOS開發中需明晰以下幾點

    1. 藍牙4.0最多可聯機7個設備,iPhone6以上都是藍牙4.0
    2. 兩台iPhone並不直接通過藍牙互相搜索配對
    3. 蘋果設備不支援和其他非蘋果設備連接藍牙,當然,除了藍牙耳機和車載藍牙之外
    4. 藍牙傳輸的位元組順序是小端
    5. CoreBluetooth的最大傳輸單元是20個位元組

    知識科普:

    位元組順序只是對內置數據類型而言

    例如對於一整型(int,int 是內置數據類型)數,比如 0x123456
    大端模式:
    高地址---------->低地址 
    0x56 | 0x34 | 0x12
    小端模式:
    高地址 ---------->低地址
    0x12 | 0x34 | 0x56 
    
  • 流程

    以中心設備為例分析整個流程

    1.實例化中心設備管理者

    cManager = CBCentralManager(delegate: self, queue: nil)
    

    2.監測狀態為PowOn,並搜索附近設備

    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        if central.state == .poweredOn {
            scanForServices()
        } else {
            print("\(central.state)")
        }
    }
    

    3.發現外設,保存並展示

    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
        if let name = peripheral.name {
            if !list.contains(name) {
                list.append(name)
                dict[name] = peripheral
                table.reloadData()
            }
        }
    }
    

    4.根據需要選擇連接的外設

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        // 連接設備
        if let pheral = dict[list[indexPath.row]] {
            cManager?.connect(pheral, options: nil)
        }
    }
    

    5.連接外設,失敗/成功,成功則掃描外設服務

    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
        print("連接外設失敗")
    }
    
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
        // 停止掃描
        central.stopScan()
        print("連接外設成功")
        
        peripheral.delegate = self
        self.peripheral = peripheral
        
        // 掃描外設的服務
        peripheral.discoverServices([CBUUID(string: "列印")])
    }
    

    6.回調中發現服務

    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
        var findService: CBService?
        for item in peripheral.services ?? [] {
            print(item.uuid)
            // 如果服務調用在service中
            if item.uuid.isEqual(CBUUID(string: "列印")) {
                findService = item
            }
        }
        if let findService = findService  {
            peripheral.discoverCharacteristics(nil, for: findService)
        }
    }
    

    7.查詢服務下面的特徵,回調中返回 then發送列印數據

    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
        for item in service.characteristics ?? [] {
            if item.uuid.isEqual(CBUUID(string: "列印")) {
                // 讀取成功回調didUpdateValueForCharacteristic
                self.characteristic = item
                
                // 接收一次(是讀一次資訊還是數據經常變實時接收視情況而定, 再決定使用哪個)
                peripheral.readValue(for: item)
                
                // 訂閱、實時接收
                peripheral.setNotifyValue(true, for: item)
                
                // 發送下行指令【發送一條】
                guard let data = "硬體工程師給我的指令, 發送給藍牙該指令, 藍牙會給我返回一條數據".data(using: .utf8) else { return }
                self.peripheral?.writeValue(data, for: item, type: .withResponse)
            }
            
            // 當發現characteristic有descriptor,回調didDiscoverDescriptorsForCharacteristic
            peripheral.discoverDescriptors(for: item)
        }
    }
    

    8.從外圍設備讀取數據

    func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
        //characteristic.value就是藍牙傳遞過來的值
        print("\(String(describing: characteristic.value))")
    }
    

    9.中心設備讀取外設實時數據

    func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
        if characteristic.isNotifying {
            peripheral.readValue(for: characteristic)
        } else {
            print("notification stoped on \(characteristic)")
            self.cManager?.cancelPeripheralConnection(peripheral)
        }
    }
    
  • 結束