最近学习蓝牙,所以找了一些资料学习研究了一下蓝牙通讯的一个流程。写了一个小demo,demo效果见下图:
设备a作为发送数据端,发送不同的数字 设备b作为接收数据端,接收到不同的数字
需要demo的朋友自行下载,如果有用,请给star,谢谢
接下来我们讲学习:
- 基础知识了解
- app作为主设备,发送数据
- app作为从设备,接收数据
1.基础知识了解
蓝牙5.0是由蓝牙技术联盟在2016年提出的蓝牙技术标准,蓝牙5.0针对低功耗设备速度有相应提升和优化,蓝牙5.0结合wifi对室内位置进行辅助定位,提高传输速度,增加有效工作距离。百度百科
我们在开发中要使用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
};
蓝牙设备的几种状态:
蓝牙设备状态图.jpg设备作为中心模式(central)流程:
中心模式流程图.jpg设备作为外设模式(peripheral)流程:
外设模式流程图.jpg2.app作为主设备,发送数据
注意:蓝牙需要真机调试,所以必须要有真机
1) 导入 CoreBluetooth 框架,和头文件
import <CoreBluetooth/CoreBluetooth.h>
两个属性:
@property(nonatomic,strong)CBCentralManager *manager;//主设备
@property(nonatomic,strong)NSMutableArray *peripherals;//被发现设备数组
初始化,设置代理 CBCentralManagerDelegate
self.manager = [[CBCentralManager alloc]initWithDelegate:self queue:dispatch_get_main_queue()];
self.peripherals = @[].mutableCopy;
2) 实现CBCentralManagerDelegate相关的协议方法
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);
//接下来可以连接设备
//这里自己去设置下连接规则,我设置的是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,具体开发时,会根据外设协议制定的方式去解析数据
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 中,也就是发送数据
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) 实现代理方法
创建characteristics 创建service,把characteristics添加到service 然后把service添加到peripheralManager
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral{
// 在这里判断蓝牙的状态, 因为蓝牙打开成功后才能配置service和characteristics
//具体配置代码见demo
}
开启广播
//perihpheral添加了service
- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error{
if (error == nil) {
serviceNum++;
}
//因为我们添加了2个服务,所以想两次都添加完成后才去发送广播
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];
}
}
从设备还有一个需要每次接收到给主设备发送一个时间
蓝牙学习就告一段落了。
需要demo的朋友自行下载,如果有用,请给star,谢谢
直接用官方的框架写还是比较麻烦的,这篇文章只做学习用,现在其实有很多CoreBluetooth 的封装,比如这个BabyBluetooth就很棒
网友评论