蓝牙 BlueTooth Low Energy (BLE)
来源:互联网 发布:stc单片机检测原理 编辑:程序博客网 时间:2024/05/01 07:18
每个设备会有很多服务
,每个服务中包含很多字段
,这些字段的权限一般分为读(read),写(write),通知(notify)几种,就是我们连接设备后具体需要操作的内容
CoreBluetooth框架的核心其实是俩东西:peripheral和central,对应他们分别有一组相关的API和类
手机作为外设
连接其他中心设备操作的场景
每个设备都会有1个or多个服务
每个服务里都会有1个or多个特征
特征就是具体键值对,提供数据的地方
每个特征属性分为:读,写,通知等等
- 外设,服务,特征的关系
2.扫描外设(Discover Peripheral)
3.连接外设(Connect Peripheral)
4.扫描外设中的服务和特征(Discover Services And Characteristics)
4.1 获取外设的services
4.2 获取外设的Characteristics,获取characteristics的值,,获取Characteristics的Descriptor和Descriptor的值
5.利用特征与外设做数据交互(Explore And Interact)
6.订阅Characteristic的通知
7.断开连接(Disconnect)
BLE外设模式流程
1.启动一个Peripheral管理对象
2.本地peripheral设置服务,特征,描述,权限等等
3.peripheral发送广告
4.设置处理订阅,取消订阅,读characteristic,写characteristic的代理方法
1.待机状态(standby):设备没有传输和发送数据,并且没有连接到任何外设
2.广播状态(Advertiser):周期性广播状态
3.扫描状态(Scanner):主动搜索正在广播的设备
4.发起链接状态(Initiator):主动向扫描设备发起连接
5.主设备(Master):作为主设备连接到其它设备.
6.从设备(Slave):作为从设备链接到其它设备
蓝牙设备的五种工作状态
准备(Standby)
广播(Advertising)
监听扫描(Scanning)
发起连接(Initiating)
已连接(Connected)
/*******************************************************************************************************/
1.待机状态(standby):设备没有传输和发送数据,并且没有连接到任何外设
2.广播状态(Advertiser):周期性广播状态
3.扫描状态(Scanner):主动搜索正在广播的设备
4.发起链接状态(Initiator):主动向扫描设备发起连接
5.主设备(Master):作为主设备连接到其它设备.
6.从设备(Slave):作为从设备链接到其它设备
蓝牙设备的五种工作状态
准备(Standby)
广播(Advertising)
监听扫描(Scanning)
发起连接(Initiating)
已连接(Connected)
实现代码
中心设备代码:#import "CentralViewController.h"#import <CoreBluetooth/CoreBluetooth.h>#define SERVICE_UUID @"307846BD-EDB4-4E3B-A0A7-52B2E5AFA760"#define CHARACTERISTIC_UUID @"C430B109-A222-44A9-B75D-49D89BD97642"#define BLUETOOTH_PIC_END @"PIC_END"#define BLUETOOTH_TEXT_END @"TEXT_END"#define MAX_BYTES 20@interface CentralViewController ()<CBCentralManagerDelegate,CBPeripheralDelegate>{ CBCentralManager * _manager; UILabel * _statusLb; UILabel * _showLb;}@property (nonatomic,strong) CBPeripheral * discovedPeripheral;@property (nonatomic,strong) NSMutableData * data;@end@implementation CentralViewController- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. // 创建中心管理器对象 _manager = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue()]; _data = [NSMutableData data]; _statusLb = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 200, 30)]; _statusLb.text = @"扫描外设中..."; [self.view addSubview:_statusLb]; _showLb = [[UILabel alloc] initWithFrame:CGRectMake(0, 30, 320, 100)]; _showLb.numberOfLines = 0; _showLb.text = @"接受到的数据:"; [self.view addSubview:_showLb];}-(void)scan{ // 是否允许中央设备多次收到曾经监听到的设备的消息,这样来监听外围设备联接的信号强度,以决定是否增大广播强度,为YES时会多耗电 [_manager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:SERVICE_UUID]] options:@{ CBCentralManagerScanOptionAllowDuplicatesKey : @YES }];}-(void)clearUp{ if (![self.discovedPeripheral isConnected]) { return; } if (self.discovedPeripheral.services!=nil) { for (CBService*server in self.discovedPeripheral.services) { if (server.characteristics!=nil) { for (CBCharacteristic*chatacter in server.characteristics) { if ([chatacter.UUID isEqual:[CBUUID UUIDWithString:CHARACTERISTIC_UUID]]) { // 是否订阅 if (chatacter.isNotifying) { // 如果订阅 取消订阅 [self.discovedPeripheral setNotifyValue:NO forCharacteristic:chatacter]; return; } } } } } } // 连接没有订阅 断开连接 [_manager cancelPeripheralConnection:self.discovedPeripheral]; }#pragma mark - CBCentralManagerDelegate// 检测中央设备状态-(void)centralManagerDidUpdateState:(CBCentralManager *)central{ if (central.state!=CBCentralManagerStatePoweredOn) { NSLog(@"蓝牙关闭"); return; } // 开启检测 [self scan];}// 当外围设备广播同样的UUID信号 被发现时 函数被调用 RSSI接收的信号强度指示 足够近才能连接- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{ NSLog(@"Discovered %@ at %@", peripheral.name, RSSI); // 判断是不是我们监听到的外围设备 if (self.discovedPeripheral != peripheral) { self.discovedPeripheral = peripheral; // 连接周边 [_manager connectPeripheral:peripheral options:nil]; NSLog(@"Connecting to peripheral %@", peripheral); }}// 连接上外围设备后我们就要找到外围设备的服务特性-(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{ // 连接完成后,就停止检测 [_manager stopScan]; [self.data setLength:0]; // 确保我们收到的外围设备连接后的回调代理函数 peripheral.delegate=self; // 让外围设备找到与我们发送的UUID所匹配的服务 // 生成UUID命令:uuidgen [peripheral discoverServices:@[[CBUUID UUIDWithString:SERVICE_UUID]]];}// 发现了服务-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{ if (error) { NSLog(@"Errordiscover:%@",error.localizedDescription); [self clearUp]; return; } // 找到我们想要的特性 // 遍历外围设备 for (CBService*server in peripheral.services) { // 寻找指定UUID的特征 [peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:CHARACTERISTIC_UUID]] forService:server]; } }// 当发现传送服务特性后我们要订阅他 来告诉外围设备我们想要这个特性所持有的数据-(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{ if (error) { NSLog(@"error %@",[error localizedDescription]); [self clearUp]; return; } // 检查特性 for (CBCharacteristic*characteristic in service.characteristics) { if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:CHARACTERISTIC_UUID]]) { // 有来自外围的特性,找到了,就订阅他 // 如果第一个参数是yes的话,就是允许代理方法peripheral:didUpdateValueForCharacteristic:error: 来监听 第二个参数 特性是否发生变化 // 订阅特征 [peripheral setNotifyValue:YES forCharacteristic:characteristic]; NSLog(@"订阅成功"); } }}// 外围设备让我们知道,我们订阅和取消订阅是否发生-(void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{ if (error) { NSLog(@"error %@",error.localizedDescription); } // 如果不是我们要的特性就退出 if (![characteristic.UUID isEqual:[CBUUID UUIDWithString:CHARACTERISTIC_UUID]]) { return; } if (characteristic.isNotifying) { NSLog(@"外围特性通知开始"); _statusLb.text = @"连接成功 等待接受数据"; }else{ NSLog(@"外围设备特性通知结束,也就是用户要下线或者离开%@",characteristic); // 断开连接 [_manager cancelPeripheralConnection:peripheral]; }}// 接受蓝牙传递的数据-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{ if (error) { return; } // characteristic.value 是特性中所包含的数据 NSString * stringFromData=[[NSString alloc]initWithData:characteristic.value encoding:NSUTF8StringEncoding]; if ([stringFromData isEqualToString:BLUETOOTH_PIC_END]) { // 接受文字 NSString * str= [[NSString alloc]initWithData:self.data encoding:NSUTF8StringEncoding]; _showLb.text = str; self.data.length = 0;#if 0 // 接受图片 UIImage * img = [UIImage imageWithData:self.data]; UIImageView * imgView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 150, 320, 200)]; imgView.image = img; [self.view addSubview:imgView];#endif #if 0 // 取消订阅 断开蓝牙连接 [peripheral setNotifyValue:NO forCharacteristic:characteristic]; [_manager cancelPeripheralConnection:peripheral];#endif }else{ // 数据没有传递完成,继续传递数据 [self.data appendData:characteristic.value]; } }@end
外设实现代码:
#import "PeripheralViewController.h"#import <CoreBluetooth/CoreBluetooth.h>@interface PeripheralViewController ()<CBPeripheralManagerDelegate>{ UILabel * _statusLb; UITextField * _inputView; // 当前发送了多少字节 unsigned long _sendBytes; // 是否发送完成 BOOL _finish;}// 周边设备管理类@property(nonatomic,strong)CBPeripheralManager*peripheralManager;// 可变服务特性@property(nonatomic,strong)CBMutableCharacteristic*transferCharacteristic;@end@implementation PeripheralViewController- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. self.peripheralManager=[[CBPeripheralManager alloc]initWithDelegate:self queue:nil]; _statusLb = [[UILabel alloc] initWithFrame:CGRectMake(0, 0, 200, 30)]; _statusLb.text = @"发送广播中..."; [self.view addSubview:_statusLb]; _inputView = [[UITextField alloc] initWithFrame:CGRectMake(0, 30, 270, 30)]; _inputView.placeholder = @"需要通过蓝牙发送的消息..."; [self.view addSubview:_inputView]; UIButton * b = [UIButton buttonWithType:UIButtonTypeSystem]; [b setFrame:CGRectMake(270, 30, 50, 30)]; [b setBackgroundColor:[UIColor grayColor]]; [b setTitle:@"发送" forState:UIControlStateNormal]; [b addTarget:self action:@selector(click) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:b];}/* 切割发送二进制数据 一次性发送20字节 直到发送完毕 如果发送完毕 那么最后再发送一个 pic_end字符串给中心 */- (void)click{ // 如果蓝牙数据发送完成了 最后发一个字符串 "pic_end"给中心 表示发送完成 if (_finish) { //第三个参数代表指定与我们的订阅的中心设备发送,返回一个布尔值,代表发送成功 BOOL didSend=[self.peripheralManager updateValue:[BLUETOOTH_PIC_END dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:self.transferCharacteristic onSubscribedCentrals:nil]; if (didSend) { //全部发送完成 _finish = NO; _sendBytes = 0; NSLog(@"发送完成"); } //如果没有发送,我们就要退出并且等待 //peripheralManagerIsReadyToUpdateSubscribers 来再一次调用sendData来发送数据 return; } // 如果没有正在发送BluetoothEnd,就是在发送数据 // 发送文字 NSData * sendData=[_inputView.text dataUsingEncoding:NSUTF8StringEncoding];#if 0 // 发送图片 NSData * sendData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"1" ofType:@"png"]];#endif //判断是否还有剩下的数据 if (_sendBytes >= sendData.length) { //没有数据,退出即可 return; } //如果有数据没有发送完就发送它,除非回调失败或者我们发送完 BOOL didSend=YES; while (didSend) { //发送下一块数据,计算出数据有多大 NSInteger amountToSend=sendData.length-_sendBytes; if (amountToSend>MAX_BYTES) { //如果剩余的数据还是大于20字节,那么我最多传送20字节 amountToSend=MAX_BYTES; } //切出我想要发送的数据 +sendDataIndex就是从多少字节开始向后切多少 NSData*chunk=[NSData dataWithBytes:sendData.bytes+_sendBytes length:amountToSend]; //发送 didSend=[self.peripheralManager updateValue:chunk forCharacteristic:self.transferCharacteristic onSubscribedCentrals:nil]; //如果没发送成功,等待回调发送 if (!didSend) { return; }else{ _sendBytes+=amountToSend; //判断是否发送完 if (_sendBytes>=sendData.length) { //发送完成,就开始发送结束标示bluetoothEND _finish = YES; [self performSelector:@selector(click) withObject:nil afterDelay:0.1]; } } } [self.view endEditing:YES];}#pragma mark - CBPeripheralManagerDelegate// 检测状态-(void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral{ if (peripheral.state!=CBPeripheralManagerStatePoweredOn) { return; } // 启动service // 启动可变服务特性properties:Notify允许没有回答的服务特性,向中心设备发送数据,permissions:read通讯属性为只读 self.transferCharacteristic=[[CBMutableCharacteristic alloc]initWithType:[CBUUID UUIDWithString:CHARACTERISTIC_UUID] properties:CBCharacteristicPropertyNotify value:nil permissions:CBAttributePermissionsReadable]; // 创建服务 primary 是首次还是第二次 CBMutableService*transferService=[[CBMutableService alloc]initWithType:[CBUUID UUIDWithString:SERVICE_UUID] primary:YES]; // 把特性加到服务上 transferService.characteristics=@[self.transferCharacteristic]; // 把服务加到管理上 [self.peripheralManager addService:transferService]; // 发送广播,标示是TRANSFER_SERVICE_UUID为对方观察接收的值,2边要对应上 [self.peripheralManager startAdvertising:@{CBAdvertisementDataServiceUUIDsKey:@[[CBUUID UUIDWithString:SERVICE_UUID]]}];}// 订阅特性成功 开始发送数据-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic{ NSLog(@"订阅成功"); _statusLb.text = @"连接成功 可以发送消息了";}// 中央设备结束订阅时候调用-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic{ NSLog(@"结束订阅"); _statusLb.text = @"连接断开";}// 发送队列满了 需要再次发送-(void)peripheralManagerIsReadyToUpdateSubscribers:(CBPeripheralManager *)peripheral{ // NSLog(@"发送队列已满 再次发送"); [self click];}@end
- 蓝牙 BlueTooth Low Energy (BLE)
- BLE/Bluetooth Low Energy
- 低功耗蓝牙Bluetooth Low Energy(BLE)
- Android5.0(Lollipop) 蓝牙BLE(Bluetooth Low Energy)
- Android-低功耗蓝牙BLE(Bluetooth Low Energy)开发
- 基于蓝牙4.0的蓝牙打印机 低耗电蓝牙 BLE (Bluetooth Low Energy)
- BLE(BLuetooth Lower Energy)蓝牙
- Android:BLE(Bluetooth Low Energy)开发
- BLE(Bluetooth Low Energy)---first part
- 蓝牙低功耗(Bluetooth Low Energy)
- Bluetooth Low Energy(蓝牙低功耗)
- Bluetooth Low Energy android低功耗蓝牙
- Bluetooth Low Energy Advertising Bluetooth Low Energy (BLE) conserves power by remaining in sleep m
- Bluetooth Low Energy(低功耗蓝牙)-For蓝牙4.x
- Bluetooth Low Energy——蓝牙低功耗
- Bluetooth Low Energy 介绍 低功耗蓝牙介绍
- Android Bluetooth Low Energy(Android低功耗蓝牙)
- Bluetooth Low Energy——蓝牙低功耗
- playfair密码 C语言实现
- MySQL大表优化方案
- HDFS合并文件
- BZOJ4006 [JLOI2015]管道连接
- javascript自定义函数传递参数的常见问题
- 蓝牙 BlueTooth Low Energy (BLE)
- BZOJ 3576 [Hnoi2014] 江南乐
- Linux 连续执行多条命令的方法
- 如何使用windows计划任务运行jar程序
- java se 10
- window实验
- 区间信息的维护与查询
- nginx+tomcat+memcached配置session共享
- CentOS7 搭建 IRedMail邮箱服务