最近學習藍牙熄捍,所以找了一些資料學習研究了一下藍牙通訊的一個流程。寫了一個小demo,demo效果見下圖:
需要demo的朋友自行下載纬朝,如果有用处窥,請給star,謝謝
接下來我們講學習:
- 基礎知識了解
- app作為主設備,發(fā)送數據
- app作為從設備玄组,接收數據
1.基礎知識了解
藍牙5.0是由藍牙技術聯(lián)盟在2016年提出的藍牙技術標準滔驾,藍牙5.0針對低功耗設備速度有相應提升和優(yōu)化,藍牙5.0結合wifi對室內位置進行輔助定位俄讹,提高傳輸速度哆致,增加有效工作距離。百度百科
我們在開發(fā)中要使用CoreBluetooth框架
我們先學習幾個概念:
service and characteristic 服務和特征
- 每個設備都會提供服務和特征患膛,類似于服務端的api摊阀,但是由于結構不
同,每個外設會有很多的服務,每個服務中又包含很多字段踪蹬,這些字段的權限一般分為 讀read胞此,寫write,通知notiy幾種跃捣,就是我們連接設備后具體需要操作的內容漱牵。- service是characteristic的集合
- 一個characteristic包括一個單一變量和0-n個用來描述characteristic變量的descriptor奉芦,characteristic可以被認為是一個類型缩麸,類 似于類。
Description
每個characteristic可以對應一個或多個Description用戶描述characteristic的信息或屬性
CoreBluetooth框架
CoreBluetooth框架的核心其實是兩個東西全度,central和peripheral, 可以理解成中心和外設。對應他們分別有一組相關的API和類
外設-服務-特征
每個設備(CBPeripheral)都會有一些服務(CBService)闻镶,每個服務里面都會有一些特征(CBCharacteristic)甚脉,特征就是具體鍵值對,提供數據的地方铆农。每個特征屬性分為這么幾種:讀牺氨,寫,通知這么幾種方式
屬性列表
typedef NS_OPTIONS(NSUInteger, CBCharacteristicProperties) {
CBCharacteristicPropertyBroadcast = 0x01,
CBCharacteristicPropertyRead = 0x02,
CBCharacteristicPropertyWriteWithoutResponse = 0x04,
CBCharacteristicPropertyWrite = 0x08,
CBCharacteristicPropertyNotify = 0x10,
CBCharacteristicPropertyIndicate = 0x20,
CBCharacteristicPropertyAuthenticatedSignedWrites = 0x40,
CBCharacteristicPropertyExtendedProperties = 0x80,
CBCharacteristicPropertyNotifyEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0) = 0x100,
CBCharacteristicPropertyIndicateEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0) = 0x200
};
藍牙設備的幾種狀態(tài):
設備作為中心模式(central)流程:
設備作為外設模式(peripheral)流程:
2.app作為主設備墩剖,發(fā)送數據
注意:藍牙需要真機調試,所以必須要有真機
1) 導入 CoreBluetooth 框架,和頭文件
import <CoreBluetooth/CoreBluetooth.h>
兩個屬性:
@property(nonatomic,strong)CBCentralManager *manager;//主設備
@property(nonatomic,strong)NSMutableArray *peripherals;//被發(fā)現(xiàn)設備數組
初始化波闹,設置代理 CBCentralManagerDelegate
self.manager = [[CBCentralManager alloc]initWithDelegate:self queue:dispatch_get_main_queue()];
self.peripherals = @[].mutableCopy;
2) 實現(xiàn)CBCentralManagerDelegate相關的協(xié)議方法
a. 當設備開關藍牙 都會走這個回調
-(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");
//開始掃描周圍的外設
[manager scanForPeripheralsWithServices:nil options:nil];
//參數可以添加一些option,來增加精確的查找范圍, 如 :
// NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
// [NSNumber numberWithBool:YES], //CBCentralManagerScanOptionAllowDuplicatesKey,
// nil];
// [manager scanForPeripheralsWithServices:nil options:options];
*/
break;
default:
break;
}
}
b. 掃描到設備會進入這個方法,并連接外設(connect)
-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{
NSLog(@"當掃描到設備:%@",peripheral.name);
//接下來可以連接設備
//這里自己去設置下連接規(guī)則,我設置的是nicolas開頭的設備
if ([peripheral.name hasPrefix:@"nicolas"]){
//找到的設備必須持有它涛碑,否則CBCentralManager中也不會保存peripheral精堕,那么CBPeripheralDelegate中的方法也不會被調用!蒲障!
[self.peripherals addObject:peripheral];
//連接設備
[self.manager connectPeripheral:peripheral options:nil];
}
}
c. 連接外設(connect)后是否成功會進入下面的方法
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;//外設(Peripherals)連接成功的委托
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//外設(Peripherals)連接失敗的委托
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//斷開外設(Peripherals)的委托
3) 獲取外設的服務和特征
在設備連接成功后歹篓,我們需要設置外設Peripherals的委托(CBPeripheralDelegate),并開始獲取外設(Peripherals)的服務service和特征characteristic
CBPeripheralDelegate 的委托里包含了主設備與外設交互的許多 回調方法揉阎,包括獲取services庄撮,獲取characteristics,獲取characteristics的值毙籽,獲取characteristics的Descriptor洞斯,和Descriptor的值,寫數據坑赡,讀rssi烙如,用通知的方式訂閱數據等等。
//連接到Peripherals-成功回調
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{ [peripheral setDelegate:self];
[peripheral discoverServices:nil];
// 獲取service時候也可以設置option 添加指定條件可以 提高效率如:
// NSMutableArray *serviceUUIDs = [NSMutableArray array ];
//指定設備
// CBUUID *cbuuid = [CBUUID UUIDWithString:[NSString stringWithFormat:@"%x",Ble_Device_Service]];
// [serviceUUIDs addObject:cbuuid];
// [peripheral discoverServices:serviceUUIDs];
}
掃描到服務services
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
if (error)
{
return;
}
for (CBService *service in peripheral.services) {
NSLog(@"%@",service.UUID);
//掃描每個service的Characteristics
[peripheral discoverCharacteristics:nil forService:service];
}
}
掃描到Characteristics
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{
if (error)
{
NSLog(@"error Discovered characteristics for %@ with error: %@", service.UUID, [error localizedDescription]);
return;
}
for (CBCharacteristic *characteristic in service.characteristics){
{
//獲取Characteristic的值
[peripheral readValueForCharacteristic:characteristic];
//讀到數據會進入方法:-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
}
}
for (CBCharacteristic *characteristic in service.characteristics){
//搜索Characteristic的Descriptors
[peripheral discoverDescriptorsForCharacteristic:characteristic];
}
}
獲取的charateristic的值 (也就是從設備給主設備的通訊)
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
//打印出characteristic的UUID和值
//!注意毅否,value的類型是NSData亚铁,具體開發(fā)時,會根據外設協(xié)議制定的方式去解析數據
NSLog(@"characteristic uuid:%@ value:%@",characteristic.UUID,characteristic.value);
//主設備存從設備的設備與特征
if(characteristic.properties & CBCharacteristicPropertyWrite){
if ([characteristic.UUID.UUIDString isEqual:@"FFF2"]) {
self.cunPeripheral = peripheral;
self.cunCBCharacteristic = characteristic;
}
}
}
把數據寫到 Characteristic 中螟加,也就是發(fā)送數據
Byte b =0X01;
NSData *data = [NSData dataWithBytes:&b length:sizeof(b)];
[self writeCharacteristic:self.cunPeripheral characteristic:self.cunCBCharacteristic value:data];
-(void)writeCharacteristic:(CBPeripheral *)peripheral
characteristic:(CBCharacteristic *)characteristic
value:(NSData *)value{
//只有 characteristic.properties 有write的權限才可以寫
if(characteristic.properties & CBCharacteristicPropertyWrite){
[peripheral writeValue:value forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
}else{
NSLog(@"該字段不能寫徘溢!");
}
}
4) 訂閱Characteristic的通知
//設置通知
-(void)notifyCharacteristic:(CBPeripheral *)peripheral
characteristic:(CBCharacteristic *)characteristic{
//設置通知,數據通知會進入:didUpdateValueForCharacteristic方法
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
}
//取消通知
-(void)cancelNotifyCharacteristic:(CBPeripheral *)peripheral
characteristic:(CBCharacteristic *)characteristic{
[peripheral setNotifyValue:NO forCharacteristic:characteristic];
}
5) 斷開連接
-(void)disconnectPeripheral:(CBCentralManager *)centralManager
peripheral:(CBPeripheral *)peripheral{
//停止掃描
[centralManager stopScan];
//斷開連接
[centralManager cancelPeripheralConnection:peripheral];
}
3.app作為從設備捆探,接收數據
1) 初始化peripheralManager,設置peripheralManager的委托
self.peripheralManager = [[CBPeripheralManager alloc]initWithDelegate:self queue:nil];
2) 實現(xiàn)代理方法
創(chuàng)建characteristics 創(chuàng)建service然爆,把characteristics添加到service 然后把service添加到peripheralManager
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral{
// 在這里判斷藍牙的狀態(tài), 因為藍牙打開成功后才能配置service和characteristics
//具體配置代碼見demo
}
開啟廣播
//perihpheral添加了service
- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error{
if (error == nil) {
serviceNum++;
}
//因為我們添加了2個服務,所以想兩次都添加完成后才去發(fā)送廣播
if (serviceNum==2) {
[peripheralManager startAdvertising:@{
CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:ServiceUUID1],[CBUUID UUIDWithString:ServiceUUID2]],
CBAdvertisementDataLocalNameKey : LocalNameKey
}
];
}
}
處理收到的數據
//寫characteristics請求
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray *)requests{
NSLog(@"didReceiveWriteRequests");
CBATTRequest *request = requests[0];
//判斷是否有寫數據的權限
if (request.characteristic.properties & CBCharacteristicPropertyWrite) {
//需要轉換成CBMutableCharacteristic對象才能進行寫值
CBMutableCharacteristic *c =(CBMutableCharacteristic *)request.characteristic;
c.value = request.value;
[peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
int i;
[c.value getBytes:&i length:sizeof(i)];
self.numberLabel.text = @(i).stringValue;
}else{
[peripheralManager respondToRequest:request withResult:CBATTErrorWriteNotPermitted];
}
}
從設備還有一個需要每次接收到給主設備發(fā)送一個時間
藍牙學習就告一段落了黍图。
需要demo的朋友自行下載曾雕,如果有用,請給star,謝謝
直接用官方的框架寫還是比較麻煩的雌隅,這篇文章只做學習用翻默,現(xiàn)在其實有很多CoreBluetooth 的封裝,比如這個BabyBluetooth就很棒