前言

  • 蓝牙传输所用的框架是<CoreBluetooth/CoreBluetooth.h>
  • 蓝牙连接需要中心管理者和外部设备,我们所做的开发基本是围绕中心管理来的;
  • 蓝牙设备发过来的每个数据包,为了保证数据在传输的时候没有丢失,一般需要包头,包尾,校验和
  • 有很多蓝牙协议很复杂,需要把数据转化成二进制进行转化解析,对于高字节,低字节,小端模式,大端模式,符号位,位运算这些基本概念需要了解清楚

1.关于Mac地址的获取

自iOS7之后,苹果不支持获取Mac地址,只能用UUID来标识设备,要注意的是同一个设备在不同手机上显示的UUID不相同,但有的设备可以通过 “180A”这个服务来发现特征,再来读取 “2A23”这个特征值,可以获得Mac地址。如果你的蓝牙设备不支持这样获取,你可以跟硬件工程师沟通,来获得Mac地址,添加一个获取地址命令或者增加一个含地址的特征值都可以很容易的获取。上面获取地址的前提都是需要先建立连接,如果一定要在扫描的时候获得Mac地址,让硬件工程师把数据写入广播包里,看是否可行;

2.蓝牙连接流程

如果你不是新手,又不想浪费时间,请直接看第三点 注意点,核心部分

  • 建立中心设备管理者
  • 扫描外设
  • 连接外设
  • 扫描外设中的服务
  • 扫描外设中的特征
  • 订阅或读取特征值
  • 获取外设中的数据

建立中心设备管理者

 // 创建之后会马上检查蓝牙的状态,nil默认为主线程
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil]

蓝牙线程没必要去开异步线程,在主线程消耗不了什么性能

扫描外设

// 蓝牙状态发生改变,这个方法一定要实现
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{// 蓝牙状态可用if (central.state == CBCentralManagerStatePoweredOn) {// 如果蓝牙支持后台模式,一定要指定服务,否则在后台断开连接不上,如果不支持,可设为nil, option里的CBCentralManagerScanOptionAllowDuplicatesKey默认为NO, 如果设置为YES,允许搜索到重名,会很耗电[self.centralManager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:kServiceUUID]] options:nil];}
}

连接外设

 /*** 发现设备* @param peripheral 设备* @param advertisementData 广播内容* @param RSSI 信号强度*/
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI
{// 判断是否是你需要连接的设备if ([peripheral.name isEqualToString:kPeripheralName]) {peripheral.delegate = self;// 一定要记得把外设保存起来self.selectedPeripheral = peripheral;// 开始连接设备[self.centralManager connectPeripheral:self.selectedPeripheral options:nil];}
}

扫描外设中的服务

/*** 已经连接上设备*/
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{// 停止扫描[self.centralManager stopScan];// 发现服务[self.selectedPeripheral discoverServices:nil];
}

扫描外设中的特征

