【前言】
* 目前項目里有藍牙支付功能,對於藍牙開發功能,要求比較高,包括iOS與Android之間的通訊。
* 今天整理了下iOS藍牙SDK開發流程中的知識點,總結了這篇文章,希望給各位開發藍牙功能的同學帶來幫助。
【功能目標】
開發移動設備的藍牙功能,目的用來實現設備之間數據自由通訊(數據發,收),完成移動服務端和客服端場景交互。
【定義場景】
1: 廣播端:服務端定義,用於被多台掃描設備同時識別並訂閱;
2: 掃描端:客服端定義,用於掃描並訂閱廣播端設備;
【實現方案】
CoreBluetooth:iOS原生SDK。
導入: <CoreBluetooth/CoreBluetooth.h> 。
開始廣播功能:
【第一步、開啟廣播】
(1): 涉及的類
1: CBPeripheralManager;
外設管理器,管理設備廣播狀態。
2: CBUUID;
唯一標識,設備的服務,特性和特征描述符。
3: CBMutableService;
外設管理器的服務,用於設定服務特征。
4: CBMutableCharacteristic;
服務的特征,用於設定特征描述。
5: CBMutableDescriptor;
特征的描述。
(2): 類調用時序圖

時序圖備注: CBPeripheralManager 添加服務是很重要的一步。
(3): CoreBluetooth 原生函數
1: 藍牙創建,用於權限判斷:
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral;
2: 外設管理器添加服務完成,回調結果:
- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error;
3: 廣播開啟完成,回調結果:
-(void)peripheralManagerDidStartAdvertising: (CBPeripheralManager *)peripheral error:(NSError *)error;
(4): 廣播開啟失敗,重啟
1: 重置廣播,目前重試次數: ADVERTISING_RETRY = 3;(注:根據自己需求設置)
2: 重置加載安全模塊調用方法: [self setUpServiceSecurity]; (注:需要根據自己功能需求實現)
3: 重置外設管理器步驟: 1:關閉廣播 2:清除設備 3:重置管理器
4: 重置創建服務和特征調用方法: [self setupServiceAndCharacteristics];(注:需要根據自己功能需求實現)
(5): 藍牙廣播,參數配置
1: 廣播設備名稱: 可通過CBPeripheralManager 函數:
- (void)startAdvertising:(nullable NSDictionary<NSString *, id> *)advertisementData;
進行設置:CBAdvertisementDataLocalNameKey 的值。
2: 信號強度 : iOS不支持設置。
3: 廣播頻率 : iOS不支持設置。
【第二步、廣播被訂閱】
(1): 涉及的類
1: CBCentral: 發起訂閱的掃描設備;
2: CBCharacteristic: 掃描設備的特性信息;
(2): CoreBluetooth原生函數
1: 訂閱成功回調,記錄訂閱掃描設備:
-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic;
(3): 被取消訂閱函數
l 掃描設備取消訂閱,移除相應記錄的設備信息:
-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic;
(4):完成訂閱
注:當有掃描設備訂閱了廣播設備后,廣播端可以給掃描設備發送數據。
【第三步、發送數據】
(1): 涉及的類
DeviceInforModel: 數據容器對象。(注:自定義對象,用來記錄外設設備部信息和接,發數據的記錄)
-屬性- messageSentData: 發送數據DATA;
-屬性- sentStartIndex: 發送數據開始下標;
-屬性- diffLength: 每個包最大字節;
(2): 發送流程圖

(流程圖備注):
1: Start: 分包首個數據包字符串標示;
2: End: 分包最后一個數據包字符串標示;
3: sentStartIndex: 每個發送數據包的開始標示,默認等於0;
4: messageSentData:總數據中按照 sentStartIndex 截取的分包數據;
5: IsReady: 外圍設備已准備好發送特征值更新,有相應的回調函數;
6: sendMessagePart:進入數據分包流程;
(3): CoreBluetooth原生函數
1: 發送:通過通知或指示將更新后的特征值發送給一個或多個中心。
- (BOOL)updateValue:(NSData *)value forCharacteristic:(CBMutableCharacteristic *)characteristic onSubscribedCentrals:(nullable NSArray<CBCentral *> *)centrals;
2: 發送結果:告訴委托本地外圍設備已准備好發送特征值更新。
- (void)peripheralManagerIsReadyToUpdateSubscribers:(CBPeripheralManager *)peripheral;
【第四步、接收數據】
(1): 涉及的類
1: CBATTRequest: 寫入數據的請求。
-屬性- lastObject: 目標數據;
2: DeviceInforModel: 數據容器對象。(自定義對象,用來記錄外設設備部信息和接,發數據的記錄)
-屬性- messageReceivedData:發送數據DATA;
-屬性- isStart: 開頭標示;
-屬性- startMsg: 開頭數據;
-屬性- isEnd: 結尾標示;
-屬性- endMsg: 結尾數據;
(2): 接收流程圖
(流程圖備注):
1: Start: 分包首個數據包字符串標示,此時設置isStart=YES;
2: End: 分包最后一個數據包字符串標示,此時設置isEnd=YES;
3: CBATTRequest.lastObject:每個包的數據源;
4: messageReceivedData: 與歷史數據進行合並;
5: hasPrefix:@"End":這步判斷結束標示,有End標示,合並數據,輸出完整包;
6: CBATTRequest: 沒有End標示,等待分包數據;
(3): CoreBluetooth原生函數
l 中心設備寫入數據的時候,回調函數:
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray<CBATTRequest *> *)requests;
【完成:廣播功能】
完成上面步驟,此時廣播端可以自由發送和接收數據。
開始掃描功能:
【第一步、開啟掃描】
(1): 涉及的類
1: CBCentralManager:中心設備管理器。
2: CBUUID: 唯一標識,設備的服務,特性和特征描述符。
3: CBService: 中心設備管理器的服務,用於掃描指定服務廣播。
4: CBPeripheral:外設管理器,掃描到或連接的廣播設備。
5: RSSI: 外設型號強度值。
(2): 類調用時序圖

