iOS蓝牙开发

    xiaoxiao2021-12-01  17

    iOS蓝牙开发(一)蓝牙相关基础知识

    蓝牙常见名称和缩写

    MFI ======= make for ipad ,iphone, itouch 专们为苹果设备制作的设备

    BLE ==== buletouch low energy,蓝牙4.0设备因为低耗电,所以也叫做BLE

    peripheral,central == 外设和中心,发起连接的时central,被连接的设备为perilheral

    service and characteristic === 服务和特征 每个设备会提供服务和特征,类似于服务端的api,但是机构不同。每个外设会有很多服务,每个服务中包含很多字段,这些字段的权限一般分为 读read,写write,通知notiy几种,就是我们连接设备后具体需要操作的内容。

    Description 每个characteristic可以对应一个或多个Description用户描述characteristic的信息或属性

    MFI === 开发使用ExternalAccessory 框架

    4.0 BLE === 开发使用CoreBluetooth 框架

    蓝牙基础知识

    CoreBluetooth框架的核心其实是两个东西,peripheral和central, 可以理解成外设和中心。对应他们分别有一组相关的API和类

    这两组api分别对应不同的业务场景,左侧叫做中心模式,就是以你的app作为中心,连接其他的外设的场景,而右侧称为外设模式,使用手机作为外设别其他中心设备操作的场景。

    服务和特征,特征的属性(service and characteristic):

    每个设备都会有一些服务,每个服务里面都会有一些特征,特征就是具体键值对,提供数据的地方。每个特征属性分为这么几种:读,写,通知这么几种方式。

    1 2 3 4 5 6 7 8 9 10 11 12 13 //objcetive c特征的定义枚举 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 };

    外设、服务、特征间的关系

    蓝牙中心模式流程

    1. 建立中心角色

    2. 扫描外设(discover)

    3. 连接外设(connect)

    4. 扫描外设中的服务和特征(discover)

        - 4.1 获取外设的services

        - 4.2 获取外设的Characteristics,获取Characteristics的值,获取Characteristics的Descriptor和Descriptor的值

    5. 与外设做数据交互(explore and interact)

    6. 订阅Characteristic的通知

    7. 断开连接(disconnect)

    蓝牙外设模式流程

    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)

    蓝牙和版本的使用限制

    蓝牙2.0 === 越狱设备

    蓝牙4.0 === iOS 6 以上

    MFI认证设备(Make For ipod/ipad/iphone) === 无限制

    iOS 蓝牙开发(二)iOS 连接外设的代码实现

    上一篇文章介绍了蓝牙的技术知识,这里我们具体说明一下中心模式的应用场景。主设备(手机去扫描连接外设,发现外设服务和属性,操作服务和属性的应用。一般来说,外设(蓝牙设备,比如智能手环之类的东西),会由硬件工程师开发好,并定义好设备提供的服务,每个服务对于的特征,每个特征的属性(只读,只写,通知等等),本文例子的业务场景,就是用一手机app去读写蓝牙设备。

    iOS连接外设的代码实现流程

    1. 建立中心角色

    2. 扫描外设(discover)

    3. 连接外设(connect)

    4. 扫描外设中的服务和特征(discover)

     - 4.1 获取外设的services

     - 4.2 获取外设的Characteristics,获取Characteristics的值,获取Characteristics的Descriptor和Descriptor的值

    5. 与外设做数据交互(explore and interact)

    6. 订阅Characteristic的通知

    7. 断开连接(disconnect)

    准备环境

     1 Xcode

     2 开发证书和手机(蓝牙程序需要使用使用真机调试,使用模拟器也可以调试,但是方法很蛋疼,我会放在最后说)

     3 蓝牙外设

    实现步骤

    1 导入CoreBluetooth头文件,建立主设备管理类,设置主设备委托

    #import      @interface ViewController : UIViewController     @interface ViewController (){         //系统蓝牙设备管理对象,可以把他理解为主设备,通过他,可以去扫描和链接外设         CBCentralManager *manager;     }     - (void)viewDidLoad {         [super viewDidLoad];         /*          设置主设备的委托,CBCentralManagerDelegate             必须实现的:             - (void)centralManagerDidUpdateState:(CBCentralManager *)central;//主设备状态改变的委托,在初始化CBCentralManager的适合会打开设备,只有当设备正确打开后才能使用             其他选择实现的委托中比较重要的:             - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI; //找到外设的委托             - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;//连接外设成功的委托             - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//外设连接失败的委托             - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//断开外设的委托         */          //初始化并设置委托和线程队列,最好一个线程的参数可以为nil,默认会就main线程          manager = [[CBCentralManager alloc]initWithDelegate:self queue:dispatch_get_main_queue()];

    2 扫描外设(discover),扫描外设的方法我们放在centralManager成功打开的委托中,因为只有设备成功打开,才能开始扫描,否则会报错。

     -(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");                     //开始扫描周围的外设                     /*                      第一个参数nil就是扫描周围所有的外设,扫描到外设后会进入                           - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI;                      */                     [manager scanForPeripheralsWithServices:nil options:nil];                     break;                 default:                     break;             }         }         //扫描到设备会进入方法         -(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{             NSLog(@"当扫描到设备:%@",peripheral.name);             //接下来可以连接设备         }

    3 连接外设(connect)

         //扫描到设备会进入方法         -(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{             //接下连接我们的测试设备,如果你没有设备,可以下载一个app叫lightbule的app去模拟一个设备             //这里自己去设置下连接规则,我设置的是P开头的设备                    if ([peripheral.name hasPrefix:@"P"]){                    /*                        一个主设备最多能连7个外设,每个外设最多只能给一个主设备连接,连接成功,失败,断开会进入各自的委托                     - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;//连接外设成功的委托                     - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//外设连接失败的委托                     - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//断开外设的委托                     */                     //连接设备                    [manager connectPeripheral:peripheral options:nil];                }         }         //连接到Peripherals-成功         - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral         {             NSLog(@">>>连接到名称为(%@)的设备-成功",peripheral.name);         }         //连接到Peripherals-失败         -(void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error         {             NSLog(@">>>连接到名称为(%@)的设备-失败,原因:%@",[peripheral name],[error localizedDescription]);         }         //Peripherals断开连接         - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{             NSLog(@">>>外设连接断开连接 %@: %@\n", [peripheral name], [error localizedDescription]);         }

    4 扫描外设中的服务和特征(discover)

    设备连接成功后,就可以扫描设备的服务了,同样是通过委托形式,扫描到结果后会进入委托方法。但是这个委托已经不再是主设备的委托(CBCentralManagerDelegate),而是外设的委托(CBPeripheralDelegate),这个委托包含了主设备与外设交互的许多 回叫方法,包括获取services,获取characteristics,获取characteristics的值,获取characteristics的Descriptor,和Descriptor的值,写数据,读rssi,用通知的方式订阅数据等等。

    4.1 获取外设的services

      //连接到Peripherals-成功         - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral         {             NSLog(@">>>连接到名称为(%@)的设备-成功",peripheral.name);             //设置的peripheral委托CBPeripheralDelegate             //@interface ViewController : UIViewController             [peripheral setDelegate:self];             //扫描外设Services,成功后会进入方法:-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{             [peripheral discoverServices:nil];         }         //扫描到Services         -(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{             //  NSLog(@">>>扫描到服务:%@",peripheral.services);             if (error)             {                 NSLog(@">>>Discovered services for %@ with error: %@", peripheral.name, [error localizedDescription]);                 return;             }             for (CBService *service in peripheral.services) {                              NSLog(@"%@",service.UUID);                              //扫描每个service的Characteristics,扫描到后会进入方法: -(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error                              [peripheral discoverCharacteristics:nil forService:service];                          }         }

    4.2 获取外设的Characteristics,获取Characteristics的值,获取Characteristics的Descriptor和Descriptor的值

         //扫描到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)          {              NSLog(@"service:%@ 的 Characteristic: %@",service.UUID,characteristic.UUID);          }          //获取Characteristic的值,读到数据会进入方法:-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error          for (CBCharacteristic *characteristic in service.characteristics){              {                  [peripheral readValueForCharacteristic:characteristic];              }          }          //搜索Characteristic的Descriptors,读到数据会进入方法:-(void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error          for (CBCharacteristic *characteristic in service.characteristics){              [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);     }     //搜索到Characteristic的Descriptors     -(void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{         //打印出Characteristic和他的Descriptors          NSLog(@"characteristic uuid:%@",characteristic.UUID);         for (CBDescriptor *d in characteristic.descriptors) {             NSLog(@"Descriptor uuid:%@",d.UUID);         }     }     //获取到Descriptors的值     -(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForDescriptor:(CBDescriptor *)descriptor error:(NSError *)error{         //打印出DescriptorsUUID 和value         //这个descriptor都是对于characteristic的描述,一般都是字符串,所以这里我们转换成字符串去解析         NSLog(@"characteristic uuid:%@  value:%@",[NSString stringWithFormat:@"%@",descriptor.UUID],descriptor.value);     }

    5 把数据写到Characteristic中

        //写数据     -(void)writeCharacteristic:(CBPeripheral *)peripheral                 characteristic:(CBCharacteristic *)characteristic                          value:(NSData *)value{         //打印出 characteristic 的权限,可以看到有很多种,这是一个NS_OPTIONS,就是可以同时用于好几个值,常见的有read,write,notify,indicate,知知道这几个基本就够用了,前连个是读写权限,后两个都是通知,两种不同的通知方式。         /*          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          };          */         NSLog(@"%lu", (unsigned long)characteristic.properties);         //只有 characteristic.properties 有write的权限才可以写         if(characteristic.properties & CBCharacteristicPropertyWrite){             /*                 最好一个type参数可以为CBCharacteristicWriteWithResponse或type:CBCharacteristicWriteWithResponse,区别是是否会有反馈             */             [peripheral writeValue:value forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];         }else{             NSLog(@"该字段不可写!");         }     }

    6 订阅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];     }

    7 断开连接(disconnect)

        //停止扫描并断开连接     -(void)disconnectPeripheral:(CBCentralManager *)centralManager                      peripheral:(CBPeripheral *)peripheral{         //停止扫描         [centralManager stopScan];         //断开连接         [centralManager cancelPeripheralConnection:peripheral];     }

    8 模拟器蓝牙调试,慎用,最好还是用真机去调试。

    由于在iPhone 4s之后的iOS才支持BLE,新一代的这些iOS设备又都不便宜,在做测试的时候,用iOS模拟器进行调试,可以节约一些开发成本。怎么在iOS模拟器上调试BLE,

    苹果最初给出的说明是,支持BLE的mac机子上可以用模拟器进行调试,并给出了一份技术文档(传送门),恶心的是,后来苹果抽风,又把这份文档移除,

    并且把iOS 7.0的模拟器上对BLE的支持也移除掉了(难道是想让大家多买设备测试?Apple sucks.)后面,网上搜了一下,解决办法如下:

    1. 买一个CSR蓝牙4.0 USB适配器(某宝上大概30块钱),在机子上插入该物(你懂的)

    2. 在Terminal下敲入sudo nvram bluetoothHostControllerSwitchBehavior="never" , 重启Mac。

    3. 用XCode 4.6调试代码,在iOS 6.1的模拟器上跑程序(用XCode 5.0跑iOS 7.0模拟器会抛异常,原因上面详诉过了,Apple sucks,你懂的)

    如何降低模拟器的iOS版本呢?

    XCode->Preferences->Downloads里面有很多simulators你可以下载

    选择个6.1的下载好了

    代码下载:

    我博客中大部分示例代码都上传到了github,地址是:https://github.com/coolnameismy/demo

    本文代码存放目录是BleDemo

    如果大家支持,请follow我的github账号,并star我的项目,有其他问题可以在blog中给我留言 blog的RSS订阅

    iOS 蓝牙开发(三)app作为外设被连接的实现

    再上一节说了app作为central连接peripheral的情况,这一节介绍如何使用app发布一个peripheral,给其他的central连接

    还是这张图,central模式用的都是左边的类,而peripheral模式用的是右边的类

    peripheral模式的流程

    1. 打开peripheralManager,设置peripheralManager的委托

    2. 创建characteristics,characteristics的description 创建service,把characteristics添加到service中,再把service添加到peripheralManager中

    3. 开启广播advertising

    4. 对central的操作进行响应

        - 4.1 读characteristics请求

        - 4.2 写characteristics请求

        - 4.4 订阅和取消订阅characteristics

    准备环境

      1 Xcode

      2 开发证书和手机(蓝牙程序需要使用使用真机调试,使用模拟器也可以调试,但是方法很蛋疼,我会放在最后说),如果不行可以使用osx程序调试

      3 蓝牙外设

    实现步骤

    1. 打开peripheralManager,设置peripheralManager的委托

    设置当前ViewController实现CBPeripheralManagerDelegate委托

        @interface BePeripheralViewController : UIViewController

    初始化peripheralManager

         /*      和CBCentralManager类似,蓝牙设备打开需要一定时间,打开成功后会进入委托方法      - (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral;      模拟器永远也不会得CBPeripheralManagerStatePoweredOn状态      */     peripheralManager = [[CBPeripheralManager alloc]initWithDelegate:self queue:nil];

    2. 创建characteristics,characteristics的description ,创建service,把characteristics添加到service中,再把service添加到peripheralManager中

    在委托方法 - (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral中,当peripheral成功打开后,才可以配置service和characteristics。 这里创建的service和chara对象是CBMutableCharacteristic和CBMutableService。他们的区别就像NSArray和NSMutableArray区别类似。 我们先创建characteristics和description,description是characteristics的描述,描述分很多种, 这里不细说了,常用的就是CBUUIDCharacteristicUserDescriptionString。

    //peripheralManager状态改变 - (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral{     switch (peripheral.state) {             //在这里判断蓝牙设别的状态  当开启了则可调用  setUp方法(自定义)         case CBPeripheralManagerStatePoweredOn:             NSLog(@"powered on");             [info setText:[NSString stringWithFormat:@"设备名%@已经打开,可以使用center进行连接",LocalNameKey]];             [self setUp];             break;         case CBPeripheralManagerStatePoweredOff:             NSLog(@"powered off");             [info setText:@"powered off"];             break;         default:             break;     } }  //配置bluetooch的  -(void)setUp{         //characteristics字段描述         CBUUID *CBUUIDCharacteristicUserDescriptionStringUUID = [CBUUID UUIDWithString:CBUUIDCharacteristicUserDescriptionString];         /*          可以通知的Characteristic          properties:CBCharacteristicPropertyNotify          permissions CBAttributePermissionsReadable          */         CBMutableCharacteristic *notiyCharacteristic = [[CBMutableCharacteristic alloc]initWithType:[CBUUID UUIDWithString:notiyCharacteristicUUID] properties:CBCharacteristicPropertyNotify value:nil permissions:CBAttributePermissionsReadable];         /*          可读写的characteristics          properties:CBCharacteristicPropertyWrite | CBCharacteristicPropertyRead          permissions CBAttributePermissionsReadable | CBAttributePermissionsWriteable          */         CBMutableCharacteristic *readwriteCharacteristic = [[CBMutableCharacteristic alloc]initWithType:[CBUUID UUIDWithString:readwriteCharacteristicUUID] properties:CBCharacteristicPropertyWrite | CBCharacteristicPropertyRead value:nil permissions:CBAttributePermissionsReadable | CBAttributePermissionsWriteable];         //设置description         CBMutableDescriptor *readwriteCharacteristicDescription1 = [[CBMutableDescriptor alloc]initWithType: CBUUIDCharacteristicUserDescriptionStringUUID value:@"name"];         [readwriteCharacteristic setDescriptors:@[readwriteCharacteristicDescription1]];         /*          只读的Characteristic          properties:CBCharacteristicPropertyRead          permissions CBAttributePermissionsReadable          */         CBMutableCharacteristic *readCharacteristic = [[CBMutableCharacteristic alloc]initWithType:[CBUUID UUIDWithString:readCharacteristicUUID] properties:CBCharacteristicPropertyRead value:nil permissions:CBAttributePermissionsReadable];         //service1初始化并加入两个characteristics         CBMutableService *service1 = [[CBMutableService alloc]initWithType:[CBUUID UUIDWithString:ServiceUUID1] primary:YES];         [service1 setCharacteristics:@[notiyCharacteristic,readwriteCharacteristic]];         //service2初始化并加入一个characteristics         CBMutableService *service2 = [[CBMutableService alloc]initWithType:[CBUUID UUIDWithString:ServiceUUID2] primary:YES];         [service2 setCharacteristics:@[readCharacteristic]];         //添加后就会调用代理的- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error         [peripheralManager addService:service1];         [peripheralManager addService:service2];  }

    3. 开启广播advertising

    //perihpheral添加了service - (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error{     if (error == nil) {         serviceNum++;     }     //因为我们添加了2个服务,所以想两次都添加完成后才去发送广播     if (serviceNum==2) {         //添加服务后可以在此向外界发出通告 调用完这个方法后会调用代理的         //(void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error         [peripheralManager startAdvertising:@{                                               CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:ServiceUUID1],[CBUUID UUIDWithString:ServiceUUID2]],                                               CBAdvertisementDataLocalNameKey : LocalNameKey                                              }          ];     } } //peripheral开始发送advertising - (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error{     NSLog(@"in peripheralManagerDidStartAdvertisiong"); }

    4. 对central的操作进行响应

    - 4.1 读characteristics请求

    - 4.2 写characteristics请求

    - 4.3 订阅和取消订阅characteristics

    //订阅characteristics -(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic{     NSLog(@"订阅了 %@的数据",characteristic.UUID);     //每秒执行一次给主设备发送一个当前时间的秒数     timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(sendData:) userInfo:characteristic  repeats:YES]; } //取消订阅characteristics -(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic{     NSLog(@"取消订阅 %@的数据",characteristic.UUID);     //取消回应     [timer invalidate]; } //发送数据,发送当前时间的秒数 -(BOOL)sendData:(NSTimer *)t {     CBMutableCharacteristic *characteristic = t.userInfo;     NSDateFormatter *dft = [[NSDateFormatter alloc]init];     [dft setDateFormat:@"ss"];     NSLog(@"%@",[dft stringFromDate:[NSDate date]]);     //执行回应Central通知数据     return  [peripheralManager updateValue:[[dft stringFromDate:[NSDate date]] dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:(CBMutableCharacteristic *)characteristic onSubscribedCentrals:nil]; } //读characteristics请求 - (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request{     NSLog(@"didReceiveReadRequest");     //判断是否有读数据的权限     if (request.characteristic.properties & CBCharacteristicPropertyRead) {         NSData *data = request.characteristic.value;         [request setValue:data];         //对请求作出成功响应         [peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];     }else{         [peripheralManager respondToRequest:request withResult:CBATTErrorWriteNotPermitted];     } } //写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];     }else{         [peripheralManager respondToRequest:request withResult:CBATTErrorWriteNotPermitted];     } }

    代码下载:

    我博客中大部分示例代码都上传到了github,地址是:https://github.com/coolnameismy/demo,点击跳转代码下载地址

    本文代码存放目录是BleDemo

    如果大家支持,请follow我的github账号,并star我的项目,有其他问题可以在blog中给我留言 blog的RSS订阅

    iOS 蓝牙开发(四)BabyBluetooth蓝牙库介绍

    BabyBluetooth 是一个最简单易用的蓝牙库,基于CoreBluetooth的封装,并兼容iOS和Mac OS X。

    特色:

    基于原生CoreBluetooth框架封装的轻量级的开源库,可以帮你更简单地使用CoreBluetooth API。

    CoreBluetooth所有方法都是通过委托完成,代码冗余且顺序凌乱。BabyBluetooth使用block方法,可以重新按照功能和顺序组织代码,并提供许多方法减少蓝牙开发过程中的代码量。

    链式方法体,代码更简洁、优雅。

    通过channel切换区分委托调用,并方便切换

    来源

    最近几个月都在做蓝牙项目,用CoreBluetooch感觉语句写的到处都是,不优雅。一整条链下来要近10几个委托方法,并且不断的在委托方法中调用方法再进入其他的委托,导致 代码很零散。因此我就想让coreBlueTooth使用更简单,语法更优雅,所以开始写这个BabyBluetooch蓝牙库。

    刚开始写BabyBluetooth时,只有目标,也不值得如何实现,于是一步步安装目标去实现,也遇到过很多问题,慢慢的也想出了解决方案,并一步步直到9月7日早晨,才算完成了 第一个可以用的版本。对于蓝牙这种在app中亮相率不高的功能,github上ios使用的操作蓝牙的库最多就100来个star,然后BabyBluetooch2天的时间内已经有了15个star和10个fork, 已经相当不错了,很快就有越来越多的人参与到项目中,issues,pull request等等。这些都是个人成长和项目成长非常宝贵的资源。

    更新于:20150916,现在BabyBluetooth 已经有了96个star

    期待

    蓝牙库写起来很辛苦,希望大家可以多多支持,多多star。BabyBluetooth主页

    如果在使用过程中遇到BUG,或发现功能不够用,希望你能Issues我,谢谢

    期待大家也能一起为BabyBluetooth输出代码,这里我只是给BabyBluetooth开了个头,他可以增加和优化的地方还是非常多。也期待和大家在Pull Requests一起学习,交流,成长。

    Quick Example

    //导入.h文件和系统蓝牙库的头文件 #import "BabyBluetooth.h" -(void)viewDidLoad {     [super viewDidLoad];    //初始化BabyBluetooth 蓝牙库     baby = [BabyBluetooth shareBabyBluetooth];      //设置蓝牙委托      [self babyDelegate];      //设置委托后直接可以使用,无需等待CBCentralManagerStatePoweredOn状态      baby.scanForPeripherals().begin() } //蓝牙网关初始化和委托方法设置 -(void)babyDelegate{     //设置扫描到设备的委托     [baby setBlockOnDiscoverToPeripherals:^(CBCentralManager *central, CBPeripheral *peripheral, NSDictionary *advertisementData, NSNumber *RSSI) {         NSLog(@"搜索到了设备:%@",peripheral.name);     }];     //设置设备连接成功的委托     [baby setBlockOnConnected:^(CBCentralManager *central, CBPeripheral *peripheral) {         NSLog(@"设备:%@--连接成功",peripheral.name);     }];     //设置发现设备的Services的委托     [baby setBlockOnDiscoverServices:^(CBPeripheral *peripheral, NSError *error) {         for (CBService *service in peripheral.services) {             NSLog(@"搜索到服务:%@",service.UUID.UUIDString);         }     }];     //设置发现设service的Characteristics的委托     [baby setBlockOnDiscoverCharacteristics:^(CBPeripheral *peripheral, CBService *service, NSError *error) {         NSLog(@"===service name:%@",service.UUID);         for (CBCharacteristic *c in service.characteristics) {             NSLog(@"charateristic name is :%@",c.UUID);         }     }];     //设置读取characteristics的委托     [baby setBlockOnReadValueForCharacteristic:^(CBPeripheral *peripheral, CBCharacteristic *characteristics, NSError *error) {         NSLog(@"characteristic name:%@ value is:%@",characteristics.UUID,characteristics.value);     }];     //设置发现characteristics的descriptors的委托     [baby setBlockOnDiscoverDescriptorsForCharacteristic:^(CBPeripheral *peripheral, CBCharacteristic *characteristic, NSError *error) {         NSLog(@"===characteristic name:%@",characteristic.service.UUID);         for (CBDescriptor *d in characteristic.descriptors) {             NSLog(@"CBDescriptor name is :%@",d.UUID);         }     }];     //设置读取Descriptor的委托     [baby setBlockOnReadValueForDescriptors:^(CBPeripheral *peripheral, CBDescriptor *descriptor, NSError *error) {         NSLog(@"Descriptor name:%@ value is:%@",descriptor.characteristic.UUID, descriptor.value);     }];     //过滤器     //设置查找设备的过滤器     [baby setDiscoverPeripheralsFilter:^BOOL(NSString *peripheralsFilter) {         //设置查找规则是名称大于1 , the search rule is peripheral.name length > 1         if (peripheralsFilter.length >1) {             return YES;         }         return NO;     }];     //设置连接的设备的过滤器      __block BOOL isFirst = YES;     [baby setFilterOnConnetToPeripherals:^BOOL(NSString *peripheralName) {         //这里的规则是:连接第一个AAA打头的设备         if(isFirst && [peripheralName hasPrefix:@"AAA"]){             isFirst = NO;             return YES;         }         return NO;     }]; }

    CoreBluetooch中实现上扫描,连接,发现服务和characteristic以及它的值相关方法调用是很麻烦啰嗦凌乱的。如下: centralManager启动->状态委托->调用扫描方法->进入扫描到设备的委托->调用连接设备方法->进入连接到设备的委托->发现服务方法->发现服务委托-> 发现characteristic方法->发现characteristic委托->读characteristic的value->读characteristic的value的委托->读description,读description的value-> ....的委托

    而BabyBluetooth只需要一句话就执行了上面的内容。

      //扫描设备 然后读取服务,然后读取characteristics名称和值和属性,获取characteristics对应的description的名称和值   baby.scanForPeripherals().connectToPeripheral().discoverServices()   .discoverCharacteristics().readValueForCharacteristic().discoverDescriptorsForCharacteristic()   .readValueForDescriptors().begin();

    另一方面,BabyBluetooth所有的委托方法都紧凑的聚在了一起。此外,快速示例中没有包括channel的使用,如果包括了channel,那么ios几个页面或者组件的蓝牙 调用模块都可以写在一起,看起来就觉得很方便。

    关于更多BabyBluetooth的介绍和使用示例已经api,请移步到BabyBluetooth主页查看

    转载请注明原文地址: https://ju.6miu.com/read-679507.html

    最新回复(0)