iOS的藍(lán)牙數(shù)據(jù)接收以及發(fā)送
- 名詞:Central(中心設(shè)備)臭笆、Peripheral(外圍設(shè)備)、advertising(廣告)秤掌、Services(服務(wù))愁铺、Characteristic(特征)
- 新建Central Manager實(shí)例進(jìn)行藍(lán)牙管理
- 搜索外圍設(shè)備
- 連接外圍設(shè)備
- 獲得外圍設(shè)備的服務(wù)
- 獲得服務(wù)的特征
- 給外圍設(shè)備發(fā)送數(shù)據(jù)
- 從外圍設(shè)備讀數(shù)據(jù)
藍(lán)牙介紹
本文要介紹的CoreBluetooth,專門用于與BLE設(shè)備通訊。并且現(xiàn)在很多藍(lán)牙設(shè)備都支持4.0,4.0以其低功耗著稱闻鉴,所以一般也叫BLE(Bluetoothlow energy)茵乱,所以也是在iOS比較推薦的一種開(kāi)發(fā)方法。
CoreBluetooth介紹
在CoreBluetooth中有兩個(gè)主要的部分,Central和Peripheral孟岛,CBPeripheralManager 作為周邊設(shè)備瓶竭。CBCentralManager作為中心設(shè)備督勺。所有可用的iOS設(shè)備可以作為周邊(Peripheral)也可以作為中央(Central),但不可以同時(shí)既是周邊也是中央在验。
- 周邊設(shè)備(Peripheral)設(shè)備是廣播設(shè)備的數(shù)據(jù)玷氏,中央設(shè)備(Central)是管理并且使用這些數(shù)據(jù)的設(shè)備。
- 也就是說(shuō)周邊(Peripheral)向周圍發(fā)送廣播腋舌,告訴周圍的中央設(shè)備(Central)它(周邊(Peripheral)這里有數(shù)據(jù)盏触,并且說(shuō)明了能提供的服務(wù)和特征值(連接之后才能獲取)块饺,
- 其實(shí)藍(lán)牙傳值相當(dāng)于網(wǎng)絡(luò)接口赞辩,硬件的service的UUID加上characteristic的UUID,
打一個(gè)比喻:service的UUID相當(dāng)于主地址授艰,characteristic的UUID相當(dāng)于短鏈接辨嗽,短鏈接必須是主地址的分支,拼在一起的是接口淮腾,你和硬件設(shè)定的藍(lán)牙傳輸格式類似于json糟需,雙方可識(shí)別的數(shù)據(jù),因?yàn)樗{(lán)牙只能支持16進(jìn)制谷朝,而且每次傳輸只能20個(gè)字節(jié)洲押,所以要把信息流轉(zhuǎn)成雙方可識(shí)別的16進(jìn)制
實(shí)現(xiàn)方法介紹
- .h 導(dǎo)入頭文件
#import <CoreBluetooth/CoreBluetooth.h>
- 自定義設(shè)置枚舉狀態(tài)
typedef NS_ENUM(NSInteger, BluetoothState){
BluetoothStateDisconnect = 0,
BluetoothStateScanSuccess,
BluetoothStateScaning,
BluetoothStateConnected,
BluetoothStateConnecting
};
typedef NS_ENUM(NSInteger, BluetoothFailState){
BluetoothFailStateUnExit = 0,
BluetoothFailStateUnKnow,
BluetoothFailStateByHW,
BluetoothFailStateByOff,
BluetoothFailStateUnauthorized,
BluetoothFailStateByTimeout
};
- 設(shè)置代理
<CBCentralManagerDelegate,CBPeripheralDelegate>
- 添加屬性
@property (strong , nonatomic) UITableView *tableView;
@property (strong , nonatomic) CBCentralManager *manager;//中央設(shè)備
@property (assign , nonatomic) BluetoothFailState bluetoothFailState;
@property (assign , nonatomic) BluetoothState bluetoothState;
@property (strong , nonatomic) CBPeripheral * discoveredPeripheral;//周邊設(shè)備
@property (strong , nonatomic) CBCharacteristic *characteristic1;//周邊設(shè)備服務(wù)特性
@property (strong , nonatomic) NSMutableArray *BleViewPerArr;
- 創(chuàng)建UITableView
-(void)setTableView{
_tableView = [[UITableView alloc]initWithFrame:CGRectMake(0, 20, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height) style:UITableViewStyleGrouped];
_tableView.delegate = self;
_tableView.dataSource = self;
_tableView.backgroundColor = [UIColor whiteColor];
[self.view addSubview:_tableView];
}
- 接下來(lái)寫(xiě)tableView的dalegate
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:@"IsConnect"];
if (cell == nil) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"IsConnect"];
}
// 將藍(lán)牙外設(shè)對(duì)象接出,取出name圆凰,顯示
//藍(lán)牙對(duì)象在下面環(huán)節(jié)會(huì)查找出來(lái)杈帐,被放進(jìn)BleViewPerArr數(shù)組里面,是CBPeripheral對(duì)象
CBPeripheral *per=(CBPeripheral *)_BleViewPerArr[indexPath.row];
NSString *bleName=[per.name substringWithRange:NSMakeRange(0, 9)];
cell.textLabel.text = per.name;
return cell;
}
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
return 44;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return _BleViewPerArr.count;
}
- 創(chuàng)建實(shí)例,設(shè)置代理,創(chuàng)建數(shù)組管理外設(shè)专钉,
self.manager = [[CBCentralManager alloc]initWithDelegate:self queue:dispatch_get_main_queue()];
self.manager.delegate = self;
self.BleViewPerArr = [[NSMutableArray alloc]initWithCapacity:1];
- 開(kāi)始掃描
-(void)scan{
//判斷狀態(tài)開(kāi)始掃瞄周圍設(shè)備 第一個(gè)參數(shù)為空則會(huì)掃瞄所有的可連接設(shè)備 你可以
//指定一個(gè)CBUUID對(duì)象 從而只掃瞄注冊(cè)用指定服務(wù)的設(shè)備
//scanForPeripheralsWithServices方法調(diào)用完后會(huì)調(diào)用代理CBCentralManagerDelegate的
//- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI方法
[self.manager scanForPeripheralsWithServices:nil options:@{ CBCentralManagerScanOptionAllowDuplicatesKey : @NO }];
//記錄目前是掃描狀態(tài)
_bluetoothState = BluetoothStateScaning;
//清空所有外設(shè)數(shù)組
[self.BleViewPerArr removeAllObjects];
//如果藍(lán)牙狀態(tài)未開(kāi)啟挑童,提示開(kāi)啟藍(lán)牙
if(_bluetoothFailState==BluetoothFailStateByOff)
{
NSLog(@"%@",@"檢查您的藍(lán)牙是否開(kāi)啟后重試");
}
}
- 接下來(lái)會(huì)檢測(cè)藍(lán)牙狀態(tài)
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
if (central.state != CBCentralManagerStatePoweredOn) {
NSLog(@"fail, state is off.");
switch (central.state) {
case CBCentralManagerStatePoweredOff:
NSLog(@"連接失敗了\n請(qǐng)您再檢查一下您的手機(jī)藍(lán)牙是否開(kāi)啟,\n然后再試一次吧");
_bluetoothFailState = BluetoothFailStateByOff;
break;
case CBCentralManagerStateResetting:
_bluetoothFailState=BluetoothFailStateByTimeout;
break;
case CBCentralManagerStateUnsupported:
NSLog(@"檢測(cè)到您的手機(jī)不支持藍(lán)牙4.0\n所以建立不了連接.建議更換您\n的手機(jī)再試試跃须。");
_bluetoothFailState = BluetoothFailStateByHW;
break;
case CBCentralManagerStateUnauthorized:
NSLog(@"連接失敗了\n請(qǐng)您再檢查一下您的手機(jī)藍(lán)牙是否開(kāi)啟站叼,\n然后再試一次吧");
_bluetoothFailState = BluetoothFailStateUnauthorized;
break;
case CBCentralManagerStateUnknown:
_bluetoothFailState = BluetoothFailStateUnKnow;
break;
default:
break;
}
return;
}
_bluetoothFailState = BluetoothFailStateUnExit;
// ... so start scanning
}
- 中央設(shè)備開(kāi)始掃描之后,我們需要實(shí)現(xiàn)
centralManager:didDiscoverPeripheral:advertisementData:RSSI:
通過(guò)該回調(diào)來(lái)獲取發(fā)現(xiàn)設(shè)備菇民。
這個(gè)回調(diào)說(shuō)明著廣播數(shù)據(jù)和信號(hào)質(zhì)量(RSSI-Received Signal Strength Indicator)的周邊設(shè)備被發(fā)現(xiàn)尽楔。通過(guò)信號(hào)質(zhì)量,可以用判斷周邊設(shè)備離中央設(shè)備的遠(yuǎn)近玉雾。
- (void)centralManager:(CBCentralManager *)central
didDiscoverPeripheral:(CBPeripheral *)peripheral
advertisementData:(NSDictionary *)advertisementData
RSSI:(NSNumber *)RSSI
{
if (peripheral == nil||peripheral.identifier == nil/*||peripheral.name == nil*/)
{
return;
}
NSString *pername=[NSString stringWithFormat:@"%@",peripheral.name];
//判斷是否存在@"你的設(shè)備名"
NSRange range=[pername rangeOfString:@"你的設(shè)備名"];
//如果從搜索到的設(shè)備中找到指定設(shè)備名,和BleViewPerArr數(shù)組沒(méi)有它的地址
//加入BleViewPerArr數(shù)組
if(range.location!=NSNotFound&&[_BleViewPerArr containsObject:peripheral]==NO){
[_BleViewPerArr addObject:peripheral];
}
_bluetoothFailState = BluetoothFailStateUnExit;
_bluetoothState = BluetoothStateScanSuccess;
[_tableView reloadData];
}
掃描設(shè)備輸出臺(tái)log:
<CBPeripheral: 0x14e625f80, identifier = 954DBF72-104A-E041-19F8-D9538DBA7C23, name = brand, state = disconnected>
藍(lán)牙廣播中可以攜帶一些信息轻要,最好將mac 地址也一并在這里廣播出來(lái)8囱!冲泥!
掃描設(shè)備advertisementData 輸出臺(tái)log :
Printing description of advertisementData:
{
kCBAdvDataIsConnectable = 1;
kCBAdvDataLocalName = "brand";
kCBAdvDataTxPowerLevel = 2;
}
- 掃描到設(shè)備之后當(dāng)然是鏈接設(shè)備了,這里會(huì)有一個(gè)UITableView驹碍,在tableview點(diǎn)擊方法里寫(xiě)連接方法
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
CBPeripheral *peripheral=(CBPeripheral *)_BleViewPerArr[indexPath.row];
//設(shè)定周邊設(shè)備壁涎,指定代理者
_discoveredPeripheral = peripheral;
_discoveredPeripheral.delegate = self;
//連接設(shè)備
[_manager connectPeripheral:peripheral
options:@{CBConnectPeripheralOptionNotifyOnConnectionKey:@YES}];
}
說(shuō)明 : 點(diǎn)擊單元格連接對(duì)應(yīng)的設(shè)備,連接該設(shè)備 調(diào)用完該方法后會(huì)調(diào)用代理- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
表示連接上了設(shè)備
連接失敗會(huì)調(diào)用- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
- 已經(jīng)連接上該設(shè)備志秃,就可以獲取當(dāng)前設(shè)備的信息
// 獲取當(dāng)前設(shè)備
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
NSLog(@"%@",peripheral);
// 設(shè)置設(shè)備代理
[peripheral setDelegate:self];
// 大概獲取服務(wù)和特征
[peripheral discoverServices:nil];
//或許只獲取你的設(shè)備藍(lán)牙服務(wù)的uuid數(shù)組怔球,一個(gè)或者多個(gè)
//[peripheral discoverServices:@[[CBUUID UUIDWithString:@""],[CBUUID UUIDWithString:@""]]];
NSLog(@"Peripheral Connected");
[_manager stopScan];
NSLog(@"Scanning stopped");
_bluetoothState=BluetoothStateConnected;
}
- 各種服務(wù)
說(shuō)明:在這個(gè)方法中我們要查找到我們需要的服務(wù) 然后調(diào)用discoverCharacteristics方法查找我們需要的特性
該discoverCharacteristics方法調(diào)用完后會(huì)調(diào)用代理CBPeripheralDelegate的- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
// 獲取當(dāng)前設(shè)備服務(wù)services
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
if (error) {
NSLog(@"Error discovering services: %@", [error localizedDescription]);
return;
}
NSLog(@"所有的servicesUUID%@",peripheral.services);
//遍歷所有service
for (CBService *service in peripheral.services)
{
NSLog(@"服務(wù)%@",service.UUID);
//找到你需要的servicesuuid
if ([service.UUID isEqual:[CBUUID UUIDWithString:@"你的設(shè)備服務(wù)的uuid"]])
{
//監(jiān)聽(tīng)它
[peripheral discoverCharacteristics:nil forService:service];
}
}
NSLog(@"此時(shí)鏈接的peripheral:%@",peripheral);
}
- 特征獲取
說(shuō)明:在這個(gè)方法中我們要找到我們所需的服務(wù)的特性 然后調(diào)用setNotifyValue方法告知我們要監(jiān)測(cè)這個(gè)服務(wù)特性的狀態(tài)變化 當(dāng)setNotifyValue方法調(diào)用后調(diào)用代理CBPeripheralDelegate的- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
if (error)
{
NSLog(@"Discovered characteristics for %@ with error: %@", service.UUID, [error localizedDescription]);
return;
}
NSLog(@"服務(wù):%@",service.UUID);
// 特征
for (CBCharacteristic *characteristic in service.characteristics)
{
NSLog(@"%@",characteristic.UUID);
//發(fā)現(xiàn)特征
//注意:uuid 分為可讀,可寫(xiě)浮还,要區(qū)別對(duì)待>固场!钧舌!
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"你的特征uuid"]])
{
NSLog(@"監(jiān)聽(tīng):%@",characteristic);//監(jiān)聽(tīng)特征
//保存characteristic特征值對(duì)象
//以后發(fā)信息也是用這個(gè)uuid
_characteristic1 = characteristic;
[_discoveredPeripheral setNotifyValue:YES forCharacteristic:characteristic];
}
//當(dāng)然担汤,你也可以監(jiān)聽(tīng)多個(gè)characteristic特征值對(duì)象
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"你的特征uuid"]])
{
//同樣用一個(gè)變量保存,demo里面沒(méi)有聲明變量洼冻,要去聲明
// _characteristic2 = characteristic;
// [peripheral setNotifyValue:YES forCharacteristic:_characteristic2];
// NSLog(@"監(jiān)聽(tīng):%@",characteristic);//監(jiān)聽(tīng)特征
}
}
}
- 讀
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
if (error)
{
NSLog(@"Error updating value for characteristic %@ error: %@", characteristic.UUID, [error localizedDescription]);
return;
}
NSLog(@"收到的數(shù)據(jù):%@",characteristic.value);
}
藍(lán)牙傳值
藍(lán)牙寫(xiě)到這里崭歧,基本用法已經(jīng)說(shuō)明,代碼千變?nèi)f變撞牢,思路不變率碾,接下來(lái)介紹傳值,因?yàn)槲业捻?xiàng)目是手環(huán)屋彪,我就以我們硬件工程師的藍(lán)牙接口文檔來(lái)介紹如何和硬件交互所宰,
其實(shí)藍(lán)牙傳值相當(dāng)于網(wǎng)絡(luò)接口,硬件的service的UUID加上characteristic的UUID撼班,
打一個(gè)比喻:service的UUID相當(dāng)于主地址歧匈,characteristic的UUID相當(dāng)于短鏈接,短鏈接必須是主地址的分支砰嘁,拼在一起的是接口件炉,你和硬件設(shè)定的藍(lán)牙傳輸格式類似于json,雙方可識(shí)別的數(shù)據(jù)矮湘,因?yàn)樗{(lán)牙只能支持16進(jìn)制斟冕,而且每次傳輸只能20個(gè)字節(jié),所以要把信息流轉(zhuǎn)成雙方可識(shí)別的16進(jìn)制
- 下面是手環(huán)的接口文檔
APP請(qǐng)求運(yùn)動(dòng)模式基礎(chǔ)數(shù)據(jù)傳輸時(shí)拆分的總包數(shù)
字節(jié)序號(hào) | 參數(shù)值 |
---|---|
0 | 0xa5 |
1 | 0x06 |
2 | 0x03 |
3~4 | 2字節(jié)的時(shí)間缅阳,如1月2日用0x0102表示磕蛇。 |
5 | 異或校驗(yàn)和 |
由此看出:0、1十办、2字節(jié)都是固定的秀撇,3字節(jié)是月(16進(jìn)制),4字節(jié)是日(16進(jìn)制)向族,5字節(jié)是異或校驗(yàn)和
那么:
// APP請(qǐng)求運(yùn)動(dòng)模式基礎(chǔ)數(shù)據(jù)傳輸時(shí)拆分的總包數(shù)
- (NSData *)sportBao
{
Byte reg[6];
reg[0]=0xa5;
reg[1]=0x06;
reg[2]=0x03;
reg[3]=0x01;
reg[4]=0x02;
reg[5]=(Byte)(reg[0]^reg[1]^reg[2]^reg[3]^reg[4]);
NSData *data=[NSData dataWithBytes:reg length:6];
return data;
}
這時(shí)候呵燕,要把請(qǐng)求命令發(fā)送給手環(huán),發(fā)送給剛才紀(jì)錄的discoveredPeripheral的藍(lán)牙設(shè)備的characteristic1的特征值
// 獲取總包數(shù)
- (void)getAltogether
{
//生成總包數(shù)data
NSData *d1 = [self sportBao];
NSLog(@"寫(xiě)%@",d1);
NSLog(@"%@",discoveredPeripheral);
[discoveredPeripheral writeValue:d1 forCharacteristic:characteristic1 type:CBCharacteristicWriteWithResponse];
}
發(fā)送完成件相,手環(huán)會(huì)返回?cái)?shù)據(jù)
數(shù)據(jù)是以下格式:
手環(huán)返回運(yùn)動(dòng)模式基礎(chǔ)數(shù)據(jù)傳輸時(shí)拆分的總包數(shù)
字節(jié)序號(hào) | 參數(shù)值 |
---|---|
0 | 0xa5 |
1 | 0x06 |
2 | 0x83 |
3~4 | 該運(yùn)動(dòng)模式數(shù)據(jù)傳輸時(shí)拆分的總包數(shù) |
5 | 異或校驗(yàn)和 |
由此看出:0再扭、1氧苍、2字節(jié)都是固定的,3-4字節(jié)是總包數(shù)(16進(jìn)制)泛范,5字節(jié)是異或校驗(yàn)和
那么:我們需要轉(zhuǎn)換3-4字節(jié)的16進(jìn)制让虐,得到總包數(shù)
- (void)SetAltogether:(NSData *)DayData
{
Byte *testByte = (Byte *)[DayData bytes];
if (DayData.length == 6) {
//收到數(shù)據(jù)之后,要異或校驗(yàn)罢荡,看數(shù)據(jù)是否完整以及正確
if (testByte[5]==(testByte[0]^testByte[1]^testByte[2]^testByte[3]^testByte[4]))
{
// 這里記錄總包數(shù)
int totalBao = 0;
totalBao = testByte[4]*256+testByte[3];
}
}
}
解析數(shù)據(jù)的格式已經(jīng)有了赡突,那么在接收數(shù)據(jù)的時(shí)候
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
if (error)
{
NSLog(@"讀失敗");
return;
}
NSLog(@"收到的數(shù)據(jù) :%@",characteristic.value);
NSString *str = [NSString stringWithFormat:@"%@",characteristic.value];
// 運(yùn)動(dòng)總包數(shù)
if (str.length>7&&[[str substringWithRange:NSMakeRange(1, 2)]isEqualToString:@"a5"]&&[[str substringWithRange:NSMakeRange(5, 2)]isEqualToString:@"83"]) {
//調(diào)用解析總包數(shù)方法
[self SetAltogether:characteristic.value];
}
}
數(shù)據(jù)傳輸介紹完畢,基本用法已經(jīng)說(shuō)明