iOS 藍牙開發詳解(基本知識、相關類圖、交互流程)


本文從以下三方面講解下藍牙開發

1、藍牙相關基本知識

2、藍牙相關類圖

3、藍牙交互流程

 

一、藍牙相關基本知識

涉及到藍牙開發,首先有幾個問題是需要我們理解的

1、任何設備既可以是中心設備、也可以是外圍設備

 

2、外設 和 中心設備 之間通過特征建立一個雙向的數據通道

 

3、CBCentralManager主要操作中心設備,處理鏈接上外設之前的操作,鏈接上外設后,主要靠CBPeripheral(主要操作外設)處理外設相關操作(服務、特征、數據讀寫

 

4、中心設備管理 CBCentralManager

中心控制類,主要管理中心設備,以及處理跟外設(外圍設備)相關操作,主要是掃描、鏈接、斷開外設。

操作中心設備核心類

很重要的協議CBCentralManagerDelegate,包含中心設備狀態(是否打開藍牙)回調、發現外設回調、鏈接外設成功回調、鏈接外設失敗回調、外設鏈接斷開回調等方法。

一個中心設備可以鏈接多個外圍設備

 

5、外圍設備 CBPeripheral

外設類,包含設備的基礎屬性,名字,uuid等信息。向外設寫入數據。

當中心設備連接到外設后,需要通過外設對象的代理方法進行數據交互。

操作外圍設備核心類

很重要的協議CBPeripheralDelegate,包含發現服務回調、發現特征回調、特征的通知設置改變回調、特征更新回調、特征已寫入數據回調等方法。

一個設備包含多個服務、一個服務包含多個特征、一個特征又包含多個描述

 

6、外圍設備管理 CBPeripheralManager

設備的控制,主要可以為設備設置Service以及Characteristic,可以手動配置特定的服務和特征值,也可看作可以自定義藍牙協議,例如將手機作為外設時可以為自己的手機藍牙設置服務和特征值。CBCentralManager更適合將自己的軟件作為中心。

用的較少


7、服務 CBService

服務對象是用來管理外設提供的一些數據服務的。

一個服務可以包含多個特征

 

8、特征 CBCharacteristic

通過綁定服務中的特征值來進行數據的讀寫操作。

特征就是具體鍵值對提供數據的地方。

每個特征屬性分為這么幾種:讀,寫,通知等幾種方式。

有時讀、寫、通知可以是同一個特征,也可以讀、寫、通知各用一個特征表示。

一個特征可以包含多個描述。

一般我們操作到特征這一層

 

9、描述 CBDescriptor

每個characteristic可以對應一個或多個Description 供用戶描述characteristic的信息或屬性。

 

10、CBAttribute

 CBServiceCBCharacteristicCBDescriptor 類都繼承自 CBAttribute,

它們有一個共同的屬性 CBUUID,用來作為唯一的標識

 

二、藍牙相關類圖

下面用類圖簡述下:

一個中心設備可以連接多個外設,一個外設包含多個服務,一個服務包含多個特征,一個特征包含多個描述

服務、特征、描述都用CBUUID唯一標識

 

  

三、藍牙交互流程

下面簡述下以手機作為中心設備其它作為外圍設備的交互流程,大致流程如圖所示

下面是操作詳解

1、創建中心設備管理對象(初始化中心設備

CBCentralManager創建異步的,如果初始化完成之后沒有被當前創建它的類所持有,就會在下一次 RunLoop 迭代的時候釋放。

創建線程nil,為主線程

初始化成功后,就會觸發 CBCentralManagerDelegate 中的中心設備狀態更新方法:centralManagerDidUpdateState:

NSDictionary *options = @{CBCentralManagerOptionShowPowerAlertKey:@NO};
        self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:options];
/*!
 *  @method initWithDelegate:queue:options:
 *
 *  @param delegate The delegate that will receive central role events.
 *  @param queue    The dispatch queue on which the events will be dispatched.
 *  @param options  An optional dictionary specifying options for the manager.
 *
 *  @discussion     The initialization call. The events of the central role will be dispatched on the provided queue.
 *                  If <i>nil</i>, the main queue will be used.
 *
 *    @seealso        CBCentralManagerOptionShowPowerAlertKey
 *    @seealso        CBCentralManagerOptionRestoreIdentifierKey
 *
 */
- (instancetype)initWithDelegate:(nullable id<CBCentralManagerDelegate>)delegate
                           queue:(nullable dispatch_queue_t)queue
                         options:(nullable NSDictionary<NSString *, id> *)options NS_AVAILABLE(10_9, 7_0) NS_DESIGNATED_INITIALIZER;
@protocol CBCentralManagerDelegate <NSObject>

@required

/*!
 *  @method centralManagerDidUpdateState:
 *
 *  @param central  The central manager whose state has changed.
 *
 *  @discussion     Invoked whenever the central manager's state has been updated. Commands should only be issued when the state is
 *                  <code>CBCentralManagerStatePoweredOn</code>. A state below <code>CBCentralManagerStatePoweredOn</code>
 *                  implies that scanning has stopped and any connected peripherals have been disconnected. If the state moves below
 *                  <code>CBCentralManagerStatePoweredOff</code>, all <code>CBPeripheral</code> objects obtained from this central
 *                  manager become invalid and must be retrieved or discovered again.
 *
 *  @see            state
 *
 */
- (void)centralManagerDidUpdateState:(CBCentralManager *)central;

 

2、掃描外圍設備

CBCentralManagerDelegate 中的中心設備狀態更新方法:centralManagerDidUpdateState:

當中心設備處於CBManagerStatePoweredOn 狀態的時候開始掃描周邊設備(可以使用指定的 UUID 發現特定的 Service,也可以傳入 nil,表示發現所有周邊的藍牙設備,不過還是建議只發現自己需要服務的設備)。

掃描外設:scanForPeripheralsWithServices:options:

該操作由CBCentralManager對象通過scanForPeripheralsWithServices:options:方法實現

case CBManagerStatePoweredOn:
                //藍牙正常開啟
                [self startScan];
                break;
- (void)startScan {
    
//    [self.centralManager scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey : @YES }];
//    掃描所有設備 當指定設備不好使時可以使用該方法
//    [self.centralManager scanForPeripheralsWithServices:nil options:nil];
//    掃描指定設備 快速
    [self.centralManager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:self.peripheralServiceUUID]] options:nil];
    
    [self startTime];
}

 

