最近由於項目需要, 一直在研究藍牙4.0,在這兒分享給大家, 望共同進步. 一、關於藍牙開發的一些重要的理論概念: 1.當前ios中開發藍牙所運用的系統庫是<CoreBluetooth/CoreBluetooth.h>。 2.藍牙外設必須為4.0及以上(2.0需要MFI認證),否則無法開發,藍牙4. ...
最近由於項目需要, 一直在研究藍牙4.0,在這兒分享給大家, 望共同進步.
一、關於藍牙開發的一些重要的理論概念:
1.當前ios中開發藍牙所運用的系統庫是<CoreBluetooth/CoreBluetooth.h>。
2.藍牙外設必須為4.0及以上(2.0需要MFI認證),否則無法開發,藍牙4.0設備因為低耗電,所以也叫做BLE。
3.CoreBluetooth框架的核心其實是兩個東西,peripheral和central, 可以理解成外設和中心,就是你的蘋果手機就是中心,外部藍牙稱為外設。
4.服務和特征(service and characteristic):簡而言之,外部藍牙中它有若幹個服務service(服務你可以理解為藍牙所擁有的能力),而每個服務service下擁有若幹個特征characteristic(特征你可以理解為解釋這個服務的屬性)。
5.Descriptor(描述)用來描述characteristic變數的屬性。例如,一個descriptor可以規定一個可讀的描述,或者一個characteristic變數可接受的範圍,或者一個characteristic變數特定的單位。
6.我們使用的藍牙模塊是在淘寶買的, 大概十多元一個, ios大概每次可以接受90個位元組, 安卓大概每次可以接收20個位元組, 具體數字可能會浮動, 應該是與藍牙模塊有關。
二、藍牙連接的主要步驟
1、創建一個CBCentralManager實例來進行藍牙管理; 2、搜索掃描外圍設備; 3、連接外圍設備; 4、獲得外圍設備的服務; 5、獲得服務的特征; 6、從外圍設備讀取數據; 7、給外圍設備發送(寫入)數據。
三、代碼
// 加入許可權訪問, 否則上傳AppStore會因為許可權不足失敗
1. 初始化
#import <CoreBluetooth/CoreBluetooth.h>
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
2. 搜索掃描外圍設備
/** * -- 初始化成功自動調用 * -- 必須實現的代理,用來返回創建的centralManager的狀態。 * -- 註意:必須確認當前是CBCentralManagerStatePoweredOn狀態才可以調用掃描外設的方法: scanForPeripheralsWithServices */ - (void)centralManagerDidUpdateState:(CBCentralManager *)central{ switch (central.state) { case CBCentralManagerStateUnknown: NSLog(@">>>CBCentralManagerStateUnknown"); break; case CBCentralManagerStateResetting: NSLog(@">>>CBCentralManagerStateResetting"); break; case CBCentralManagerStateUnsupported: NSLog(@">>>CBCentralManagerStateUnsupported"); break; case CBCentralManagerStateUnauthorized: NSLog(@">>>CBCentralManagerStateUnauthorized"); break; case CBCentralManagerStatePoweredOff: NSLog(@">>>CBCentralManagerStatePoweredOff"); break; case CBCentralManagerStatePoweredOn: { NSLog(@">>>CBCentralManagerStatePoweredOn"); // 開始掃描周圍的外設。 /* -- 兩個參數為Nil表示預設掃描所有可見藍牙設備。 -- 註意:第一個參數是用來掃描有指定服務的外設。然後有些外設的服務是相同的,比如都有FFF5服務,那麼都會發現;而有些外設的服務是不可見的,就會掃描不到設備。 -- 成功掃描到外設後調用didDiscoverPeripheral */ [self.centralManager scanForPeripheralsWithServices:nil options:nil]; } break; default: break; } }
#pragma mark 發現外設 - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI{ NSLog(@"Find device:%@", [peripheral name]); if (![_deviceDic objectForKey:[peripheral name]]) { NSLog(@"Find device:%@", [peripheral name]); if (peripheral!=nil) { if ([peripheral name]!=nil) { if ([[peripheral name] hasPrefix:@"根據設備名過濾"]) { [_deviceDic setObject:peripheral forKey:[peripheral name]]; // 停止掃描, 看需求決定要不要加 // [_centralManager stopScan]; // 將設備信息傳到外面的頁面(VC), 構成掃描到的設備列表 if ([self.delegate respondsToSelector:@selector(dataWithBluetoothDic:)]) { [self.delegate dataWithBluetoothDic:_deviceDic]; } } } } } }
3.連接外圍設備
// 連接設備(.h中聲明出去的介面, 一般在點擊設備列表連接時調用) - (void)connectDeviceWithPeripheral:(CBPeripheral *)peripheral { [self.centralManager connectPeripheral:peripheral options:nil]; }
#pragma mark 連接外設--成功 - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{ //連接成功後停止掃描,節省記憶體 [central stopScan]; peripheral.delegate = self; self.peripheral = peripheral; //4.掃描外設的服務 /** -- 外設的服務、特征、描述等方法是CBPeripheralDelegate的內容,所以要先設置代理peripheral.delegate = self -- 參數表示你關心的服務的UUID,比如我關心的是"FFE0",參數就可以為@[[CBUUID UUIDWithString:@"FFE0"]].那麼didDiscoverServices方法回調內容就只有這兩個UUID的服務,不會有其他多餘的內容,提高效率。nil表示掃描所有服務 -- 成功發現服務,回調didDiscoverServices */ [peripheral discoverServices:@[[CBUUID UUIDWithString:@"你要用的服務UUID"]]]; if ([self.delegate respondsToSelector:@selector(didConnectBle)]) { // 已經連接 [self.delegate didConnectBle]; } }
#pragma mark 連接外設——失敗 - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{ NSLog(@"%@", error); }
#pragma mark 取消與外設的連接回調 - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{ NSLog(@"%@", peripheral); }
4. 獲得外圍設備的服務
#pragma mark 發現服務回調 - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{ //NSLog(@"didDiscoverServices,Error:%@",error); CBService * __nullable findService = nil; // 遍歷服務 for (CBService *service in peripheral.services) { //NSLog(@"UUID:%@",service.UUID); if ([[service UUID] isEqual:[CBUUID UUIDWithString:@"你要用的服務UUID"]]) { findService = service; } } NSLog(@"Find Service:%@",findService); if (findService) [peripheral discoverCharacteristics:NULL forService:findService]; }
5、獲得服務的特征
#pragma mark 發現特征回調 /** -- 發現特征後,可以根據特征的properties進行:讀readValueForCharacteristic、寫writeValue、訂閱通知setNotifyValue、掃描特征的描述discoverDescriptorsForCharacteristic。 **/ - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{ for (CBCharacteristic *characteristic in service.characteristics) { if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"你要用的特征UUID"]]) { /** -- 讀取成功回調didUpdateValueForCharacteristic */ self.characteristic = characteristic; // 接收一次(是讀一次信息還是數據經常變實時接收視情況而定, 再決定使用哪個) // [peripheral readValueForCharacteristic:characteristic]; // 訂閱, 實時接收 [peripheral setNotifyValue:YES forCharacteristic:characteristic]; // 發送下行指令(發送一條) NSData *data = [@"硬體工程師給我的指令, 發送給藍牙該指令, 藍牙會給我返回一條數據" dataUsingEncoding:NSUTF8StringEncoding]; // 將指令寫入藍牙 [self.peripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse]; } /** -- 當發現characteristic有descriptor,回調didDiscoverDescriptorsForCharacteristic */ [peripheral discoverDescriptorsForCharacteristic:characteristic]; } }
6.從外圍設備讀取數據
#pragma mark - 獲取值 - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{ // characteristic.value就是藍牙給我們的值(我這裡是json格式字元串) NSData *jsonData = [characteristic.value dataUsingEncoding:NSUTF8StringEncoding]; NSDictionary *dataDic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:nil]; // 將字典傳出去就可以使用了 } #pragma mark - 中心讀取外設實時數據 - (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{ if (characteristic.isNotifying) { [peripheral readValueForCharacteristic:characteristic]; } else { NSLog(@"Notification stopped on %@. Disconnecting", characteristic); NSLog(@"%@", characteristic); [self.centralManager cancelPeripheralConnection:peripheral]; } }
7. 給外圍設備發送(寫入)數據
// 上文中發現特征之後, 發送下行指令的時候其實就是向藍牙中寫入數據 // 例: // 發送檢查藍牙命令 - (void)writeCheckBleWithBle { _style = 1; // 發送下行指令(發送一條) NSData *data = [@"硬體工程師提供給你的指令, 類似於5E16010203...這種很長一串" dataUsingEncoding:NSUTF8StringEncoding]; [self.peripheral writeValue:data forCharacteristic:self.characteristic type:CBCharacteristicWriteWithResponse]; }
#pragma mark 數據寫入成功回調 - (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{ NSLog(@"寫入成功"); if ([self.delegate respondsToSelector:@selector(didWriteSucessWithStyle:)]) { [self.delegate didWriteSucessWithStyle:_style]; } }
8. 另外
// 掃描設備 - (void)scanDevice { if (_centralManager == nil) { self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil]; [_deviceDic removeAllObjects]; } }
#pragma mark - 斷開連接 - (void)disConnectPeripheral{ /** -- 斷開連接後回調didDisconnectPeripheral -- 註意斷開後如果要重新掃描這個外設,需要重新調用[self.centralManager scanForPeripheralsWithServices:nil options:nil]; */ [self.centralManager cancelPeripheralConnection:self.peripheral]; }
#pragma mark - 停止掃描外設 - (void)stopScanPeripheral{ [self.centralManager stopScan]; }
由於硬體方面剛開始用藍牙2.0跟我對接, 導致程式一直搜索不到設備.希望小伙伴們註意一下這個問題. 如果有不清楚的地方歡迎留言探討.