[iOS/swift]藍牙鏈接

以前作過藍牙鏈接的功能,可是要麼是直接在別人的基礎上更改和新增功能,要麼就是囫圇吞棗直接按別人的文章一步步寫過去,寫完了本身也仍是沒有頭緒,僅僅是能用而已。此次借設計YModem升級工具及藍牙多設備同時升級功能的機會,從頭梳理一遍藍牙鏈接的功能,並記之以文字。

1、原理

更詳細信息能夠查看官方藍牙框架文檔: 《Core Bluetooth Programming Guide》

文章連接:藍牙鏈接(swift版)html

1.基本概念

1.1 藍牙版本

藍牙2.0

藍牙2.0是傳統藍牙,也叫作經典藍牙。git

藍牙2.0如要上架需進行MFI認證,使用ExternalAccessory框架。github

藍牙4.0

藍牙4.0由於耗電低,也叫作低功耗藍牙(BLE)。它將三種規格集於一體,包括傳統藍牙技術、高速技術和低耗能技術。swift

藍牙4.0使用CoreBluetooth框架。數組

1.2 參數

基本參數

  • CBCentralManager:中心設備,鏈接硬件的設備。
  • CBPeripheral:外圍設備,被鏈接的硬件。
  • service:服務
  • characteristic:特徵

一個外設包含多個服務,而每個服務中又包含多個特徵,特徵包括特徵的值和特徵的描述.每一個服務包含多個字段,字段的權限有read(讀)、write(寫)、notify(通知/訂閱)。閉包

characteristic的三種操做:

  • write:發送信息給外圍設備
  • notify:接收外圍設備的通知
  • read:獲取外圍設備的信息。當部分設備爲只讀時,沒法使用write發送信息給外圍設備,但可使用read去獲取外圍設備的信息。

正常多使用write和notify。併發

2.模式

藍牙開發分爲兩種模式,中心模式(central),和外設模式(peripheral)。通常來說,咱們須要在軟件內鏈接硬件,經過鏈接硬件給硬件發送指令以完成一些動做的藍牙開發都是基於中心模式(central)模式的開發,也就是說咱們開發的app是中心,咱們要鏈接的硬件是外設。若是須要其餘設備鏈接手機藍牙,並對手機進行一些操做,那就是基於外設模式(peripheral)的開發。app

中心模式流程

swift版:框架

  1. 建立中心設備(CBCentralManager)
  2. 中心設備開始掃描(scanForPeripherals)
  3. 掃描到外圍設備以後, 自動調用中心設備的代理方法(didDiscoverPeripheral)
  4. 若是設備過多, 能夠將掃描到的外圍設備添加到數組
  5. 開始鏈接, 從數組中過濾出本身想要的設備, 進行鏈接(connectPeripheral)
  6. 鏈接上以後, 自動調用中心設備的代理方法(didConnectPeripheral), 在代理中, 進行查找外圍設備的服務(peripheral.discoverServices)
  7. 查找到服務以後, 自動調用外圍設備的代理(didDiscoverServices), 可經過UUID,查找具體的服務,查找服務(discoverCharacteristics)
  8. 查找到特徵以後, 自動調用外圍設備的代理(didDiscoverCharacteristics), 經過UUID找到本身想要的特徵, 讀取特徵(readValueForCharacteristic)
  9. 讀取到特徵以後, 自動調用外設的代理方法(didUpdateValueForCharacteristic),在這裏打印或者解析本身想要的特徵值.

