一、引言
藍芽是裝置近距離通訊的一種方便手段,在iPhone引入藍芽4.0後,裝置之間的通訊變得更加簡單。相關的藍芽操作由專門的 CoreBluetooth.framework進行統一管理。通過藍芽進行通訊互動分為兩方,一方為中心裝置central,一方為外設 peripheral,外設通過廣播的方式向外傳送資訊,中心裝置檢索到外設發的廣播資訊,可以進行配對連線,進而進行資料互動。這裡我寫了一個輕量級的藍芽庫 GJLightBlueTooth,裡面包括基本需要的藍芽掃描連線操作。
二、中心裝置CBCentralManager
CBCentralManager是管理中心裝置的管理類,其中重要方法如下:
//設定中心裝置代理
@property(assign, nonatomic, nullable) id<CBCentralManagerDelegate> delegate;
//中心裝置當前狀態
@property(readonly) CBCentralManagerState state;
//中心裝置是否正在掃描
@property(readonly) BOOL isScanning NS_AVAILABLE(NA, 9_0);
其中state是一個列舉,有關藍芽是否可用的狀態如下:
typedef NS_ENUM(NSInteger, CBCentralManagerState) {
//狀態未知
CBCentralManagerStateUnknown = 0,
//連線斷開 即將重置
CBCentralManagerStateResetting,
//該平臺不支援藍芽
CBCentralManagerStateUnsupported,
//未授權藍芽使用
CBCentralManagerStateUnauthorized,
//藍芽關閉
CBCentralManagerStatePoweredOff,
//藍芽正常開啟
CBCentralManagerStatePoweredOn,
};
下面這些方法用於初始化管理中心:
//初始化方法
//設定的代理需要遵守CBCentralManagerDelegate協議
//queue可以設定藍芽掃描的執行緒 傳入nil則為在主執行緒中進行
- (instancetype)initWithDelegate:(nullable id<CBCentralManagerDelegate>)delegate
queue:(nullable dispatch_queue_t)queue;
//此方法同上 在options字典中用於進行一些管理中心的初始化屬性設定
//字典中支援的鍵值如下
/*
NSString * const CBCentralManagerOptionShowPowerAlertKey 對應一個NSNumber型別的bool值,用於設定是否在關閉藍芽時彈出使用者提示
NSString * const CBCentralManagerOptionRestoreIdentifierKey 對應一個NSString物件,設定管理中心的識別符號ID
*/
- (instancetype)initWithDelegate:(nullable id<CBCentralManagerDelegate>)delegate
queue:(nullable dispatch_queue_t)queue
options:(nullable NSDictionary<NSString *, id> *)options;
//根據獲取所有已知裝置
- (NSArray<CBPeripheral *> *)retrievePeripheralsWithIdentifiers:(NSArray<NSUUID *> *)identifiers;
//根據服務id獲取所有連線的裝置
- (NSArray<CBPeripheral *> *)retrieveConnectedPeripheralsWithServices:(NSArray<CBUUID *> *)serviceUUIDs;
在初始化管理中心完成後,會回撥代理中的如下方法,我們必須實現如下方法:
//這個方法中可以獲取到管理中心的狀態
- (void)centralManagerDidUpdateState:(CBCentralManager *)central;
如果上面方法中管理中心狀態為藍芽可用,可以通過下面方法開啟掃描外設:
//serviceUUIDs用於掃描一個特點ID的外設 options用於設定一些掃描屬性 鍵值如下
/*
//是否允許重複掃描 對應NSNumber的bool值,預設為NO,會自動去重
NSString *const CBCentralManagerScanOptionAllowDuplicatesKey;
//要掃描的裝置UUID 陣列 對應NSArray
NSString *const CBCentralManagerScanOptionSolicitedServiceUUIDsKey;
*/
- (void)scanForPeripheralsWithServices:(nullable NSArray<CBUUID *> *)serviceUUIDs options:(nullable NSDictionary<NSString *, id> *)options;
//停止掃描外設
- (void)stopScan;
掃描的結果會在如下代理方法中回掉:
//peripheral 掃描到的外設
//advertisementData是外設傳送的廣播資料
//RSSI 是訊號強度
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI;
掃描到外設後,通過下面方法可以連線一個外設:
/*
options中可以設定一些連線裝置的初始屬性鍵值如下
//對應NSNumber的bool值,設定當外設連線後是否彈出一個警告
NSString *const CBConnectPeripheralOptionNotifyOnConnectionKey;
//對應NSNumber的bool值,設定當外設斷開連線後是否彈出一個警告
NSString *const CBConnectPeripheralOptionNotifyOnDisconnectionKey;
//對應NSNumber的bool值,設定當外設暫停連線後是否彈出一個警告
NSString *const CBConnectPeripheralOptionNotifyOnNotificationKey;
*/
- (void)connectPeripheral:(CBPeripheral *)peripheral options:(nullable NSDictionary<NSString *, id> *)options;
//取消一個外設的連線
- (void)cancelPeripheralConnection:(CBPeripheral *)peripheral;
呼叫過連線外設的方法後,會回掉如下代理方法:
//連線外設成功
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;
//連線外設失敗
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error;
//斷開外設連線
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error;
當管理中心恢復時會呼叫如下代理:
//dict中會傳入如下鍵值對
/*
//恢復連線的外設陣列
NSString *const CBCentralManagerRestoredStatePeripheralsKey;
//恢復連線的服務UUID陣列
NSString *const CBCentralManagerRestoredStateScanServicesKey;
//恢復連線的外設掃描屬性字典陣列
NSString *const CBCentralManagerRestoredStateScanOptionsKey;
*/
- (void)centralManager:(CBCentralManager *)central willRestoreState:(NSDictionary<NSString *, id> *)dict;
三、外設CBPeripheralManager
從上面我們知道,中心裝置是用來掃描周圍的外設,兩臺裝置的通訊中,必須有一個充當中心裝置,一個充當外設,外設是由CBPeripheralManager進行管理,主要方法如下:
//設定外設管理中心代理
@property(assign, nonatomic, nullable) id<CBPeripheralManagerDelegate> delegate;
//外設狀態 列舉如中心裝置
@property(readonly) CBPeripheralManagerState state;
//是否正在傳送廣播
@property(readonly) BOOL isAdvertising;
//使用者的授權狀態
+ (CBPeripheralManagerAuthorizationStatus)authorizationStatus;
//初始化並設定代理 引數的具體含義與中心裝置管理中心
- (instancetype)initWithDelegate:(nullable id<CBPeripheralManagerDelegate>)delegate
queue:(nullable dispatch_queue_t);
- (instancetype)initWithDelegate:(nullable id<CBPeripheralManagerDelegate>)delegate
queue:(nullable dispatch_queue_t)queue
options:(nullable NSDictionary<NSString *, id> *)options;
//開始傳送廣播
//advertisementData中可以傳送的資料有約定 如下
/*
對應設定NSString型別的廣播名
NSString *const CBAdvertisementDataLocalNameKey;
外設製造商的NSData資料
NSString *const CBAdvertisementDataManufacturerDataKey;
外設製造商的CBUUID資料
NSString *const CBAdvertisementDataServiceDataKey;
服務的UUID與其對應的服務資料字典陣列
NSString *const CBAdvertisementDataServiceUUIDsKey;
附加服務的UUID陣列
NSString *const CBAdvertisementDataOverflowServiceUUIDsKey;
外設的傳送功率 NSNumber型別
NSString *const CBAdvertisementDataTxPowerLevelKey;
外設是否可以連線
NSString *const CBAdvertisementDataIsConnectable;
服務的UUID陣列
NSString *const CBAdvertisementDataSolicitedServiceUUIDsKey;
*/
- (void)startAdvertising:(nullable NSDictionary<NSString *, id> *)advertisementData;
//停止傳送廣播
- (void)stopAdvertising;
//設定一個連線的具體central裝置的延時 列舉如下
/*
typedef NS_ENUM(NSInteger, CBPeripheralManagerConnectionLatency) {
CBPeripheralManagerConnectionLatencyLow = 0,
CBPeripheralManagerConnectionLatencyMedium,
CBPeripheralManagerConnectionLatencyHigh
} NS_ENUM_AVAILABLE(NA, 6_0);
*/
- (void)setDesiredConnectionLatency:(CBPeripheralManagerConnectionLatency)latency forCentral:(CBCentral *)central;
//新增一個服務
- (void)addService:(CBMutableService *)service;
//移除一個服務
- (void)removeService:(CBMutableService *)service;
//移除所有服務
- (void)removeAllServices;
//響應中心裝置的讀寫請求
- (void)respondToRequest:(CBATTRequest *)request withResult:(CBATTError)result;
//更新一個連線中心裝置的訂閱特徵值
- (BOOL)updateValue:(NSData *)value forCharacteristic:(CBMutableCharacteristic *)characteristic onSubscribedCentrals:(nullable NSArray<CBCentral *> *)centrals;
外設代理的相關方法如下:
//這個方法是必須實現的 狀態可用後可以傳送廣播
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral;
//連線回復時呼叫的方法 和centralManager類似
- (void)peripheralManager:(CBPeripheralManager *)peripheral willRestoreState:(NSDictionary<NSString *, id> *)dict;
//開始傳送廣播時呼叫的方法
- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(nullable NSError *)error;
//新增服務呼叫的回撥
- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(nullable NSError *)error;
//當一個central裝置訂閱一個特徵值時呼叫的方法
- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic;
//取消訂閱一個特徵值時呼叫的方法
- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic;
//收到讀請求時觸發的方法
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request;
//收到寫請求時觸發的方法
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray<CBATTRequest *> *)requests;
//外設準備更新特徵值時呼叫的方法
- (void)peripheralManagerIsReadyToUpdateSubscribers:(CBPeripheralManager *)peripheral;
四、中心裝置與外設物件CBCentral與CBPeripheral
上面介紹了中心裝置管理類與外設管理類,這些類用於將裝置連線建立起來,器具的資料交換的服務和一些資訊則是在對應的裝置物件中。
1、中心裝置 CBCentral屬性與方法
//裝置UUID
@property(readonly, nonatomic) NSUUID *identifier;
//中心裝置最大接收的資料長度
@property(readonly, nonatomic) NSUInteger maximumUpdateValueLength;
2、外設CAPeripheral屬性與方法
外設物件要比中心物件複雜的多,當centralManager連線到外設後,需要通過外設物件的代理方法進行資料互動,其中主要方法屬性如下:
//設定代理
@property(assign, nonatomic, nullable) id<CBPeripheralDelegate> delegate;
//外設name
@property(retain, readonly, nullable) NSString *name;
//訊號強度
@property(retain, readonly, nullable) NSNumber *RSSI NS_DEPRECATED(NA, NA, 5_0, 8_0);
//外設狀態
/*
typedef NS_ENUM(NSInteger, CBPeripheralState) {
CBPeripheralStateDisconnected = 0,//未連線
CBPeripheralStateConnecting,//正在連結
CBPeripheralStateConnected,//已經連線
CBPeripheralStateDisconnecting NS_AVAILABLE(NA, 9_0),//正在斷開連線
} NS_AVAILABLE(NA, 7_0);
*/
@property(readonly) CBPeripheralState state;
//所有的服務陣列
@property(retain, readonly, nullable) NSArray<CBService *> *services;
//獲取當前訊號強度
- (void)readRSSI;
//根據服務UUID尋找服務物件
- (void)discoverServices:(nullable NSArray<CBUUID *> *)serviceUUIDs;
//在服務物件UUID陣列中尋找特定服務
- (void)discoverIncludedServices:(nullable NSArray<CBUUID *> *)includedServiceUUIDs forService:(CBService *)service;
//在一個服務中尋找特徵值
- (void)discoverCharacteristics:(nullable NSArray<CBUUID *> *)characteristicUUIDs forService:(CBService *)service;
//從一個特徵中讀取資料
- (void)readValueForCharacteristic:(CBCharacteristic *)characteristic;
//寫資料的最大長度
//type列舉如下
/*
typedef NS_ENUM(NSInteger, CBCharacteristicWriteType) {
CBCharacteristicWriteWithResponse = 0,//寫資料並且接收成功與否回執
CBCharacteristicWriteWithoutResponse,//寫資料不接收回執
};
*/
- (NSUInteger)maximumWriteValueLengthForType:(CBCharacteristicWriteType)type NS_AVAILABLE(NA, 9_0);
//向某個特徵中寫資料
- (void)writeValue:(NSData *)data forCharacteristic:(CBCharacteristic *)characteristic type:(CBCharacteristicWriteType)type;
//為制定的特徵值設定監聽通知
- (void)setNotifyValue:(BOOL)enabled forCharacteristic:(CBCharacteristic *)characteristic;
//尋找特徵值的描述
- (void)discoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic;
//讀取特徵的描述值
- (void)readValueForDescriptor:(CBDescriptor *)descriptor;
//寫特徵的描述值
- (void)writeValue:(NSData *)data forDescriptor:(CBDescriptor *)descriptor;
外設的代理方法如下:
//外設名稱更改時回撥的方法
- (void)peripheralDidUpdateName:(CBPeripheral *)peripheral NS_AVAILABLE(NA, 6_0);
//外設服務變化時回撥的方法
- (void)peripheral:(CBPeripheral *)peripheral didModifyServices:(NSArray<CBService *> *)invalidatedServices NS_AVAILABLE(NA, 7_0);
//訊號強度改變時呼叫的方法
- (void)peripheralDidUpdateRSSI:(CBPeripheral *)peripheral error:(nullable NSError *)error NS_DEPRECATED(NA, NA, 5_0, 8_0);
//讀取訊號強度回撥的方法
- (void)peripheral:(CBPeripheral *)peripheral didReadRSSI:(NSNumber *)RSSI error:(nullable NSError *)error NS_AVAILABLE(NA, 8_0);
//發現服務時呼叫的方法
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(nullable NSError *)error;
//在服務中發現子服務回撥的方法
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverIncludedServicesForService:(CBService *)service error:(nullable NSError *)error;
//發現服務的特徵值後回撥的方法
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(nullable NSError *)error;
//特徵值更新時回撥的方法
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error;
//向特徵值寫資料時回撥的方法
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error;
//特徵值的通知設定改變時觸發的方法
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error;
//發現特徵值的描述資訊觸發的方法
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error;
//特徵的描述值更新時觸發的方法
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForDescriptor:(CBDescriptor *)descriptor error:(nullable NSError *)error;
//寫描述資訊時觸發的方法
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForDescriptor:(CBDescriptor *)descriptor error:(nullable NSError *)error;
五、服務物件CBService
服務物件是用來管理外設提供的一些資料服務的,其中屬性如下:
//對應的外設
@property(assign, readonly, nonatomic) CBPeripheral *peripheral;
//是否是初等服務
@property(readonly, nonatomic) BOOL isPrimary;
//包含的自服務
@property(retain, readonly, nullable) NSArray<CBService *> *includedServices;
//服務中的特徵值
@property(retain, readonly, nullable) NSArray<CBCharacteristic *> *characteristics;
六、服務的特徵值CBCharacteristic
通過繫結服務中的特徵值來進行資料的讀寫操作,其中屬性如下:
//對應的服務物件
@property(assign, readonly, nonatomic) CBService *service;
//特徵值的屬性 列舉如下
/*
typedef NS_OPTIONS(NSUInteger, CBCharacteristicProperties) {
CBCharacteristicPropertyBroadcast,//允許廣播特徵
CBCharacteristicPropertyRead,//可讀屬性
CBCharacteristicPropertyWriteWithoutResponse,//可寫並且接收回執
CBCharacteristicPropertyWrite,//可寫屬性
CBCharacteristicPropertyNotify,//可通知屬性
CBCharacteristicPropertyIndicate,//可展現的特徵值
CBCharacteristicPropertyAuthenticatedSignedWrites,//允許簽名的特徵值寫入
CBCharacteristicPropertyExtendedProperties,
CBCharacteristicPropertyNotifyEncryptionRequired,
CBCharacteristicPropertyIndicateEncryptionRequired
};
*/
@property(readonly, nonatomic) CBCharacteristicProperties properties;
//特徵值的資料
@property(retain, readonly, nullable) NSData *value;
//特徵值的描述
@property(retain, readonly, nullable) NSArray<CBDescriptor *> *descriptors;
//是否是當前廣播的特徵
@property(readonly) BOOL isBroadcasted;
//是否是正在通知的特徵
@property(readonly) BOOL isNotifying;
七、讀寫請求物件CBATTRequest
服務物件是外設向中心裝置提供的相關資料服務,獲取到相應服務後,中心裝置可以進行讀寫請求,讀寫物件屬性如下:
//對應的中心裝置
@property(readonly, nonatomic) CBCentral *central;
//對應的特徵值
@property(readonly, nonatomic) CBCharacteristic *characteristic;
//讀寫資料值
@property(readwrite, copy, nullable) NSData *value;
這裡我寫了一個輕量級的藍芽庫 GJLightBlueTooth,裡面包括基本需要的藍芽掃描連線操作。