時序圖備注:在識別到廣播外設 CBPeripheral,並且完成記錄后,就可以開啟指定外設的連接請求,APP級外設標示通過 CBService 區分。
(3): 掃描開啟失敗
注:藍牙功能不可用,或者未開啟,會導致開啟失敗。
未發現符合要求的外設時,會繼續掃描。
(4): 藍牙掃描,參數配置
1: 掃描設備名稱: iOS不支持設置,
備注:訂閱廣播端成功后,廣播端區別掃描設備通過:central.identifier;
2: 掃描頻率: iOS不支持設置。
【第二步、連接廣播】
(1): 涉及的對象
1: self. centralManager:中心設備管理器;
2: self.peripheral: 中心設備管理器連接目標外設;
3: self.service: 用於查找的服務特征;
4: service.characteristics:對應服務的特征集合;
(2): CoreBluetooth原生函數
1: 發起連接外設,調用函數:
- (void)connectPeripheral:(CBPeripheral *)peripheral options:(nullable NSDictionary<NSString *, id> *)options;
2: 連接成功,回調函數:
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;
3: 根據SERVICE_UUID來尋找服務,調用函數:
- (void)discoverServices:(nullable NSArray<CBUUID *> *)serviceUUIDs;
4: 尋找到特定服務,回調函數:
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error;
5:尋找特定服務特征,回調函數:
- (void)discoverCharacteristics:(nullable NSArray<CBUUID *> *)characteristicUUIDs forService:(CBService *)service;
6: 發現特征,回調函數:
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error;
7: 讀取特征數據,調用函數:
[peripheral readValueForCharacteristic:self.characteristic];
8: 發送訂閱通知,調用函數(廣播端會收到被訂閱消息):
[peripheral setNotifyValue:YES forCharacteristic:self.characteristic];
9: 連接失敗,回調函數:
-(void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;
10: 斷開連接,回調函數:
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error;
(3): CoreBluetooth連接流程圖

【第三步、發送數據】
注:掃描端處理發送數據流程,與廣播端處理發送一致,詳見廣播端;
【第四步、接收數據】
注:掃描端處理接收數據流程,與廣播端處理接收一致,詳見廣播端;
【第五步、斷開連接】
1: self.centralManager 斷開指定 外設 self.peripheral;
[self.centralManager cancelPeripheralConnection:self.peripheral];
l 斷開連接,回調函數:
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error;
功能補充部分:
- 前面梳理了兩端數據發送,接收流程,下面補充一下實現方案:
【一:廣播端數據讀,寫方案】
CBMutableCharacteristic: 廣播端主要通過設置 CBMutableCharacteristic 特性值屬性實現:
CBCharacteristicPropertyWrite:設置廣播允許寫入特性值,用於接收數據;
CBCharacteristicPropertyNotify:設置廣播允許特征值更新的通知,用於發送數據;
(以下代碼,案例)
CBMutableCharacteristic *characteristic = [
[CBMutableCharacteristic alloc]
initWithType:characteristicID
properties:
CBCharacteristicPropertyNotify| (注:特性支持通知方案)
CBCharacteristicPropertyWrite (注:特性支持寫入方案)
value:nil
permissions:CBAttributePermissionsReadable|
CBAttributePermissionsWriteable];
CBUUID *UUID_Descriptor = [CBUUID UUIDWithString:DESCRIPTORUUID];
// 初始化一個特征的描述
CBMutableDescriptor *mDescriptor = [[CBMutableDescriptor alloc]initWithType:UUID_Descriptor value:[NSData data]];
[characteristic setDescriptors:@[mDescriptor]];
// 特征添加進服務
service.characteristics = @[characteristic];
// 服務加入管理
[self.peripheralManager addService:service];
---- 寫入方案:
1: 通過更新自身特性值實現寫入數據,調用函數:
- (BOOL)updateValue:(NSData *)value forCharacteristic:(CBMutableCharacteristic *)characteristic onSubscribedCentrals:(nullable NSArray<CBCentral *> *)centrals;
2: 當自身特性值准備更新時,回調函數(寫入完成,會通知掃描端):
- (void)peripheralManagerIsReadyToUpdateSubscribers:(CBPeripheralManager *)peripheral;
---- 讀取方案:
1: 通過允許寫入特性值,回調函數(掃描端寫入特性值完成回調):
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray<CBATTRequest *> *)requests;
【第二:掃描端數據讀,寫方案】
---- 寫入方案:
1: 掃描發送數據,調用函數(廣播特性值需要支持writeValue: ):
- (void)writeValue:(NSData *)data forCharacteristic:(CBCharacteristic *)characteristic type:(CBCharacteristicWriteType)type;
2: 寫入數據完成,回調結果:
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(nonnull CBCharacteristic *)characteristic error:(nullable NSError *)error;
---- 讀取方案:
1:通過實現外設代理方案:CBPeripheralDelegate:
self.peripheral.delegate = self;
2: CBPeripheralDelegate代理回調函數:
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error;

結語:
藍牙廣播端和掃描端的實現流程,上面梳理完成。
藍牙數據傳輸中的加密處理沒有包括在里面。
有問題歡迎一起研究討論,本人QQ號:497609288.


