iOS中的蓝牙 CoreBluetooth蓝牙系列
前言
iOS蓝牙.png此篇作为对iOS蓝牙模块一个框架上的理解和概念 适合初次接触iOS蓝牙开发的同学 主要也是我为了对工作项目中的蓝牙开发做一个梳理和总结
后面我会封装一个蓝牙Demo 对CB API层做一层Block的封装 使用更方便 对外设数据的常见进制、大小端、文件解析等做一定Category的支持 对硬件DFU升级模式做一些处理等
iOS中的蓝牙
BLE:(Bluetooth low energy)蓝牙4.0设备因为低耗电,也叫BLE
iOS中提供了4个框架用于实现蓝牙连接。
- 1.GameKit.framework(用法简单)
只能用于iOS设备之间的同个应用内连接,多用于游戏(eg:棋牌类),从iOS7开始过期- 2.MultipeerConnectivity.framework(代替1)
只能用于iOS设备之间的连接,从iOS7开始引入,主要用于非联网状态下,通过wifi或者蓝牙进行文件共享(仅限于沙盒的文件),多用于附近无网聊天- 3.ExternalAccessory.framework(MFi)
可用于第三方蓝牙设备交互,但是蓝牙设备必须经过苹果MFi认证(国内很少)- 4.CoreBluetooth.framework(常用 Apple推行蓝牙的核心)
可用于第三方蓝牙设备交互,必须要支持蓝牙4.0
硬件至少是4s,系统至少是iOS6 (现iPhoneX 蓝牙5.0)
蓝牙4.0以低功耗著称,一般也叫BLE(Bluetooth Low Energy)
目前应用比较多的案例:运动手环,嵌入式设备,智能家居
我们公司对接的外设比较多 心率计、踏频、码表等运动蓝牙设备
涉及到使用的框架HealthKit/物联网HomeKit/wathOS1,2/iBeacon
使用CoreBluetooth框架做蓝牙连接传输
在iPhone上使用蓝牙有两种模式 可以一对一 也可以一对多(需服务通道不同)
现一般主要使用于第三方/自家蓝牙硬件智能产品(如:心率计设备、蓝牙音箱、蓝牙手环、蓝牙车锁等等)
蓝牙传输上限是20字节 所以大数据传输会涉及到拆包、拼包、校验等
蓝牙连接流程
- 建立中心设备管理者
- 扫描外设
- 连接外设
- 扫描外设中的服务
- 扫描外设中的特征
- 订阅或读取特征值
- 获取外设中的数据或将数据写入外设
中心模式 CenterManager (一般Phone作为中心设备)
手机可以作为中心设备也可以作为外设来使用 CenterManage主要处理对蓝牙状态的控制和对外围设备的状态处理
- 建立中心角色
- 监听蓝牙状态
- 扫描外设(Discover Peripheral) 接收外设蓝牙广播 (可以扫描包含制定服务的设备)
- 连接外设(Connect Peripheral)
- 断开连接(Disconnect)
外设模式
CenterManager 扫描链接外设成功后 启动一个Peripheral外设管理对象 负责外设数据的操作处理
- 启动一个Peripheral外设管理对象 负责外设数据的操作处理
- 扫描外设中的服务和特征(Discover Services And Characteristics)
- 获取外设的services (基本服务(电池信息和设备信息)、硬件自定服务)
- Discover指定Service下的特征 获取外设的Characteristics,
- 通过指定特征( Characteristics)订阅(Notiy)/读取(Read)/写入(Write) 等操作
- 获取Characteristics的Descriptor和Descriptor的值
- 根据业务做处理
Characteristics作为蓝牙数据传输操作的做小单元
什么是服务和特征(service and characteristic)
每个设备都会有1个or多个服务
每个服务里都会有1个or多个特征
特征就是具体键值对,提供数据的地方
每个特征属性分为:读、写、通知等等
代码Coding 使用CoreBluetooth相关API
建议先看一下官方文档Core Bluetooth Communicate with Bluetooth 4.0 low-energy devices.
调试iOS蓝牙的时候,可以下个LightBlue,非常方便,网上也有仿写LightBlue的Demo,参考这两处:DarkBlue
Lightblue
工具我使用的是LightBlue 可以直接去App Store下载 硬件同事也钟爱这个App 调试服务/特征很方便
- 导入CoreBluetooth头文件
#import <CoreBluetooth/CoreBluetooth.h>
CBCentralManager 中心设备的初始化和外设的链接
- 初始化CBCentralManager 中心设备管理者 注意初始化的参数
设置Delegate
设置制定队列Queue
设置可选参数options:可以设为nil
也可以查看CBCentralManagerOptionShowPowerAlertKey
蓝牙提醒弹窗和CBCentralManagerOptionRestoreIdentifierKey
字符串,一个唯一的标示符,用来蓝牙的恢复连接的。在后台的长连接中可能会用到
其他key可以查看这篇文章CBCentralManagerConstants
- (instancetype)init
{
if (self = = [super init]) {
dispatch_queue_t queue = dispatch_queue_create("com.imxingzhe.bici", 0);
_centralManager = [[CBCentralManager alloc] initWithDelegate:self
queue:queue
options:@{CBCentralManagerOptionShowPowerAlertKey:[NSNumber numberWithBool:NO]}];
_discoveredPeripherals = [[NSMutableArray alloc] init];
}
return self;
}
- 基本API 所有的状态回调都在delegate里做处理
//主动发起扫描外设 开始扫描符合服务serviceUUIDs的外设
- (void)scanForPeripheralsWithServices:(nullable NSArray<CBUUID *> *)serviceUUIDs options:(nullable NSDictionary<NSString *, id> *)options;
//主动停止扫描
- (void)stopScan;
//主动连接指定外设
- (void)connectPeripheral:(CBPeripheral *)peripheral options:(nullable NSDictionary<NSString *, id> *)options;
//主动断开指定外设
- (void)cancelPeripheralConnection:(CBPeripheral *)peripheral;
- Delegate的回调使用
#pragma mark - CBCentralManagerDelegate
//更新蓝牙状态
- (void)centralManagerDidUpdateState:(CBCentralManager *)central{
// Determine the state of the peripheral
if ([central state] == CBCentralManagerStatePoweredOff){
NSLog(@"CoreBluetooth BLE hardware is powered off");
}else if ([central state] == CBCentralManagerStatePoweredOn){
NSLog(@"CoreBluetooth BLE hardware is powered on and ready");
}else if ([central state] == CBCentralManagerStateUnauthorized){
NSLog(@"CoreBluetooth BLE state is unauthorized");
}else if ([central state] == CBCentralManagerStateUnknown) {
NSLog(@"CoreBluetooth BLE state is unknown");
}else if ([central state] == CBCentralManagerStateUnsupported) {
NSLog(@"CoreBluetooth BLE hardware is unsupported on this platform");
}
}
// 扫到设备会进入到此代理方法 对应Scan方法 advertisementData:广播包 RSSI:信号强度
- (void)centralManager:(CBCentralManager *)central
didDiscoverPeripheral:(CBPeripheral *)peripheral
advertisementData:(NSDictionary<NSString *,id> *)advertisementData
RSSI:(NSNumber *)RSSI
{
NSLog(@"%s, line = %d, per = %@, data = %@, rssi = %@", __FUNCTION__, __LINE__, peripheral, advertisementData, RSSI);
}
//代理返回已经链接成功的peripheral 外设
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
NSLog(@"获取链接成功的外设 可以操作外设处理数据传输");
}
//外设链接断开时会回调
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
NSLog(@"获取断开链接的外设 处理释放业务逻辑");
}
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
NSLog(@"连接失败 获取错误信息 处理");
}
CBPeripheral外设的状态和数据传输处理
Orz...
特殊机制处理(自动重连、多设备连接等)
Orz...
关于Mac地址的获取
自iOS7之后,苹果不支持获取Mac地址,只能用UUID来标识设备,要注意的是同一个设备在不同手机上显示的UUID不相同,但有的设备可以通过 “180A”这个服务来发现特征,再来读取 “2A23”这个特征值,可以获得Mac地址。如果你的蓝牙设备不支持这样获取,你可以跟硬件工程师沟通,来获得Mac地址,添加一个获取地址命令或者增加一个含地址的特征值都可以很容易的获取。上面获取地址的前提都是需要先建立连接,如果一定要在扫描的时候获得Mac地址,让硬件工程师把数据写入广播包里,根据需求决定;
未完待续....