/*!
 *  @method scanForPeripheralsWithServices:options:
 *
 *  @param serviceUUIDs A list of <code>CBUUID</code> objects representing the service(s) to scan for.
 *  @param options      An optional dictionary specifying options for the scan.
 *
 *  @discussion         Starts scanning for peripherals that are advertising any of the services listed in <i>serviceUUIDs</i>. Although strongly discouraged,
 *                      if <i>serviceUUIDs</i> is <i>nil</i> all discovered peripherals will be returned. If the central is already scanning with different
 *                      <i>serviceUUIDs</i> or <i>options</i>, the provided parameters will replace them.
 *                      Applications that have specified the <code>bluetooth-central</code> background mode are allowed to scan while backgrounded, with two
 *                      caveats: the scan must specify one or more service types in <i>serviceUUIDs</i>, and the <code>CBCentralManagerScanOptionAllowDuplicatesKey</code>
 *                      scan option will be ignored.
 *
 *  @see                centralManager:didDiscoverPeripheral:advertisementData:RSSI:
 *  @seealso            CBCentralManagerScanOptionAllowDuplicatesKey
 *    @seealso            CBCentralManagerScanOptionSolicitedServiceUUIDsKey
 *
 */
- (void)scanForPeripheralsWithServices:(nullable NSArray<CBUUID *> *)serviceUUIDs options:(nullable NSDictionary<NSString *, id> *)options;

 

3、發現外圍設備

CBCentralManager對象執行掃描外設方法scanForPeripheralsWithServices:options:后

會觸發 CBCentralManagerDelegate 中的方法:(發現外設)centralManager:didDiscoverPeripheral:advertisementData:RSSI:

 

如果在掃描時指定了明確的服務,那么此時該方法里的外設就是包含該服務的外設

如果傳入的是nil,那么此時該方法里的外設就是周邊所有打開的藍牙設備。

