學習ios藍牙技術,仿寫lightblue


上一次我們研究完iBeacon,發現iBeacon是基於藍牙4.0的一個封裝而已。那么,今天我們來研究iOS的藍牙4.0的應用。最出名的app當屬lightblue,我們不妨來仿寫一個lightblue,這樣基本的ios藍牙編程就算入門了。

基本理論

框架與概念

在ios中使用藍牙技術,會用到CoreBluetooth框架。

里面對設備有2個定義:周邊(peripeheral)設備 與 中央(central)設備。發送藍牙信號的是周邊設備,接收藍牙信號的是中央設備。

可以這樣理解,周邊設備是服務端,中央設備是客戶端。中央設備可以去搜索周邊有哪些服務端,可以選擇連接上其中一台,進行信息獲取。

支持藍牙4.0的手機,可以作為周邊設備,也可以作為中央設備,但是不能同時既為周邊設備又為中央設備。

類解讀

中央設備用 CBCentralManager 這個類管理。

周邊設備用 CBPeripheralManager 這個類管理;

周邊設備里面還有服務類 CBService,服務里面有各種各樣的特性類 CBCharacteristic

仿寫lightblue

基本流程

  1. 假設我們有2台以上可用設備。
  2. 其中一台作為調試機,用來搜索其它設備,並連接上去。所以,是中央設備central
  3. 其它設備設置為藍牙發射器,即是周邊設備peripheral
  4. 調試機先掃描周邊設備,用UITableView展示所掃描到的周邊設備。
  5. 點擊其中一台設備,進行連接connect
  6. 連接上后,獲取其中的所有服務services
  7. 對其中每個服務進行遍歷,獲取所有的特性Characteristic
  8. 讀取每個特性,獲取每個特性的值value

至此,lightblue基本的仿寫思路就清晰列出來了。

1. 掃描設備

先包含頭文件

1
#import <CoreBluetooth/CoreBluetooth.h> 

然后添加協議 CBCentralManagerDelegate

接着定義2個屬性, CBCentralManager用來管理我們的中央設備,NSMutableArray用來保存掃描出來的周邊設備。

1
2 
 @property (nonatomic, strong) CBCentralManager *centralMgr;  @property (nonatomic, strong) NSMutableArray *arrayBLE; 

中央設備創建很簡單,第一個參數代表 CBCentralManager 代理,第二個參數設置為nil,因為Peripheral Manager將Run在主線程中。如果你想用不同的線程做更加復雜的事情,你需要創建一個隊列(queue)並將它放在這兒。

1
2 
self.centralMgr = [[CBCentralManager alloc] initWithDelegate:self queue:nil]; self.arrayBLE = [[NSMutableArray alloc] init]; 

實現centralManagerDidUpdateState。當Central Manager被初始化,我們要檢查它的狀態,以檢查運行這個App的設備是不是支持BLE。

1
2 3 4 5 6 7 8 9 10 11 12 13 
 - (void)centralManagerDidUpdateState:(CBCentralManager *)central  {  switch (central.state)  {  case CBCentralManagerStatePoweredOn:  [self.centralMgr scanForPeripheralsWithServices:nil options:nil];  break;   default:  NSLog(@"Central Manager did change state");  break;  }  } 

-scanForPeripheralsWithServices:options:方法是中央設備開始掃描,可以設置為特定UUID來指,來差找一個指定的服務了。我們需要掃描周邊所有設備,第一個參數設置為nil。

當發起掃描之后,我們需要實現 centralManager:didDiscoverPeripheral:advertisementData:RSSI: 通過該回調來獲取發現設備。

這個回調說明着廣播數據和信號質量(RSSI-Received Signal Strength Indicator)的周邊設備被發現。通過信號質量,可以用判斷周邊設備離中央設備的遠近。

