概述
蓝牙,一种无线通讯技术标准,用来在短距离间交换数据,以形成个人局域网。其使用短波特高频无线电波。1994年由电信商爱立信发展出这个技术,从早期的1.0到目前最新的5.0版本,中间经过了十几年的发展。这里着重关注的是蓝牙4.0以及4.0之后的规范,蓝牙4.0于2010年7月7号推出,4.0之前的蓝牙统称为经典蓝牙,4.0之后又加入了低功耗功能并且有效的传输距离拓展到了最大的60米。如今蓝牙由蓝牙技术联盟(Bluetooth Special Interest Group
,简称SIG
)管理,负责监督蓝牙规范的开发,管理认证项目,并维护商标权益,制造商的设备也必须符合蓝牙技术联盟的标准才能以“蓝牙设备”的名义进入市场。蓝牙的波段为2400-2483.5MHz。这是全球范围内无需取得执照(但并非无管制的)的 2.4 GHz 短距离无线电频段。
技术类型
蓝牙技术分为基础率/增强数据率(BR/EDR)和低耗能(LE)两种技术类型。
应用
蓝牙技术已经应用到超过3万个联盟技术成员的82亿件产品之中。
1 | 2 | 3 |
---|---|---|
汽车 蓝牙免提调用系统;车载音频娱乐系统;监测和诊断机电系统 |
消费类电子产品 电视和游戏系统,家用游戏机的手柄,包括PS4、PSP Go、 Wii、Switch。 |
家居自动化 智能家居,室内的照明、温度、家用电器、窗户和门锁等安全系统以及牙刷、鞋垫等日常用品。 |
医疗和保健 血糖监测仪、脉搏血氧仪、心率监视器、哮喘吸入器等产品 |
手机 移动电话和免提设备之间的无线通讯,这也是最初流行的应用。 |
电脑与外设 鼠标、键盘、耳机、打印机等 |
可穿戴设备 智能眼镜、耳机、活动监测仪、儿童和宠物监视器、医疗救助、头部和手部安装终端以及摄像机 |
运动和健身 健身跟踪手环和智能手表,瑜伽垫、棒球棍等 |
零售和位置导向式服务 实时定位系统(RTLS),应用"节点"或"标签"嵌入被跟踪物品中读卡器从标签接收并处理无线信号以确定物品位置。 |
缺陷
干扰
Bluetooth在2.4GHz的电波干扰问题一直为大家所诟病,特别和无线局域网间的互相干扰问题。有干扰发生时,就以重新发送数据包的方法来解决干扰。
传输距离有限
iOS中的蓝牙
图片来源于网络使用过程
连接设备流程(图片来源于网络)苹果对蓝牙设备的要求
苹果在 iPhone 4S
及之后的手机型号开始支持蓝牙4.0,低于蓝牙4.0协议的设备需要进行MFI
认证。
MFi
什么是MFi认证
苹果MFi认证,是苹果公司对其授权配件厂商生产的外置配件的一种标识使用许可,是 Made for iPod, Made for iPhone, Made for iPad
的英文缩写。
苹果MFi认证类型及重要性
开发许可认证:打算开发或者帮助开发但不生产配件的厂商,可以申请MFi开发认证
生产许可认证:打算生产配件并且拥有生产设备的企业可以申请MFi生产许可证
一个拥有MFi技术开发证书的公司搭配一个拥有MFi生产制造证书的公司才能做出MFi认证的产品
nRF connect
nRF Connect
是一个功能强大的蓝牙工具,可让您扫描和浏览低功耗蓝牙设备并与之通信。苹果商店有售!
iOS蓝牙库
CoreBluetooth.framework
CoreBluetooth
框架提供了的iOS和Mac应用程序与配备了蓝牙低能耗无线技术的设备进行通信所需的类。例如,应用程序可以发现,搜索低耗能的外围设备并与之交互。从macOS 10.9和iOS 6开始,Mac和iOS设备还可以用作低功耗蓝牙外围设备,向其他设备(包括其他Mac和iOS设备)提供数据。
该框架是用于低能耗设备的蓝牙4.0规范的抽象。它向开发人员隐藏了规范的许多底层细节,使您更容易开发与蓝牙低功耗设备交互的应用。因为该框架基于规范,所以已经采用了规范中的一些概念和术语。
重要提示:在iOS 10.0或更高版本上的iOS应用程序必须在其Info.plist
文件中包含使用说明键,以说明需要访问的数据类型,否则它将崩溃。要专门访问蓝牙外围设备数据,它必须包含NSBluetoothPeripheralUsageDescription
。
中央和外围设备及其在蓝牙通信中的作用
所有低功耗蓝牙通信都涉及两个主要参与者:中央和外围设备。某种程度上基于传统的客户端-服务器体系结构,外围设备通常具有其他设备所需的数据。一个中心通常使用由外围设备提供的信息来完成一些特定的任务。如图所示,心率监视器可能会提供有用的信息,你的Mac或iOS应用程序可能需要这些信息,以便以用户友好的方式显示用户的心率。
中央设备和外围设备中心设备搜索并连接到发送广告的外围设备
外设以广告包的形式广播一些数据。广告包是一个相对较小的数据包,其中可能包含外设必须提供什么有用的信息比如外设的名称和主要功能。例如,数字恒温器可能会声明它提供房间当前的温度。在低能耗蓝牙技术中,广告是外设让其他设备知道其存在的主要方式。
另一方面,一个中心设备可以扫描和监听任何外围设备的广告信息,如图所示。中心设备可以要求连接到它发现的、发送广告的任何外设。
广告和发现外设的数据是如何构造的
连接外设的目的是发现外设提供的数据并与之交互。不过在进行此操作之前,了解外设数据的结构将有所帮助。
外设可以包含一个或多个服务,或提供有关其连接信号强度的有用信息。服务是完成设备(或该设备的一部分)的功能或特性的数据和相关行为的集合。例如,心率监控器的一项服务是可以从监控器的心率传感器公开心率数据。
服务本身由特征或包含的服务(即对其他服务的引用)组成。特征提供有关外设服务进一步的细节。例如,刚才描述的心率服务可能包含Body sensor location特征和传输心率测量数据的另一个特征。图片显示了心率监视器服务和特征一种可能的结构。
外围设备的服务和特性中心设备在外围设备上发现数据并与之交互
中心成功建立与外围设备的连接后,便可以发现外围设备必须提供的全部服务和特性(广告数据可能只包含一部分可用服务)。
中心还可以通过读取或写入该服务的特性值来与外围设备的服务进行交互。例如,应用程序可能从数字恒温器请求当前的室温,或者可能为恒温器提供一个用于设置室温的值。
中心,外围设备和外围设备数据的表示方式
低功耗蓝牙通信中涉及的主要参与者和数据以一种简单,直接的方式映射到Core Bluetooth
框架。
中心设备侧的对象
当使用本地中心与远程外设交互时,是在蓝牙低能耗通信的中心端执行操作。除非正在设置一个本地外围设备并使用它来响应中心的请求,否则大部分蓝牙相关事务将在中心端进行。
本地中心设备和远程外围设备
在中心设备侧,本地中心设备由CBCentralManager
对象表示。这些对象用于管理发现或连接的远程外围设备(以CBPeripheral
对象表示),包括扫描,发现和连接到广告外围设备。图片显示了如何在Core Bluetooth
框架中表示本地中心和远程外围设备。
远程外围设备的数据由CBService和CBCharacteristic对象表示
在与远程外围设备(由CBPeripheral
对象表示)上的数据进行交互时,实际是在处理其服务和特征。在Core Bluetooth框架中,远程外围设备的服务由CBService对象表示,远程外围设备的服务特征由CBCharacteristic
对象表示。图片说明了远程外围设备的服务和特征的基本结构。
作为外设
从macOS 10.9和iOS 6开始,Mac和iOS设备可以用作低功耗蓝牙外围设备,向其他设备(包括其他Mac,iPhone和iPad设备)提供数据。设置设备以实现外围设备角色时,则是在蓝牙低功耗通信的外围设备上执行操作。
本地外围设备和远程中心
在外设方面,本地外设由CBPeripheralManager对象表示。这些对象用来管理发布的服务,这些服务在本地外设上“特征和服务”数据库中,并将这些服务发布到远程中央设备(由CBCentral对象表示)。外设管理器对象还用于响应来自这些远程中心的读写请求。图片显示了如何在CoreBluetooth框架中表示本地外设和远程中心。
作为外设时的对象表示本地外围设备的数据由CBMutableService和CBMutableCharacteristic对象表示
在本地外围设备(由CBPeripheralManager
对象表示)上设置数据并与之交互时,您正在处理其服务和特性的可变版本。在Core Bluetooth框架中,本地外围设备的服务由CBMutableService
对象表示。同样,本地外围设备的服务特征由CBMutableCharacteristic
对象表示。图片说明了本地外围设备的服务和特征的基本结构。
iOS应用状态影响蓝牙行为
当您的iOS应用处于后台或挂起状态时,其与蓝牙相关的功能会受到影响。默认情况下,应用程序在后台或处于挂起状态时将无法执行低功耗蓝牙任务。就是说如果应用需要在后台执行蓝牙低能耗任务,则可以声明它支持一种或两种Core Bluetooth后台执行模式(其中一种用于中心角色,一种用于外围角色)。即使声明了这两种后台执行模式中的一种或两种,当的应用程序在后台运行时,某些蓝牙任务也会以不同的方式运行。在设计应用程序时,需要考虑这些差异。
系统甚至可以随时终止支持后台处理的应用程序,以释放当前前台应用程序的内存。从iOS 7开始,Core Bluetooth支持保存中央和外围设备管理器对象的状态信息,并在应用启动时恢复该状态。可以使用此功能来支持涉及蓝牙设备的长期操作。
BabyBlueTooth
GitHub开源库,封装了CoreBluetooth,将众多的delegate写成了block方法
MTU
MTU是在连接过程中协商的。iOS会自动使用两款设备支持的最大值。iOS支持185,所以这是支持的最大值。如果外围设备MTU小于185,则使用该值。没有开放的iOS API来请求MTU更改/更新。相反,此请求需要由外设发起。
Code
扫描蓝牙设备
初始化
引入头文件#import <CoreBluetooth/CoreBluetooth.h>
CBCentralManager
是蓝牙中心设备的管理类,控制着蓝牙的扫描,连接,蓝牙状态的改变。
dispatch_queue_t queue = dispatch_queue_create(@"centralQueue", DISPATCH_QUEUE_SERIAL);
NSDictionary *dict = @{CBCentralManagerOptionShowPowerAlertKey : @YES, CBCentralManagerOptionRestoreIdentifierKey : @"unique identifier"};
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:centralQueue options:dict];
CBCentralManagerOptionShowPowerAlertKey
设为YES
,表示初始化centralManager
时,如果蓝牙没有打开,则会弹出提示框
扫描
// CBCentralManagerScanOptionAllowDuplicatesKey设置为NO表示不重复扫描已发现的设备。
NSDictionary *option = @{CBCentralManagerScanOptionAllowDuplicatesKey : [NSNumber numberWithBool:NO], CBCentralManagerOptionShowPowerAlertKey:@YES};
[self.centralManager scanForPeripheralsWithServices:nil options:option];
// 可以在serviceUUIDs参数中提供表示服务uuid的CBUUID对象数组,这样中央管理器只返回指定的服务的外围设备。如果serviceUUIDs参数为nil,该方法将返回所有发现的外设,不管它们支持的服务是什么。
- (void)scanForPeripheralsWithServices:(nullable NSArray<CBUUID *> *)serviceUUIDs options:(nullable NSDictionary<NSString *, id> *)options;
CBCentralManagerDelegate代理方法
@required
,centralManager
初始化之后会调用这个方法,返回centralManager
的状态
- (void)centralManagerDidUpdateState:(CBCentralManager *)central;
/**
* @enum CBManagerState
*
* @discussion Represents the current state of a CBManager.
*
* @constant CBManagerStateUnknown State unknown, update imminent.
* @constant CBManagerStateResetting The connection with the system service was momentarily lost, update imminent.
* @constant CBManagerStateUnsupported The platform doesn't support the Bluetooth Low Energy Central/Client role.
* @constant CBManagerStateUnauthorized The application is not authorized to use the Bluetooth Low Energy role.
* @constant CBManagerStatePoweredOff Bluetooth is currently powered off.
* @constant CBManagerStatePoweredOn Bluetooth is currently powered on and available to use.
*
* @seealso authorization
*/
@available(iOS 10.0, *)
public enum CBManagerState : Int {
case unknown = 0
case resetting = 1
case unsupported = 2
case unauthorized = 3
case poweredOff = 4
case poweredOn = 5
}
发现外设
// CBPeripheral代表外设类
// advertisementData代表广播值,一般携带设备名,serviceUUIDs等信息
// RSSI代表信号值,绝对值越大信号越差,设备离的越远。
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI;
连接蓝牙设备
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
if ([peripheral.name isEqualToString:@"YDPen"]) {
// 发起连接
[self.centralManager connectPeripheral:peripheral options:nil];
self.peripheral = peripheral;
}
}
蓝牙连接状态
连接外设(connectPeripheral
)后会调用CBCentralManagerDelegate一些
代理方法
// 连接成功的回调
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;
// 连接失败的回调
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error;
// 连接断开的回调
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error;
外设的服务与特性
连接成功后还需传输数据,数据传输是以特性(CBCharacteristic)为基础。可以先通过 nRF connect 观察一下外设的一些属性
广告信息Advertisement Data
就是广播信息。
iOS蓝牙无法直接获取设备蓝牙MAC地址,可以将MAC地址放到这里广播出来。
服务与特征FEBE
是ServiceUUID
,可以以此来搜索外设。可以看到服务中包含的特性及其UUID和属性
Properties
是特征的属性,可以看出此特征可以有读
、写
、通知
的权限。特征拥有的权限类别有如下几种:
/*
* @enum CBCharacteristicProperties
*
* @discussion Characteristic properties determine how the characteristic value can be used, or how the descriptor(s) can be accessed. Can be combined. Unless
* otherwise specified, properties are valid for local characteristics published via @link CBPeripheralManager @/link.
*
* @constant CBCharacteristicPropertyBroadcast Permits broadcasts of the characteristic value using a characteristic configuration descriptor. Not allowed for local characteristics.
* @constant CBCharacteristicPropertyRead Permits reads of the characteristic value.
* @constant CBCharacteristicPropertyWriteWithoutResponse Permits writes of the characteristic value, without a response.
* @constant CBCharacteristicPropertyWrite Permits writes of the characteristic value.
* @constant CBCharacteristicPropertyNotify Permits notifications of the characteristic value, without a response.
* @constant CBCharacteristicPropertyIndicate Permits indications of the characteristic value.
* @constant CBCharacteristicPropertyAuthenticatedSignedWrites Permits signed writes of the characteristic value
* @constant CBCharacteristicPropertyExtendedProperties If set, additional characteristic properties are defined in the characteristic extended properties descriptor. Not allowed for local characteristics.
* @constant CBCharacteristicPropertyNotifyEncryptionRequired If set, only trusted devices can enable notifications of the characteristic value.
* @constant CBCharacteristicPropertyIndicateEncryptionRequired If set, only trusted devices can enable indications of the characteristic value.
*/
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(10_9, 6_0) = 0x100,
CBCharacteristicPropertyIndicateEncryptionRequired NS_ENUM_AVAILABLE(10_9, 6_0) = 0x200
};
数据传输
上面说过数据传输是基于特性的,下面看看如何使用CBPeripheral
及其代理方法,通过特性发送、读取、监听数据
首先看看CBPeripheral
的几个代理方法
// 发现服务
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(nullable NSError *)error;
// 发现特性
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(nullable NSError *)error;
// 数据有更新
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error;
// 在使用writeValue方法时,指定了写入类型为CBCharacteristicWriteWithResponse时,会调用此方法
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error;
在设备连接成功后,需要搜索服务及服务中的特性,需要使用如下方法
// 搜索外设中的服务,会调用代理的didDiscoverServices方法
- (void)discoverServices:(NSArray<CBUUID *> *)serviceUUIDs;
// 搜索特性,会调用代理的didDiscoverCharacteristicsForService方法
- (void)discoverCharacteristics:(NSArray<CBUUID *> *)characteristicUUIDs forService:(CBService *)service;
发现了特性之后可以据此来进行数据传输了
向外设发送数据
- (void)writeValue:(NSData *)data forCharacteristic:(CBCharacteristic *)characteristic type:(CBCharacteristicWriteType)type;
从外设读取数据
`- (void)readValueForCharacteristic:(CBCharacteristic *)characteristic;
当调用此方法读取特征值时,外围设备会调用其代理的peripheral:didUpdateValueForCharacteristic:error:
方法。如果外设成功读取了特征的值,则可以通过特征的value
属性访问它。并不是所有的特征都具有可读权限。可通过CBCharacteristicProperties
枚举的相关属性来确定特征是否可读。
监听通知
- (void)setNotifyValue:(BOOL)enabled forCharacteristic:(CBCharacteristic *)characteristic;
监听到从特征发来的数据时会调用peripheral:didUpdateValueForCharacteristic:error:
代理方法
网友评论