iOS蓝牙开发
CoreBluetooth詳解
CoreBluetooth框架的核心其實是兩個東西,peripheral和central, 可以理解成外設和中心。
圖中兩組api分別對應不同的業務場景,左側叫做中心模式,就是以你的app作為中心,連接其他的外設的場景,而右側稱為外設模式,使用手機作為外設別其他中心設備操作的場景
關于藍牙開發的一些重要的理論概念:
1、服務(services):藍牙外設對外廣播的時候一定會有一個服務,有些時候也可以是有多個服務,服務下面包含一些特性,服務可以理解成一個模塊的窗口;
2、特征(characteristic):特征存在于服務下面的,一個服務下面可以有多個特征,特征可以理解成具體實現功能的窗口,一般的特性都會有value,也就是特征值,是特征和外界交互的最小單位;
3、UUID:藍牙上的唯一標示符,為了區分不同設備、服務及特征,就用UUID來表示。
CBCentralMannager 中心模式
以手機(app)作為中心,連接其他外設的場景。詳細流程如下:
步驟1.建立一個Central Manager實例進行藍牙管理
步驟2.搜索外圍設備
步驟3.連接外圍設備
步驟4.獲得外圍設備的服務
步驟5.獲得服務的特征
步奏6.從外圍設備讀數據(直接讀取和訂閱兩種方法)
步驟7.給外圍設備發送數據
#import "ViewController.h" #import #define kPeripheralName @"Kenshin Cui's Device" //外圍設備名稱 #define kServiceUUID @"C4FB2349-72FE-4CA2-94D6-1F3CB16331EE" //服務的UUID #define kCharacteristicUUID @"6A3E4B28-522D-4B3B-82A9-D5E2004534FC" //特征的UUID@interface ViewController ()@property (strong,nonatomic) CBPeripheralManager *peripheralManager;//外圍設備管理器@property (strong,nonatomic) NSMutableArray *centralM;//訂閱此外圍設備特征的中心設備@property (strong,nonatomic) CBMutableCharacteristic *characteristicM;//特征 @property (weak, nonatomic) IBOutlet UITextView *log; //日志記錄@end @implementation ViewController #pragma mark - 控制器視圖事件 - (void)viewDidLoad {[super viewDidLoad]; }#pragma mark - UI事件 - (IBAction)startClick:(UIBarButtonItem *)sender {//創建中心設備管理器并設置當前控制器視圖為代理_centralManager=[[CBCentralManager alloc]initWithDelegate:self queue:nil]; }#pragma mark - CBCentralManager代理方法 //中心服務器狀態更新后 -(void)centralManagerDidUpdateState:(CBCentralManager *)central{switch (central.state) {case CBPeripheralManagerStatePoweredOn:NSLog(@"BLE已打開.");[self writeToLog:@"BLE已打開."];//掃描外圍設備 // [central scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:kServiceUUID]] options:@{CBCentralManagerScanOptionAllowDuplicatesKey:@YES}];[central scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey:@YES}];break;default:NSLog(@"此設備不支持BLE或未打開藍牙功能,無法作為外圍設備.");[self writeToLog:@"此設備不支持BLE或未打開藍牙功能,無法作為外圍設備."];break;} } /*** 發現外圍設備** @param central 中心設備* @param peripheral 外圍設備* @param advertisementData 特征數據* @param RSSI 信號質量(信號強度)*/ -(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{NSLog(@"發現外圍設備...");[self writeToLog:@"發現外圍設備..."];//停止掃描[self.centralManager stopScan];//連接外圍設備if (peripheral) {//添加保存外圍設備,注意如果這里不保存外圍設備(或者說peripheral沒有一個強引用,無法到達連接成功(或失敗)的代理方法,因為在此方法調用完就會被銷毀if(![self.peripherals containsObject:peripheral]){[self.peripherals addObject:peripheral];}NSLog(@"開始連接外圍設備...");[self writeToLog:@"開始連接外圍設備..."];[self.centralManager connectPeripheral:peripheral options:nil];}} //連接到外圍設備 -(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{NSLog(@"連接外圍設備成功!");[self writeToLog:@"連接外圍設備成功!"];//設置外圍設備的代理為當前視圖控制器peripheral.delegate=self;//外圍設備開始尋找服務[peripheral discoverServices:@[[CBUUID UUIDWithString:kServiceUUID]]]; } //連接外圍設備失敗 -(void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{NSLog(@"連接外圍設備失敗!");[self writeToLog:@"連接外圍設備失敗!"]; }#pragma mark - CBPeripheral 代理方法 //外圍設備尋找到服務后 -(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{NSLog(@"已發現可用服務...");[self writeToLog:@"已發現可用服務..."];if(error){NSLog(@"外圍設備尋找服務過程中發生錯誤,錯誤信息:%@",error.localizedDescription);[self writeToLog:[NSString stringWithFormat:@"外圍設備尋找服務過程中發生錯誤,錯誤信息:%@",error.localizedDescription]];}//遍歷查找到的服務CBUUID *serviceUUID=[CBUUID UUIDWithString:kServiceUUID];CBUUID *characteristicUUID=[CBUUID UUIDWithString:kCharacteristicUUID];for (CBService *service in peripheral.services) {if([service.UUID isEqual:serviceUUID]){//外圍設備查找指定服務中的特征[peripheral discoverCharacteristics:@[characteristicUUID] forService:service];}} } //外圍設備尋找到特征后 -(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{NSLog(@"已發現可用特征...");[self writeToLog:@"已發現可用特征..."];if (error) {NSLog(@"外圍設備尋找特征過程中發生錯誤,錯誤信息:%@",error.localizedDescription);[self writeToLog:[NSString stringWithFormat:@"外圍設備尋找特征過程中發生錯誤,錯誤信息:%@",error.localizedDescription]];}//遍歷服務中的特征CBUUID *serviceUUID=[CBUUID UUIDWithString:kServiceUUID];CBUUID *characteristicUUID=[CBUUID UUIDWithString:kCharacteristicUUID];if ([service.UUID isEqual:serviceUUID]) {for (CBCharacteristic *characteristic in service.characteristics) {if ([characteristic.UUID isEqual:characteristicUUID]) {//情景一:通知/*找到特征后設置外圍設備為已通知狀態(訂閱特征):*1.調用此方法會觸發代理方法:-(void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error*2.調用此方法會觸發外圍設備的訂閱代理方法*/[peripheral setNotifyValue:YES forCharacteristic:characteristic];//情景二:讀取 // [peripheral readValueForCharacteristic:characteristic]; // if(characteristic.value){ // NSString *value=[[NSString alloc]initWithData:characteristic.value encoding:NSUTF8StringEncoding]; // NSLog(@"讀取到特征值:%@",value); // }}}} } //特征值被更新后 -(void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{NSLog(@"收到特征更新通知...");[self writeToLog:@"收到特征更新通知..."];if (error) {NSLog(@"更新通知狀態時發生錯誤,錯誤信息:%@",error.localizedDescription);}//給特征值設置新的值CBUUID *characteristicUUID=[CBUUID UUIDWithString:kCharacteristicUUID];if ([characteristic.UUID isEqual:characteristicUUID]) {if (characteristic.isNotifying) {if (characteristic.properties==CBCharacteristicPropertyNotify) {NSLog(@"已訂閱特征通知.");[self writeToLog:@"已訂閱特征通知."];return;}else if (characteristic.properties ==CBCharacteristicPropertyRead) {//從外圍設備讀取新值,調用此方法會觸發代理方法:-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error[peripheral readValueForCharacteristic:characteristic];}}else{NSLog(@"停止已停止.");[self writeToLog:@"停止已停止."];//取消連接[self.centralManager cancelPeripheralConnection:peripheral];}} } //更新特征值后(調用readValueForCharacteristic:方法或者外圍設備在訂閱后更新特征值都會調用此代理方法) -(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{if (error) {NSLog(@"更新特征值時發生錯誤,錯誤信息:%@",error.localizedDescription);[self writeToLog:[NSString stringWithFormat:@"更新特征值時發生錯誤,錯誤信息:%@",error.localizedDescription]];return;}if (characteristic.value) {NSString *value=[[NSString alloc]initWithData:characteristic.value encoding:NSUTF8StringEncoding];NSLog(@"讀取到特征值:%@",value);[self writeToLog:[NSString stringWithFormat:@"讀取到特征值:%@",value]];}else{NSLog(@"未發現特征值.");[self writeToLog:@"未發現特征值."];} }#pragma mark - 屬性 -(NSMutableArray *)peripherals{if(!_peripherals){_peripherals=[NSMutableArray array];}return _peripherals; }#pragma mark - 私有方法 /*** 記錄日志** @param info 日志信息*/ -(void)writeToLog:(NSString *)info{self.log.text=[NSString stringWithFormat:@"%@\r\n%@",self.log.text,info]; }@endCBPeripheralManager 外設模式
使用手機作為外設連接其他中心設備操作的場景。
PS:因為蘋果設備的安全性和封閉性,蘋果設備不能通過與其他設備藍牙鏈接進行文件傳輸等功能,所以在iOS與藍牙開發的編程中是CBCentralMannager 中心模式編程居多.
1 建立外設角色
2 設置本地外設的服務和特征
3 發布外設和特征
4 廣播服務
5 響應中心的讀寫請求
6 發送更新的特征值,訂閱中心
藍牙連接發送數據的流程圖
lightblue怎么用
OTA(空中升級)
將手機作為外圍設備開發
iOS淺析藍牙設備之服務器(外圍設備)
APP被作為外設
遇到的坑
所以得知道 設備廣播的的UUID
或者用下面代碼搜索藍牙
一般藍牙連接失敗我們會實現以下方法
// 連接失敗(但不包含超時,系統沒有超時處理) - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {if (error && self.delegate && [self.delegate respondsToSelector:@selector(centralTool:connectFailure:)]) {[self.delegate centralTool:self connectFailure:error];} }- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {[self autoConnect]; }但是當廣播設備的藍牙斷開再想重連的話還要實現下面這個方法
- (void)peripheral:(CBPeripheral *)peripheral didModifyServices:(NSArray<CBService *> *)invalidatedServices{[self.centralManager connectPeripheral:peripheral options:nil];// 注意保留 Peripheral 的引用self.lastConnectedPeripheral = peripheral;[self startTimer]; }只有這樣才能實現斷開藍牙重開藍牙秒連
另外,有時候要提高搜索效率的話,可以過濾藍牙的名字
NSString *name = peripheral.name;if (name.length == 0) {return;}NSLog(@"name === %@",name);NSString *string = [NSString stringWithFormat:@"已發現 peripheral: %@ rssi: %@, UUID: %@ advertisementData: %@ ", peripheral, RSSI, peripheral.identifier, advertisementData];NSLog(@"string == %@",string);if ([name rangeOfString:@"Berry"].location == NSNotFound) {return;}// NSString *kCBAdvDataLocalName = [advertisementData objectForKey:CBAdvertisementDataLocalNameKey]; // if ([kCBAdvDataLocalName rangeOfString:@"Berry"].location == NSNotFound) { // return; // }不過要注意 上文代碼中的name 和 kCBAdvDataLocalName 是不同的
一個是藍牙設備的名字, 一個是廣播的名字
第一個坑中設置UUID找不到的問題就是,需要在上述代碼中設置UUID
網上找的大部分資料都是關于如何設置中心設備的,外設的比較少,所以這里容易出錯
不過,如果這塊都設置了,對于快速精準找到目標藍牙服務是大有裨益的,也會減少耗電量,提高響應速度等等。
總結
- 上一篇: 【python】内建异常类的层次
- 下一篇: 如何安装matlab2016b