1
2 3 4 5 6 7 8 9 
 - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI  {  BLEInfo *discoveredBLEInfo = [[BLEInfo alloc] init];  discoveredBLEInfo.discoveredPeripheral = peripheral;  discoveredBLEInfo.rssi = RSSI;   // update tableview  [self saveBLE:discoveredBLEInfo];  } 

BLEInfo是我新建的一個類,用來存儲周邊設備信息的,具體如下:

1
2 3 4 5 6 
 @interface BLEInfo : NSObject   @property (nonatomic, strong) CBPeripheral *discoveredPeripheral;  @property (nonatomic, strong) NSNumber *rssi;   @end 

保存周邊設備信息,並把它們顯示到UITableView上:

1
2 3 4 5 6 7 8 9 10 11 12 13 14 
 - (BOOL)saveBLE:(BLEInfo *)discoveredBLEInfo  {  for (BLEInfo *info in self.arrayBLE)  {  if ([info.discoveredPeripheral.identifier.UUIDString isEqualToString:discoveredBLEInfo.discoveredPeripheral.identifier.UUIDString])  {  return NO;  }  }   [self.arrayBLE addObject:discoveredBLEInfo];  [self.tableView reloadData];  return YES;  } 

掃描到的周邊設備展示如下:

掃描到的周邊設備掃描到的周邊設備

2. 連接設備

當我們點擊其中一個設備,嘗試進行連接。lightblue是點擊后就立馬連接的,然后在下一個UITableView來展示該周邊設備的服務與特性。

而我是進入下一頁UITableView才開始連接,差別不大。但是注意的是,一定要把我們之前的self.centralMgr傳遞到下一頁的UITableView來使用,並且重新設置delegate。

用來展示服務和特性的UITableViewController:

1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 
 #import <UIKit/UIKit.h>  #import <CoreBluetooth/CoreBluetooth.h>   @interface BLEInfoTableViewController : UITableViewController  <  CBPeripheralManagerDelegate,  CBCentralManagerDelegate,  CBPeripheralDelegate  >   @property (nonatomic, strong) CBCentralManager *centralMgr;  @property (nonatomic, strong) CBPeripheral *discoveredPeripheral;   // tableview sections,保存藍牙設備里面的services字典,字典第一個為service,剩下是特性與值  @property (nonatomic, strong) NSMutableArray *arrayServices;   // 用來記錄有多少特性,當全部特性保存完畢,刷新列表  @property (atomic, assign) int characteristicNum;   @end 

記得把之前的centrlMgr傳過來,記得要重新設置delegate:

1
2 3 4 5 6 7 8 9 10 11 12 
- (void)viewDidLoad {  [super viewDidLoad];   [_centralMgr setDelegate:self];  if (_discoveredPeripheral)  {  [_centralMgr connectPeripheral:_discoveredPeripheral options:nil];  }  _arrayServices = [[NSMutableArray alloc] init];  _characteristicNum = 0; } 

其中,

[centralMgr connectPeripheral:discoveredPeripheral options:nil];

就是中央設備向周邊設備發起連接。

我們可以實現下面的函數,如果連接失敗,就會得到回調:

1
2 3 4 
 - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error  {  NSLog(@"didFailToConnectPeripheral : %@", error.localizedDescription);  } 

我們必須實現didConnectPeripheral,只要連接成功,就能回調到該函數,開始獲取服務。

1
2 3 4 5 6 7 8 9 
 - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral  {   [self.arrayServices removeAllObjects];   [_discoveredPeripheral setDelegate:self];   [_discoveredPeripheral discoverServices:nil];  } 

discoverServices就是查找該周邊設備的服務。

3. 獲取服務

當找到了服務之后,就能進入didDiscoverServices的回調。我們把全部服務都保存起來。

1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 
 - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error  {  if (error)  {  NSLog(@"didDiscoverServices : %@", [error localizedDescription]);  // [self cleanup];  return;  }   for (CBService *s in peripheral.services)  {  NSLog(@"Service found with UUID : %@", s.UUID);  NSMutableDictionary *dic = [[NSMutableDictionary alloc] initWithDictionary:@{SECTION_NAME:s.UUID.description}];  [self.arrayServices addObject:dic];  [s.peripheral discoverCharacteristics:nil forService:s];  }  } 

4. 獲取特性

我們通過discoverCharacteristics來獲取每個服務下的特性,通過下面的回調來獲取。

1
2 3 4 5 6 7 8 9 10 11 12 13 14 
 - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error  {  if (error)  {  NSLog(@"didDiscoverCharacteristicsForService error : %@", [error localizedDescription]);  return;  }   for (CBCharacteristic *c in service.characteristics)  {  self.characteristicNum++;  [peripheral readValueForCharacteristic:c];  }  } 

5. 獲取特性值

readValueForCharacteristic可以讀取特性的值。

通過下面的回調,就能得到特性值。

1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 
 - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error  {  self.characteristicNum--;  if (self.characteristicNum == 0)  {  [self.tableView reloadData];  }   if (error)  {  NSLog(@"didUpdateValueForCharacteristic error : %@", error.localizedDescription);  return;  }   NSString *stringFromData = [[NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding];   if ([stringFromData isEqualToString:@"EOM"])  {  NSLog(@"the characteristic text is END");  // [peripheral setNotifyValue:NO forCharacteristic:characteristic];  // [self.centralMgr cancelPeripheralConnection:peripheral];  }   for (NSMutableDictionary *dic in self.arrayServices)  {  NSString *service = [dic valueForKey:SECTION_NAME];  if ([service isEqual:characteristic.service.UUID.description])  {  NSLog(@"characteristic.description : %@", characteristic.UUID.description);  [dic setValue:characteristic.value forKey:characteristic.UUID.description];  }  }  } 

連接到周邊設備獲得的藍牙信息連接到周邊設備獲得的藍牙信息

其它

本來蘋果是提供了xcode5.0加ios7的模擬器來實現模擬器開啟藍牙的,本來連文章都給出了:https://developer.apple.com/library/ios/technotes/tn2295/_index.html

后來蘋果把這文章給刪了,還把ios7模擬器支持開啟藍牙給去掉。

那么,可以通過這個文章http://blog.csdn.net/zhenyu5211314/article/details/24399887,使用6.0的模擬器來調試。

參考文章

iOS CoreBluetooth 教程

藍牙 BLE CoreBluetooth 初探

藍牙4.0 For IOS


免責聲明!

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



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