oc版:ide

  1. 創建中心角色 [[CBCentralManager alloc] initWithDelegate:self queue:nil]
  2. 掃描外設 cancelPeripheralConnection
  3. 發現外設 didDiscoverPeripheral
  4. 鏈接外設 connectPeripheral

    • 4.1鏈接失敗 didFailToConnectPeripheral
    • 4.2鏈接斷開 didDisconnectPeripheral
    • 4.3鏈接成功 didConnectPeripheral
  5. 掃描外設中的服務 discoverServices

    • 5.1發現並獲取外設中的服務 didDiscoverServices
  6. 掃描外設對應服務的特徵 discoverCharacteristics

    • 6.1發現並獲取外設對應服務的特徵 didDiscoverCharacteristicsForService
    • 6.2給對應特徵寫數據 writeValue:forCharacteristic:type:
  7. 訂閱特徵的通知 setNotifyValue:forCharacteristic:

    • 7.1根據特徵讀取數據 didUpdateValueForCharacteristic

外設模式流程

  1. 創建外設設備
  2. 設置本地外設的服務和特徵
  3. 發佈外設和特徵
  4. 廣播服務
  5. 響應中心的讀寫請求
  6. 發送更新的特徵值,訂閱中心


2、實現

經常使用方法記錄用到的方法, 代碼是具體運用

2.1 前置

  • 設備的特徵包含UUID,可經過UUID區分對應的特徵。
  • 要用到藍牙鏈接相關類的頁面需導入頭文件:import CoreBluetooth

變量

private let BLE_WRITE_UUID = "xxxx"
private let BLE_NOTIFY_UUID = "xxxx"

var centralManager:CBCentralManager?
///掃描到的全部設備
var aPeArray:[CBPeripheral] = []
//當前鏈接的設備
var pe:CBPeripheral?
var writeCh: CBCharacteristic?
var notifyCh: CBCharacteristic?

經常使用方法

實例化

var centralManager = CBCentralManager(delegate: self, queue: nil, options: nil)

開始掃描

centralManager.scanForPeripherals(withServices: serviceUUIDS, options: nil)

鏈接設備

centralManager.connect(peripheral, options: nil)
  • 鏈接設備以前要先設置代理,正常狀況,當第一次獲取外設peripheral的時候就會同時設置代理

斷開鏈接

centralManager.cancelPeripheralConnection(peripheral)

中止掃描

centralManager.stopScan()

發現服務

peripheral.discoverServices(nil)
  • 外設鏈接成功的時候使用該方法發現服務
  • 通常在此處同步設置代理:peripheral.delegate = self

發現特徵

peripheral.discoverCharacteristics(nil, for: service)
  • 搜索到服務以後執行該方法,將特徵下的服務peripheral.services對應搜索特徵

設置通知

peripheral.setNotifyValue(true, for: characteristic)
  • 當搜索到對應特徵以後,能夠根據條件判斷是否設置及保存該特徵,如特徵的uuid
  • 通知的特徵須要設置該項,以開啓通知

2.2 代碼

2.2.1 流程

實例化

將CBCenteralManager實例化:

centralManager = CBCentralManager(delegate: self, queue: nil, options: [CBCentralManagerOptionShowPowerAlertKey:false])

實例化完成後的回調:

func centralManagerDidUpdateState(_ central: CBCentralManager) {
    if central.state == CBManagerState.poweredOn {
            print("powered on")
            
        } else {
            if central.state == CBManagerState.poweredOff {
                print("BLE powered off")
            } else if central.state == CBManagerState.unauthorized {
                print("BLE unauthorized")
            } else if central.state == CBManagerState.unknown {
                print("BLE unknown")
            } else if central.state == CBManagerState.resetting {
                print("BLE ressetting")
            }
        }
}
  • CBCenteralManager實例化完畢,能夠開始掃描外設CBPeripheral
  • 如無特殊要求,能夠在此處直接進行掃描操做

掃描並發現設備

掃描設備:

centralManager.scanForPeripherals(withServices: serviceUUIDS, options: nil)

發現設備:

func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
    guard !aPeArray.contains(peripheral), let deviceName = peripheral.name, deviceName.count > 0 else {
        return
    }
    
}
  • 此處能夠將搜索到的設備保存並顯示出來,也能夠將須要的設備直接鏈接

