前言
上一次我們研究完iBeacon,,發(fā)現(xiàn)iBeacon是基于藍牙4.0的一個封裝而已,。那么,今天我們來研究ios的藍牙4.0的應(yīng)用,。最出名的app當(dāng)屬lightblue,,我們不妨來仿寫一個lightblue,這樣基本的ios藍牙編程就算入門了,。
基本理論
框架與概念
在ios中使用藍牙技術(shù),,會用到CoreBluetooth框架。
里面對設(shè)備有2個定義:周邊(peripeheral)設(shè)備 與 中央(central)設(shè)備,。發(fā)送藍牙信號的是周邊設(shè)備,,接收藍牙信號的是中央設(shè)備。
可以這樣理解,,周邊設(shè)備是服務(wù)端,,中央設(shè)備是客戶端。中央設(shè)備可以去搜索周邊有哪些服務(wù)端,,可以選擇連接上其中一臺,,進行信息獲取。
支持藍牙4.0的手機,,可以作為周邊設(shè)備,,也可以作為中央設(shè)備,但是不能同時既為周邊設(shè)備又為中央設(shè)備,。
類解讀
中央設(shè)備用 CBCentralManager 這個類管理,。
周邊設(shè)備用 CBPeripheralManager 這個類管理;
周邊設(shè)備里面還有服務(wù)類 CBService ,,服務(wù)里面有各種各樣的特性類 CBCharacteristic ,。
仿寫lightblue
基本流程
- 假設(shè)我們有2臺以上可用設(shè)備。
- 其中一臺作為調(diào)試機,,用來搜索其它設(shè)備,,并連接上去。所以,,是中央設(shè)備
central ,。
- 其它設(shè)備設(shè)置為藍牙發(fā)射器,即是周邊設(shè)備
peripheral ,。
- 調(diào)試機先掃描周邊設(shè)備,,用UITableView展示所掃描到的周邊設(shè)備。
- 點擊其中一臺設(shè)備,,進行連接
connect ,。
- 連接上后,獲取其中的所有服務(wù)
services ,。
- 對其中每個服務(wù)進行遍歷,,獲取所有的特性
Characteristic 。
- 讀取每個特性,,獲取每個特性的值
value ,。
至此,lightblue基本的仿寫思路就清晰列出來了,。
1. 掃描設(shè)備
先包含頭文件
1
| #import <CoreBluetooth/CoreBluetooth.h>
|
然后添加協(xié)議 CBCentralManagerDelegate
接著定義2個屬性,, CBCentralManager用來管理我們的中央設(shè)備,NSMutableArray用來保存掃描出來的周邊設(shè)備,。
1
2
| @property (nonatomic, strong) CBCentralManager *centralMgr;
@property (nonatomic, strong) NSMutableArray *arrayBLE;
|
中央設(shè)備創(chuàng)建很簡單,,第一個參數(shù)代表 CBCentralManager 代理,第二個參數(shù)設(shè)置為nil,,因為Peripheral Manager將Run在主線程中,。如果你想用不同的線程做更加復(fù)雜的事情,你需要創(chuàng)建一個隊列(queue)并將它放在這兒,。
1
2
| self.centralMgr = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
self.arrayBLE = [[NSMutableArray alloc] init];
|
實現(xiàn)centralManagerDidUpdateState ,。當(dāng)Central Manager被初始化,我們要檢查它的狀態(tài),,以檢查運行這個App的設(shè)備是不是支持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: 方法是中央設(shè)備開始掃描,可以設(shè)置為特定UUID來指,,來差找一個指定的服務(wù)了,。我們需要掃描周邊所有設(shè)備,第一個參數(shù)設(shè)置為nil,。
當(dāng)發(fā)起掃描之后,,我們需要實現(xiàn)
centralManager:didDiscoverPeripheral:advertisementData:RSSI:
通過該回調(diào)來獲取發(fā)現(xiàn)設(shè)備。
這個回調(diào)說明著廣播數(shù)據(jù)和信號質(zhì)量(RSSI-Received Signal Strength Indicator)的周邊設(shè)備被發(fā)現(xiàn),。通過信號質(zhì)量,,可以用判斷周邊設(shè)備離中央設(shè)備的遠近。
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是我新建的一個類,,用來存儲周邊設(shè)備信息的,,具體如下:
1
2
3
4
5
6
| @interface BLEInfo : NSObject
@property (nonatomic, strong) CBPeripheral *discoveredPeripheral;
@property (nonatomic, strong) NSNumber *rssi;
@end
|
保存周邊設(shè)備信息,并把它們顯示到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;
}
|
掃描到的周邊設(shè)備展示如下:
2. 連接設(shè)備
當(dāng)我們點擊其中一個設(shè)備,,嘗試進行連接,。lightblue是點擊后就立馬連接的,,然后在下一個UITableView來展示該周邊設(shè)備的服務(wù)與特性。
而我是進入下一頁UITableView才開始連接,,差別不大,。但是注意的是,一定要把我們之前的self.centralMgr傳遞到下一頁的UITableView來使用,,并且重新設(shè)置delegate,。
用來展示服務(wù)和特性的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,保存藍牙設(shè)備里面的services字典,,字典第一個為service,,剩下是特性與值
@property (nonatomic, strong) NSMutableArray *arrayServices;
// 用來記錄有多少特性,當(dāng)全部特性保存完畢,,刷新列表
@property (atomic, assign) int characteristicNum;
@end
|
記得把之前的centrlMgr傳過來,,記得要重新設(shè)置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];
就是中央設(shè)備向周邊設(shè)備發(fā)起連接,。
我們可以實現(xiàn)下面的函數(shù),,如果連接失敗,就會得到回調(diào):
1
2
3
4
| - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
NSLog(@"didFailToConnectPeripheral : %@", error.localizedDescription);
}
|
我們必須實現(xiàn)didConnectPeripheral ,,只要連接成功,,就能回調(diào)到該函數(shù),開始獲取服務(wù),。
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 就是查找該周邊設(shè)備的服務(wù),。
3. 獲取服務(wù)
當(dāng)找到了服務(wù)之后,就能進入didDiscoverServices 的回調(diào),。我們把全部服務(wù)都保存起來,。
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 來獲取每個服務(wù)下的特性,通過下面的回調(diào)來獲取,。
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 可以讀取特性的值,。
通過下面的回調(diào),就能得到特性值,。
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的模擬器來實現(xiàn)模擬器開啟藍牙的,,本來連文章都給出了:https://developer.apple.com/library/ios/technotes/tn2295/_index.html
后來蘋果把這文章給刪了,還把ios7模擬器支持開啟藍牙給去掉,。
那么,,可以通過這個文章http://blog.csdn.net/zhenyu5211314/article/details/24399887,使用6.0的模擬器來調(diào)試。
參考文章
iOS CoreBluetooth 教程
藍牙 BLE CoreBluetooth 初探
藍牙4.0 For IOS
|