/*!
 *  @method centralManager:didDiscoverPeripheral:advertisementData:RSSI:
 *
 *  @param central              The central manager providing this update.
 *  @param peripheral           A <code>CBPeripheral</code> object.
 *  @param advertisementData    A dictionary containing any advertisement and scan response data.
 *  @param RSSI                 The current RSSI of <i>peripheral</i>, in dBm. A value of <code>127</code> is reserved and indicates the RSSI
 *                                was not available.
 *
 *  @discussion                 This method is invoked while scanning, upon the discovery of <i>peripheral</i> by <i>central</i>. A discovered peripheral must
 *                              be retained in order to use it; otherwise, it is assumed to not be of interest and will be cleaned up by the central manager. For
 *                              a list of <i>advertisementData</i> keys, see {@link CBAdvertisementDataLocalNameKey} and other similar constants.
 *
 *  @seealso                    CBAdvertisementData.h
 *
 */
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI;

 

4、鏈接外設

CBCentralManagerDelegate 中的發現外設方法:centralManager:didDiscoverPeripheral:advertisementData:RSSI:

我們會鏈接外設:connectPeripheral:options:

該操作由CBCentralManager對象通過connectPeripheral:options:方法實現

我們通過名稱或者廠商數據來確定我們需要鏈接的外設(過濾外設),找到停止掃描,然后鏈接該外設,即鏈接指定外設

[central connectPeripheral:peripheral options:nil];
/*!
 *  @method connectPeripheral:options:
 *
 *  @param peripheral   The <code>CBPeripheral</code> to be connected.
 *  @param options      An optional dictionary specifying connection behavior options.
 *
 *  @discussion         Initiates a connection to <i>peripheral</i>. Connection attempts never time out and, depending on the outcome, will result
 *                      in a call to either {@link centralManager:didConnectPeripheral:} or {@link centralManager:didFailToConnectPeripheral:error:}.
 *                      Pending attempts are cancelled automatically upon deallocation of <i>peripheral</i>, and explicitly via {@link cancelPeripheralConnection}.
 *
 *  @see                centralManager:didConnectPeripheral:
 *  @see                centralManager:didFailToConnectPeripheral:error:
 *  @seealso            CBConnectPeripheralOptionNotifyOnConnectionKey
 *  @seealso            CBConnectPeripheralOptionNotifyOnDisconnectionKey
 *  @seealso            CBConnectPeripheralOptionNotifyOnNotificationKey
 *  @seealso            CBConnectPeripheralOptionEnableTransportBridgingKey
 *    @seealso            CBConnectPeripheralOptionRequiresANCS
 *
 */
- (void)connectPeripheral:(CBPeripheral *)peripheral options:(nullable NSDictionary<NSString *, id> *)options;

 

5、鏈接外設結果回調

CBCentralManager對象執行鏈接外設方法connectPeripheral:options:

會觸發 CBCentralManagerDelegate 中的方法:

鏈接外設成功回調:centralManager:didConnectPeripheral:

/*!
 *  @method centralManager:didConnectPeripheral:
 *
 *  @param central      The central manager providing this information.
 *  @param peripheral   The <code>CBPeripheral</code> that has connected.
 *
 *  @discussion         This method is invoked when a connection initiated by {@link connectPeripheral:options:} has succeeded.
 *
 */
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;

鏈接外設失敗回調:centralManager:didFailToConnectPeripheral:error:

/*!
 *  @method centralManager:didFailToConnectPeripheral:error:
 *
 *  @param central      The central manager providing this information.
 *  @param peripheral   The <code>CBPeripheral</code> that has failed to connect.
 *  @param error        The cause of the failure.
 *
 *  @discussion         This method is invoked when a connection initiated by {@link connectPeripheral:options:} has failed to complete. As connection attempts do not
 *                      timeout, the failure of a connection is atypical and usually indicative of a transient issue.
 *
 */
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error;

 

6、查找服務

CBCentralManagerDelegate 中的鏈接外設成功回調方法:centralManager:didConnectPeripheral:

我們會查找服務:discoverServices:

該操作由CBPeripheral對象通過discoverServices:方法實現

[peripheral discoverServices:@[[CBUUID UUIDWithString:self.peripheralServiceUUID]]];
/*!
 *  @method discoverServices:
 *
 *  @param serviceUUIDs A list of <code>CBUUID</code> objects representing the service types to be discovered. If <i>nil</i>,
 *                        all services will be discovered.
 *
 *  @discussion            Discovers available service(s) on the peripheral.
 *
 *  @see                peripheral:didDiscoverServices:
 */