/*** 已经发现服务*/
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{for (CBService *service in peripheral.services) {if ([service.UUID isEqual:[CBUUID UUIDWithString:kServiceUUID]]) {// 根据你要的那个服务去发现特性[self.selectedPeripheral discoverCharacteristics:nil forService:service];}// 这里我是根据 180A 用来获取Mac地址,没什么实际作用,可删掉if ([service.UUID isEqual:[CBUUID UUIDWithString:@"180A"]]) {[self.selectedPeripheral discoverCharacteristics:nil forService:service];}}
}

订阅或读取特征值

/*** 已经发现特性*/
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{for (CBCharacteristic *characteristic in service.characteristics) {if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"2A23"]]) {// 这里是读取Mac地址, 可不要, 数据固定, 用readValueForCharacteristic, 不用setNotifyValue:setNotifyValue[self.selectedPeripheral readValueForCharacteristic:characteristic];}if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kCharacteristicUUID]]) {// 订阅特性,当数据频繁改变时,一般用它, 不用readValueForCharacteristic[peripheral setNotifyValue:YES forCharacteristic:characteristic];// 获取电池电量unsigned char send[4] = {0x5d, 0x08, 0x01, 0x3b};NSData *sendData = [NSData dataWithBytes:send length:4];// 这里的type类型有两种 CBCharacteristicWriteWithResponse CBCharacteristicWriteWithoutResponse,它的属性枚举可以组合[self.selectedPeripheral writeValue:sendData forCharacteristic:characteristic type:CBCharacteristicWriteWithoutResponse];/*characteristic 属性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(@"%@",characteristic);// 打印结果为 <CBCharacteristic: 0x1702a2a00, UUID = FFF6, properties = 0x16, value = (null), notifying = NO>//  我的结果 为 0x16  (0x08 & 0x16)结果不成立, (0x04 & 0x16)结果成立,那写入类型就是 CBCharacteristicPropertyWriteWithoutResponse}}
}

获取外设中的数据

/*** 数据更新的回调*/
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{// 这里收到的数据都是16进制,有两种转换,一种就直接转字符串,另一种是转byte数组,看用哪种方便// 直接转字符串NSString *orStr = characteristic.value.description;NSString *str = [orStr substringWithRange:NSMakeRange(1, orStr.length - 2)];NSString *dataStr = [str stringByReplacingOccurrencesOfString:@" " withString:@""];NSLog(@"dataStr = %@",dataStr);// 转Byte数组Byte *byte = (Byte *)characteristic.value.bytes;//_______________________________________________________________________________________________________________// 解析你的协议,附几个解协议或许能用到的函数
}

设备连接断开

- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{// 让它自动重连[self.centralManager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:kServiceUUID]] options:nil];
}

这是系统代理方法,如果要主动断开需要调用 - (void)cancelPeripheralConnection:(CBPeripheral *)peripheral; 这个方法

写入数据成功的回调

- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{// 读取数据[self.selectedPeripheral readValueForCharacteristic:characteristic];
}

如果类型是CBCharacteristicWriteWithoutResponse,不会走这个方法;

3.注意点,核心部分,请仔细看

  1. 做蓝牙前一定要去商城下个LightBlue,一个设备有很多服务,服务中又有很多特性,特性中又分读的,写的等,有了LightBlue,你可以很快的找到你需要的特性;

    LightBlue截图

    从上图中我们可以清晰的看到每个服务中又多少个特性,特性的属性Read、Write、Write Without Response、Notify等也标明的很清楚,

  2. 一般的蓝牙都要支持重连和后台运行,如果扫描设备的时候,用这个方法- (void)scanForPeripheralsWithServices:options:没有指定特定的服务,而是用nil代替,设备在后台断开的时候是不会重连的;

  3. 蓝牙是可以同时连接多个外部设备

  4. 关于readValueForCharacteristicsetNotifyValue:forCharacteristic: 的区别, readValueForCharacteristic适合用来读取数据不怎么更新的特征值, 如果获取的数据是经常更新的,那就 一定要用setNotifyValue:forCharacteristic:来订阅这个特征;

  5. 当我们写入命令时writeValue:forCharacteristic:type:,这个type类型到时是用CBCharacteristicWriteWithResponse还是用CBCharacteristicWriteWithoutResponse会有疑惑,先看一下特性属性的枚举,它们是可以组合的

        /*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};

    再来看看我打印的两个特征值,第一个是获取Mac地址的特性,另一个是获取数据的特性
    <CBCharacteristic: 0x1700b8ae0, UUID = System ID, properties = 0x2, value = (null), notifying = NO>
    <CBCharacteristic: 0x1702a2a00, UUID = FFF6, properties = 0x16, value = (null), notifying = NO>
    第一个0x2对应只可读, 第二个 (0x16 & 0x08)不成立,(0x16 & 0x04)成立,所以用CBCharacteristicWriteWithoutResponse,而且这个特征值还可读,可以通知

  6. 代理方法- (void)centralManagerDidUpdateState:(CBCentralManager *)central;一定要调用,否则会报错,这个方法只要设置中心设备的代理之后,就一定会走,我们最开始的扫描外设应放在这个方法里;

  7. 对于是否要单独创建一个工具类来获取蓝牙数据,如果只是一个界面需要用到蓝牙数据,我觉得完全没必要,如果是多个界面的话,最好还是创建一个工具类。

  8. 如果蓝牙支持要支持后台模式,只需要去把蓝牙后台模式打开

    后台运行蓝牙

    记住只要勾选Uses Bluetooth LE accessories就行了,别勾选Acts As a Bluetooth LE accessory,除非你把你的手机当做外部设备使用;

简单又详细的Demo地址


作者:alenpaulkevin链接:http://www.jianshu.com/p/0ccfd53fc559來源:简书著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

转载于:https://www.cnblogs.com/Ghosgt/p/7779463.html

iOS 蓝牙开发和注意点相关推荐

  1. iOS蓝牙开发---CoreBluetooth[BLE 4.0] 初级篇[内附Demo地址]

    一.蓝牙基础知识 (一)常见简称 1.MFI  make for ipad ,iphone, itouch 专们为苹果设备制作的设备,开发使用ExternalAccessory 框架(认证流程貌似挺复 ...

  2. iOS 蓝牙开发 BabyBluetooth蓝牙库介绍

    BabyBluetooth 是一个最简单易用的蓝牙库,基于CoreBluetooth的封装,并兼容iOS和Mac OS X. 特色: 基于原生CoreBluetooth框架封装的轻量级的开源库,可以帮 ...

  3. iOS 蓝牙开发资料记录

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

  4. iOS蓝牙开发:蓝牙连接和数据读写

    当下蓝牙开发可谓是越来越火,不论是智能穿戴的兴起还是蓝牙家具,车联网蓝牙等等,很多同学也会接触到蓝牙的项目,我从事蓝牙开发也有一段时间了,经手了两个项目.废话不多说了,先向大家简单的介绍有关蓝牙开发的 ...

  5. ios 蓝牙开发总结

    随着蓝牙低功耗技术BLE(Bluetooth Low Energy)的发展,蓝牙技术正在一步步成熟,如今的大部分移动设备都配备有蓝牙4.0,相比之前的蓝牙技术耗电量大大降低.从iOS的发展史也不难看出 ...

  6. Andorid/IOS 蓝牙开发总结

    IOS 蓝牙 ios 蓝牙依赖CoreBluetooth 库 1 首先增加库 CoreBluetooth    general-> Linked Frameworks and lib 2 权限i ...

  7. iOS蓝牙开发数据实时传输

    随着iOS项目开发  很多app需要通过蓝牙与设备连接 蓝牙开发注意: 先定义中心设备和外围设备以及遵守蓝牙协议 @interface ViewController()<CBCentralMan ...

  8. iOS蓝牙开发:蓝牙的连接和数据的读写

    蓝牙开发说简单也简单,说不简单也有点难,开发人员在首次开发蓝牙前首先需要搞清楚蓝牙开发的概念,还要了解掌握蓝牙开发的一整套流程,这样才能快速上手开发蓝牙. 蓝牙开发分为两种模式:管理者模式和中心者模式 ...

  9. iOS蓝牙开发连接系统或其他APP已经连接、配对成功的蓝牙设备

    在蓝牙开发的时候,扫描外设的方法扫描到的设备只能是没有被连接的设备,也就是说如果有设备在本手机中已经连接配对成功了之后就不会被 [_centralManager scanForPeripheralsW ...

  10. iOS蓝牙开发 --- 唯一标识问题

    需求: 当我们使用CoreBluetooth系统框架进行蓝牙开发的时候,有时因为某种功能需求需要拿到特定的蓝牙设备进行特定的操作,这就需要我们拿到能够代表特定的蓝牙设备的唯一标识,通过唯一标识来确认是 ...

最新文章

  1. MFC:怎么将程序窗口最小化到系统托盘
  2. Java修改文件夹下所有文件名
  3. GC和JVM调优实战
  4. 算法设计与分析——算法思想总结
  5. Python PCA降维小例子
  6. C语言与汇编语言的区别
  7. centos 6.5卸载Mysql
  8. 使用json把php数据传给js处理
  9. 翻译: Swift 中信号量的美妙之处
  10. 大华摄像机调试以及保存视频
  11. layabox 打印_Layabox 集成指南
  12. 华为人到底几点钟下班?
  13. refresh( )
  14. linux wps只读模式怎么取消,wps只读模式解除的两种方法
  15. HRZ学英语(类似尺取)
  16. html5跳动的心电图,jquery制作心电图跳动特效
  17. 看到自己的体检报告,小灰瑟瑟发抖
  18. android fake camera,码市 - No.22987 - android fake camera - 其他
  19. 2021年安徽省职业院校技能大赛网络搭建与应用竞赛
  20. 计算机接口3p,Roland(罗兰)宣布推出虚拟JX-3P合成器云乐器

热门文章

  1. 动态CSS - LESS学习总结
  2. VC++调试技巧学习总结
  3. 转载:JSON技术的调研报告(四种常见的JSON格式对比及分析)
  4. 数据结构1_java---单链表的操作,约瑟夫问题
  5. Python 虚拟环境:Virtualenv
  6. -webkit-overflow-scrolling与苹果
  7. Flash学习笔记(01)
  8. android imageButton 透明图片
  9. HDU 5001 概率DP || 记忆化搜索
  10. 【转】 asp.net从视频文件中抓取一桢并生成图像文件的方法 实现多语言本地化应用程序 自动返回上次请求页面...