鏈接設備

鏈接設備:

centralManager.connect(peripheral, options: nil)

設備鏈接完成後的回調:

func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
    print("\(#function)鏈接外設成功。\ncentral:\(central),peripheral:\(peripheral)\n")
    // 設置代理
    peripheral.delegate = self
    // 開始發現服務
    peripheral.discoverServices(nil)
}
  • 鏈接完成後直接搜索對應的服務Service

設備鏈接失敗的回調:

func centralManager(_ central: CBCentralManager, didFailToConnect peripheral:
CBPeripheral, error: Error?) {
    print("\(#function)鏈接外設失敗\n\(String(describing:
peripheral.name))鏈接失敗:\(String(describing: error))\n")
    // 這裏能夠發通知出去告訴設備鏈接界面鏈接失敗
    
}

鏈接丟失的回調:

func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
    print("\(#function)鏈接丟失\n外設:\(String(describing:
peripheral.name))\n錯誤:\(String(describing: error))\n")
    // 這裏能夠發通知出去告訴設備鏈接界面鏈接丟失
    
}

搜索服務

搜索服務(設備鏈接成功的回調處執行):

peripheral.discoverServices(nil)

發現服務:

func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
    if let error = error  {
        print("\(#function)搜索到服務-出錯\n設備(peripheral):\(String(describing:
peripheral.name)) 搜索服務(Services)失敗:\(error)\n")
        return
    } else {
        print("\(#function)搜索到服務\n設備(peripheral):\(String(describing:
peripheral.name))\n")
    }
    for service in peripheral.services ?? [] {
        peripheral.discoverCharacteristics(nil, for: service)
    }
}
  • 搜索到服務以後直接搜索其對應的特徵

搜索特徵

搜索特徵(當發現服務以後即執行搜索):

peripheral.discoverCharacteristics(nil, for: service)

發現特徵

func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
    if let _ = error {
        print("\(#function)發現特徵\n設備(peripheral):\(String(describing: peripheral.name))\n服務(service):\(String(describing: service))\n掃描特徵(Characteristics)失敗:\(String(describing: error))\n")
        return
    } else {
        print("\(#function)發現特徵\n設備(peripheral):\(String(describing: peripheral.name))\n服務(service):\(String(describing: service))\n服務下的特徵:\(service.characteristics ?? [])\n")
    }
    
    for characteristic in service.characteristics ?? [] {
        if characteristic.uuid.uuidString.lowercased().isEqual(BLE_WRITE_UUID) {
            pe = peripheral
            writeCh = characteristic
        } else if characteristic.uuid.uuidString.lowercased().isEqual(BLE_NOTIFY_UUID) {
            notifyCh = characteristic
            peripheral.setNotifyValue(true, for: characteristic)
        }
        //此處表明鏈接成功
    }
}
  • 此處可保存鏈接設備外設peripheral和特徵characteristic,後續發送接收數據時使用
  • swift的UUID與oc的不同,要區分大小寫,故UUID最好保存爲字符串
  • 鏈接成功可在此處設置回調

讀取設備發送的數據

獲取設備發送的數據:

func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic:
CBCharacteristic, error: Error?) {
    if let _ = error {
        return
    }
    //拿到設備發送過來的值,可傳出去並進行處理
    
}
  • 設備發送過來的數據會在此處回調,可設置閉包、協議等回調方式將值傳出去

向設備發送數據

發送數據:

peripheral.writeValue(data, for: characteristic, type: .withResponse)
  • 類型根據須要填寫
  • 特徵characteristic須要是寫入的特徵,不能用其餘的特徵

檢測發送數據是否成功:

func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
    if let error = error {
        print("\(#function)\n發送數據失敗!錯誤信息:\(error)")
    }
}

2.2.2 完整代碼下載

github代碼:BleDemo

歡迎相互學習交流,也歡迎去github點個star。

相關文章
相關標籤/搜索