- (void)discoverServices:(nullable NSArray<CBUUID *> *)serviceUUIDs;

 

7、發現服務

CBPeripheral對象執行查找服務方法discoverServices:后,

會觸發 CBPeripheralDelegate 中的發現服務方法:peripheral:didDiscoverServices:

/*!
 *  @method peripheral:didDiscoverServices:
 *
 *  @param peripheral    The peripheral providing this information.
 *    @param error        If an error occurred, the cause of the failure.
 *
 *  @discussion            This method returns the result of a @link discoverServices: @/link call. If the service(s) were read successfully, they can be retrieved via
 *                        <i>peripheral</i>'s @link services @/link property.
 *
 */
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(nullable NSError *)error;

 

8、查找特征

在 CBPeripheralDelegate 中的發現服務方法:peripheral:didDiscoverServices:中,

我們會查找特征:discoverCharacteristics:forService:

該操作由CBPeripheral對象通過discoverCharacteristics:forService:方法實現

[peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:self.peripheralCharacteristicRTXUUID]] forService:service];
/*!
 *  @method discoverCharacteristics:forService:
 *
 *  @param characteristicUUIDs    A list of <code>CBUUID</code> objects representing the characteristic types to be discovered. If <i>nil</i>,
 *                                all characteristics of <i>service</i> will be discovered.
 *  @param service                A GATT service.
 *
 *  @discussion                    Discovers the specified characteristic(s) of <i>service</i>.
 *
 *  @see                        peripheral:didDiscoverCharacteristicsForService:error:
 */
- (void)discoverCharacteristics:(nullable NSArray<CBUUID *> *)characteristicUUIDs forService:(CBService *)service;

 

9、發現特征

CBPeripheral對象執行查找特征方法discoverCharacteristics:forService:

會觸發CBPeripheralDelegate 中的發現特征方法:peripheral:didDiscoverCharacteristicsForService:error:

/*!
 *  @method peripheral:didDiscoverCharacteristicsForService:error:
 *
 *  @param peripheral    The peripheral providing this information.
 *  @param service        The <code>CBService</code> object containing the characteristic(s).
 *    @param error        If an error occurred, the cause of the failure.
 *
 *  @discussion            This method returns the result of a @link discoverCharacteristics:forService: @/link call. If the characteristic(s) were read successfully, 
 *                        they can be retrieved via <i>service</i>'s <code>characteristics</code> property.
 */
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(nullable NSError *)error;

 

10、寫特征、通知特征(讀取)、讀特征

在 CBPeripheralDelegate 中的發現特征方法:peripheral:didDiscoverCharacteristicsForService:error:中,

我們會處理特征(讀、寫、通知),一般會保存寫入特征,方便后期寫入數據打開使能通知,方便讀取數據

寫:

保存寫特征,方便后期寫入數據

讀:

通知特征和特征都是為了讀取,一般我們使用的都是通知

使用通知的時候,要打開使能通知(訂閱),

操作CBPeripheral對象通過setNotifyValue:forCharacteristic:方法打開指定通知特征

 

