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


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

  1. 1. 建立中心角色
  2. 2. 扫描外设(discover
  3. 3. 连接外设(connect)
  4. 4. 扫描外设中的服务和特征(discover)
  5. - 4.1 获取外设的services
  6. - 4.2 获取外设的Characteristics,获取Characteristics的值,获取CharacteristicsDescriptorDescriptor的值
  7. 5. 与外设做数据交互(explore and interact)
  8. 6. 订阅Characteristic的通知
  9. 7. 断开连接(disconnect)

准备环境

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

实现步骤

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


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

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


  1. -(void)centralManagerDidUpdateState:(CBCentralManager *)central{
  2. switch (central.state) {
  3. case CBCentralManagerStateUnknown:
  4. NSLog(@">>>CBCentralManagerStateUnknown");
  5. break;
  6. case CBCentralManagerStateResetting:
  7. NSLog(@">>>CBCentralManagerStateResetting");
  8. break;
  9. case CBCentralManagerStateUnsupported:
  10. NSLog(@">>>CBCentralManagerStateUnsupported");
  11. break;
  12. case CBCentralManagerStateUnauthorized:
  13. NSLog(@">>>CBCentralManagerStateUnauthorized");
  14. break;
  15. case CBCentralManagerStatePoweredOff:
  16. NSLog(@">>>CBCentralManagerStatePoweredOff");
  17. break;
  18. case CBCentralManagerStatePoweredOn:
  19. NSLog(@">>>CBCentralManagerStatePoweredOn");
  20. //开始扫描周围的外设
  21. /*
  22. 第一个参数nil就是扫描周围所有的外设,扫描到外设后会进入
  23. - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI;
  24. */
  25. [manager scanForPeripheralsWithServices:nil options:nil];
  26. break;
  27. default:
  28. break;
  29. }
  30. }
  31. //扫描到设备会进入方法
  32. -(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{
  33. NSLog(@"当扫描到设备:%@",peripheral.name);
  34. //接下来可以连接设备
  35. }

3 连接外设(connect)


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

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


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

4.1获取外设的services

  1. //连接到Peripherals-成功
  2. - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
  3. {
  4. NSLog(@">>>连接到名称为(%@)的设备-成功",peripheral.name);
  5. //设置的peripheral委托CBPeripheralDelegate
  6. //@interface ViewController : UIViewController<CBCentralManagerDelegate,CBPeripheralDelegate>
  7. [peripheral setDelegate:self];
  8. //扫描外设Services,成功后会进入方法:-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
  9. [peripheral discoverServices:nil];
  10. }
  11. //扫描到Services
  12. -(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
  13. // NSLog(@">>>扫描到服务:%@",peripheral.services);
  14. if (error)
  15. {
  16. NSLog(@">>>Discovered services for %@ with error: %@", peripheral.name, [error localizedDescription]);
  17. return;
  18. }
  19. for (CBService *service in peripheral.services) {
  20. NSLog(@"%@",service.UUID);
  21. //扫描每个service的Characteristics,扫描到后会进入方法: -(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
  22. [peripheral discoverCharacteristics:nil forService:service];
  23. }
  24. }

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

  1. //扫描到Characteristics
  2. -(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{
  3. if (error)
  4. {
  5. NSLog(@"error Discovered characteristics for %@ with error: %@", service.UUID, [error localizedDescription]);
  6. return;
  7. }
  8. for (CBCharacteristic *characteristic in service.characteristics)
  9. {
  10. NSLog(@"service:%@ 的 Characteristic: %@",service.UUID,characteristic.UUID);
  11. }
  12. //获取Characteristic的值,读到数据会进入方法:-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
  13. for (CBCharacteristic *characteristic in service.characteristics){
  14. {
  15. [peripheral readValueForCharacteristic:characteristic];
  16. }
  17. }
  18. //搜索Characteristic的Descriptors,读到数据会进入方法:-(void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
  19. for (CBCharacteristic *characteristic in service.characteristics){
  20. [peripheral discoverDescriptorsForCharacteristic:characteristic];
  21. }
  22. }
  23. //获取的charateristic的值
  24. -(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
  25. //打印出characteristic的UUID和值
  26. //!注意,value的类型是NSData,具体开发时,会根据外设协议制定的方式去解析数据
  27. NSLog(@"characteristic uuid:%@ value:%@",characteristic.UUID,characteristic.value);
  28. }
  29. //搜索到Characteristic的Descriptors
  30. -(void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
  31. //打印出Characteristic和他的Descriptors
  32. NSLog(@"characteristic uuid:%@",characteristic.UUID);
  33. for (CBDescriptor *d in characteristic.descriptors) {
  34. NSLog(@"Descriptor uuid:%@",d.UUID);
  35. }
  36. }
  37. //获取到Descriptors的值
  38. -(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForDescriptor:(CBDescriptor *)descriptor error:(NSError *)error{
  39. //打印出DescriptorsUUID 和value
  40. //这个descriptor都是对于characteristic的描述,一般都是字符串,所以这里我们转换成字符串去解析
  41. NSLog(@"characteristic uuid:%@ value:%@",[NSString stringWithFormat:@"%@",descriptor.UUID],descriptor.value);
  42. }

5 把数据写到Characteristic中


  1. //写数据
  2. -(void)writeCharacteristic:(CBPeripheral *)peripheral
  3. characteristic:(CBCharacteristic *)characteristic
  4. value:(NSData *)value{
  5. //打印出 characteristic 的权限,可以看到有很多种,这是一个NS_OPTIONS,就是可以同时用于好几个值,常见的有read,write,notify,indicate,知知道这几个基本就够用了,前连个是读写权限,后两个都是通知,两种不同的通知方式。
  6. /*
  7. typedef NS_OPTIONS(NSUInteger, CBCharacteristicProperties) {
  8. CBCharacteristicPropertyBroadcast = 0x01,
  9. CBCharacteristicPropertyRead = 0x02,
  10. CBCharacteristicPropertyWriteWithoutResponse = 0x04,
  11. CBCharacteristicPropertyWrite = 0x08,
  12. CBCharacteristicPropertyNotify = 0x10,
  13. CBCharacteristicPropertyIndicate = 0x20,
  14. CBCharacteristicPropertyAuthenticatedSignedWrites = 0x40,
  15. CBCharacteristicPropertyExtendedProperties = 0x80,
  16. CBCharacteristicPropertyNotifyEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0) = 0x100,
  17. CBCharacteristicPropertyIndicateEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0) = 0x200
  18. };
  19. */
  20. NSLog(@"%lu", (unsigned long)characteristic.properties);
  21. //只有 characteristic.properties 有write的权限才可以写
  22. if(characteristic.properties & CBCharacteristicPropertyWrite){
  23. /*
  24. 最好一个type参数可以为CBCharacteristicWriteWithResponse或type:CBCharacteristicWriteWithResponse,区别是是否会有反馈
  25. */
  26. [peripheral writeValue:value forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
  27. }else{
  28. NSLog(@"该字段不可写!");
  29. }
  30. }

6 订阅Characteristic的通知


  1. //设置通知
  2. -(void)notifyCharacteristic:(CBPeripheral *)peripheral
  3. characteristic:(CBCharacteristic *)characteristic{
  4. //设置通知,数据通知会进入:didUpdateValueForCharacteristic方法
  5. [peripheral setNotifyValue:YES forCharacteristic:characteristic];
  6. }
  7. //取消通知
  8. -(void)cancelNotifyCharacteristic:(CBPeripheral *)peripheral
  9. characteristic:(CBCharacteristic *)characteristic{
  10. [peripheral setNotifyValue:NO forCharacteristic:characteristic];
  11. }

7 断开连接(disconnect)


  1. //停止扫描并断开连接
  2. -(void)disconnectPeripheral:(CBCentralManager *)centralManager
  3. peripheral:(CBPeripheral *)peripheral{
  4. //停止扫描
  5. [centralManager stopScan];
  6. //断开连接
  7. [centralManager cancelPeripheralConnection:peripheral];
  8. }

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


  1. 由于在iPhone 4s之后的iOS才支持BLE,新一代的这些iOS设备又都不便宜,在做测试的时候,用iOS模拟器进行调试,可以节约一些开发成本。怎么在iOS模拟器上调试BLE
  2. 苹果最初给出的说明是,支持BLEmac机子上可以用模拟器进行调试,并给出了一份技术文档(传送门),恶心的是,后来苹果抽风,又把这份文档移除,
  3. 并且把iOS 7.0的模拟器上对BLE的支持也移除掉了(难道是想让大家多买设备测试?Apple sucks.)后面,网上搜了一下,解决办法如下:
  4. 1. 买一个CSR蓝牙4.0 USB适配器(某宝上大概30块钱),在机子上插入该物(你懂的)
  5. 2. Terminal下敲入sudo nvram bluetoothHostControllerSwitchBehavior="never" 重启Mac
  6. 3. XCode 4.6调试代码,在iOS 6.1的模拟器上跑程序(用XCode 5.0iOS 7.0模拟器会抛异常,原因上面详诉过了,Apple sucks,你懂的)
  7. 如何降低模拟器的IOS版本呢?
  8. XCode->Preferences->Downloads里面有很多simulators你可以下载
  9. 选择个6.1的下载好了

代码下载:

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

本文代码存放目录是BleDemo

ios蓝牙开发(二)ios连接外设的代码实现的更多相关文章

  1. iOS 蓝牙开发资料记录

    一.蓝牙基础认识:   1.iOS蓝牙开发:  iOS蓝牙开发:蓝牙连接和数据读写   iOS蓝牙后台运行  iOS关于app连接已配对设备的问题(ancs协议的锅)          iOS蓝牙空中 ...

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

    转载自:http://www.cocoachina.com/ios/20150917/13456.html 原文作者:刘彦玮 上一篇文章介 绍了蓝牙的技术知识,这里我们具体说明一下中心模式的应用场景. ...

  3. ios蓝牙开发(三)ios连接外设的代码实现:手机app去读写蓝牙设备。

    手机app去读写蓝牙设备....... 代码下载: 原文博客主提供Github代码连接,地址是:https://github.com/coolnameismy/demo ios连接外设的代码实现流程: ...

  4. iOS蓝牙开发(二)蓝牙相关基础知识

    原文链接: http://liuyanwei.jumppo.com/2015/07/17/ios-BLE-1.html iOS蓝牙开发(一)蓝牙相关基础知识: 蓝牙常见名称和缩写 MFI ====== ...

  5. IOS 蓝牙相关-连接外设的代码实现(2)

    我们具体说明一下中心模式的应用场景.主设备(手机去扫描连接外设,发现外设服务和属性,操作服务和属性的应用.一般来说,外设(蓝牙设备,比如智能手环之类的东西), 会由硬件工程师开发好,并定义好设备提供的 ...

  6. iOS_SN_BlueTooth (二)iOS 连接外设的代码实现

    原文:http://www.cocoachina.com/ios/20150917/13456.html?utm_source=tuicool&utm_medium=referral 上一篇文 ...

  7. iOS蓝牙开发(4.0)详解

    最近由于项目需要, 一直在研究蓝牙4.0,在这儿分享给大家, 望共同进步. 一.关于蓝牙开发的一些重要的理论概念: 1.当前ios中开发蓝牙所运用的系统库是<CoreBluetooth/Core ...

  8. iOS 蓝牙开发详解

    目前iOS智能硬件的开发交互方式主要分为两种,一种是基于低功耗的蓝牙4.0技术(由于耗电低,也称作为BLE(Bluetooth Low Energy))对应iOS的框架为CoreBluetooth,另 ...

  9. iOS蓝牙开发CoreBluetooth快速入门

    在iOS开发中,实现蓝牙通信有两种方式,一种是使用传统的GameKit.framework,另一种就是使用在iOS 5中加入的CoreBluetooth.framework. 利用CoreBlueto ...

随机推荐

  1. PHP_OOP

    1.存储器方法——用于限制对象的变量属性 对于弱类型的PHP,存储器方法来限制变量属性显得非常重要! 通过为所有属性创建存储器方法,可以简化添加数据验证或新的业务逻辑的工作,也可以简化在后边对对象执行 ...

  2. GDB调试方法(转)

    一:列文件清单 1. List (gdb) list line1,line2 ************************************************************* ...

  3. 基于ArcEngine的影像数据管理系统研制

    基于ArcEngine的影像数据管理系统研制 如果批处理,速度很慢,效率低. 详情如下: 分成很多小块的影像数据,要达到连续显示的效果,并导入ArcSDE for SQL Server中以方便管理.在 ...

  4. Android中pendingIntent的深入理解

    pendingIntent字面意义:等待的,未决定的Intent.要得到一个pendingIntent对象,使用方法类的静态方法 getActivity(Context, int, Intent, i ...

  5. cygwin在Windows8.1中设置ssh的问题解决

    为了在Windows 8.1上直接使用Linux环境和hadoop开发,装了cygwin,同时设置ssh无密码登录.   但正常ssh-keygen后复制到authorised_keys后登录出现提示 ...

  6. 菜鸟级SQL Server21天自学通(文档+视频)

    SQL语言的主要功能就是同各种数据库建立联系,进行沟通.按照ANSI(美国国家标准协会)的规定,SQL被作为关系型数据库管理系统的标准语言.SQL语句可以用来执行各种各样的操作,例如更新数据库中的数据 ...

  7. cors技术

    简称跨域资源共享: 若是配置nodejs: 需在公共路由添加三句话:代码如下: // 全局头设置 app.all('*', function(req, res, next) { res.set({ ' ...

  8. 请问下mtk双卡手机怎样发短信是怎样选择sim卡来发(双卡都可用的情况下)?

    如题,我如今可以获取双卡状态,当仅仅有单一卡的时候可以指定sim卡进行发短信,可是双卡都可用的情况下,程序就默认使用卡1发短信了.即使指定了sim卡编号.

  9. .net网站开发(前端):4.MVC HtmlHelper

    通过前面三节,已经大概理解MVC是怎样运作的了.MVC的一个特点就是可以很方便地控制视图效果,数据交互也很灵活.先讲一下视图控制的,HtmlHelper,看到Help就知道它是不知疲惫的好人啦(有点像 ...

  10. CString 与 std::string 相互转化

    MFC中CString 与 std::string 相互转化 CString实际是CStringT, 也就是模板类, 在UNICODE环境下,实际是CStringW, 在多字符集环境下,实际是CStr ...