通知特征發送的數據didUpdateValueForCharacteristic方法里接受(讀取

該操作會回調CBPeripheralDelegate 中的方法peripheral:didUpdateNotificationStateForCharacteristic:error:

通過characteristic.isNotifying知曉通知狀態

/*!
 *  @method peripheral:didUpdateNotificationStateForCharacteristic:error:
 *
 *  @param peripheral        The peripheral providing this information.
 *  @param characteristic    A <code>CBCharacteristic</code> object.
 *    @param error            If an error occurred, the cause of the failure.
 *
 *  @discussion                This method returns the result of a @link setNotifyValue:forCharacteristic: @/link call. 
 */
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error;
for (CBCharacteristic *characteristic in service.characteristics)
        {
//            if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:self.peripheralCharacteristicTXUUID]]) {
//                //                打開使能通知 (訂閱)該特征發送的數據在didUpdateValueForCharacteristic方法里接受(讀取)
//                [peripheral setNotifyValue:YES forCharacteristic:characteristic];
//            } else if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:self.peripheralCharacteristicRXUUID]]) {
//                //                保存外設接受特征(寫入特征)
//                self.characteristicRX = characteristic;

 

11、寫入數據

寫入方法:writeValue:forCharacteristic:type:

寫入操作由CBPeripheral對象通過writeValue:forCharacteristic:type:方法寫入指定寫入特征

[self.peripheral writeValue:peripheralRXData forCharacteristic:self.characteristicRX type:
            CBCharacteristicWriteWithResponse];
/*!
 *  @method writeValue:forCharacteristic:type:
 *
 *  @param data                The value to write.
 *  @param characteristic    The characteristic whose characteristic value will be written.
 *  @param type                The type of write to be executed.
 *
 *  @discussion                Writes <i>value</i> to <i>characteristic</i>'s characteristic value.
 *                            If the <code>CBCharacteristicWriteWithResponse</code> type is specified, {@link peripheral:didWriteValueForCharacteristic:error:}
 *                            is called with the result of the write request.
 *                            If the <code>CBCharacteristicWriteWithoutResponse</code> type is specified, and canSendWriteWithoutResponse is false, the delivery
 *                             of the data is best-effort and may not be guaranteed.
 *
 *  @see                    peripheral:didWriteValueForCharacteristic:error:
 *  @see                    peripheralIsReadyToSendWriteWithoutResponse:
 *    @see                    canSendWriteWithoutResponse
 *    @see                    CBCharacteristicWriteType
 */
- (void)writeValue:(NSData *)data forCharacteristic:(CBCharacteristic *)characteristic type:(CBCharacteristicWriteType)type;

寫入數據有兩種方式:

/*
32 typedef NS_ENUM(NSInteger, CBCharacteristicWriteType) {
33     CBCharacteristicWriteWithResponse = 0,//寫數據並且接收成功與否回執
34     CBCharacteristicWriteWithoutResponse,//寫數據不接收回執
35 };
36 */

如果寫入類型為CBCharacteristicWriteWithResponse 回調CBPeripheralDelegate 中的方法:

peripheral:didWriteValueForCharacteristic:error:

如果寫入類型為CBCharacteristicWriteWithoutResponse不回調此方法,

該方法只是告知寫入數據是否成功

/*!
 *  @method peripheral:didWriteValueForCharacteristic:error:
 *
 *  @param peripheral        The peripheral providing this information.
 *  @param characteristic    A <code>CBCharacteristic</code> object.
 *    @param error            If an error occurred, the cause of the failure.
 *
 *  @discussion                This method returns the result of a {@link writeValue:forCharacteristic:type:} call, when the <code>CBCharacteristicWriteWithResponse</code> type is used.
 */
 - (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error;

寫入數據后外設響應數據在特征值更新方法didUpdateValueForCharacteristic:error:中讀取

 

12、讀取數據

讀取外設發送給中心設備的數據,

無論是read的回調,還是notify(訂閱)的回調都是CBPeripheralDelegate 中的方法:

特征值更新:didUpdateValueForCharacteristic:error:

/*!
 *  @method peripheral:didUpdateValueForCharacteristic:error:
 *
 *  @param peripheral        The peripheral providing this information.
 *  @param characteristic    A <code>CBCharacteristic</code> object.
 *    @param error            If an error occurred, the cause of the failure.
 *
 *  @discussion                This method is invoked after a @link readValueForCharacteristic: @/link call, or upon receipt of a notification/indication.
 */
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error;

 

13、斷開鏈接

斷開鏈接:cancelPeripheralConnection:

斷開鏈接操作由CBCentralManager對象通過cancelPeripheralConnection:方法實現

該方法不會觸發CBCentralManagerDelegate 中的方法:

斷開外設(僅在異常斷開時會觸發):centralManager:didDisconnectPeripheral:error:

/*!
 *  @method cancelPeripheralConnection:
 *
 *  @param peripheral   A <code>CBPeripheral</code>.
 *
 *  @discussion         Cancels an active or pending connection to <i>peripheral</i>. Note that this is non-blocking, and any <code>CBPeripheral</code>
 *                      commands that are still pending to <i>peripheral</i> may or may not complete.
 *
 *  @see                centralManager:didDisconnectPeripheral:error:
 *
 */
- (void)cancelPeripheralConnection:(CBPeripheral *)peripheral;

 參考資料:

http://www.myexceptions.net/operating-system/2052286.html

https://blog.csdn.net/TAO_HUADAO/article/details/54629745?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control

https://www.jianshu.com/p/ec659ffcacfe


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM