您的位置:首页 > 移动开发 > IOS开发

iOS蓝牙4.0开发(一)与外设通讯

2015-05-11 17:20 323 查看
蓝牙协议本身经历了从1.0到4.0的升级演变,最新的4.0以其低功耗著称,所以一般也叫BLE(Bluetoothlow
energy)。

iOS有两个框架支持蓝牙与外设连接。一个是 ExternalAccessory。从ios3.0就开始支持,也是在iphone4s出来之前用的比较多的一种模式,但是它有个不好的地方,External
Accessory需要拿到苹果公司的MFI认证。

另一个框架则是本文要介绍的CoreBluetooth,在iphone4s开始支持,专门用于与BLE设备通讯(因为它的API都是基于BLE的)。这个不需要MFI,并且现在很多蓝牙设备都支持4.0,所以也是在IOS比较推荐的一种开发方法。

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

周边(Peripheral)是生成或者保存了数据的设备,中央(Central)是使用这些数据的设备。所有可用的iOS设备可以作为周边(Peripheral)也可以作为中央(Central),但不可以同时既是周边也是中央。

周边和中央这两个角色在CoreBluetooth框架中是用两个类来表示的,CBPeripheralManager这个类代表周边,CBCentralManager这个类代表中央。
在中央这边,一个CBPeripheral对象代表着相应的和中央连接着的周边;同样的,在周边这边,一个CBCentral
对象代表着相应的和周边连接着的中央。
你可以认为周边是一个广播数据的设备,他广播到外部世界说他这儿有数据,并且也说明了能提供的服务。另一边,中央开始扫描附近有没有服务,如果中央发现了想要的服务,然后中央就会请求连接周边,一旦连接建立成功,两个设备之间就开始交换传输数据了。

在中央这边,CBService类代表服务,CBCharacteristic
类代表特征。
在周边这边,CBMutableService类代表服务,CBMutableCharacteristic
类代表特征。

每个蓝牙4.0的设备都是通过服务和特征来展示自己的,一个设备必然包含一个或多个服务,每个服务下面又包含若干个特征。特征是与外界交互的最小单位。比如说,一台蓝牙4.0设备,用特征A来描述自己的出厂信息,用特征B来与收发数据等。

每一个服务和特征都需要用一个UUID(unique identifier)去标识,UUID是一个16bit或者128bit的值。如果你要创建你的中央-周边App,你需要创建你自己的128bit的UUID。你必须要确定你自己的UUID不能和其他已经存在的服务冲突。如果你正要创建一个自己的设备,需要实现标准委员会需求的UUID;如果你只是创建一个中央-周边App,我建议你打开Mac
OS X的Terminal.app,用uuidgen命令生成一个128bit的UUID。你应该用该命令两次,生成两个UUID,一个是给服务用的,一个是给特征用的。然后,你需要添加他们到中央和周边App中。

蓝牙设备硬件厂商通常都会提供他们的设备里面各个服务(service)和特征(characteristics)的功能,比如哪些是用来交互(读写),哪些可获取模块信息(只读)等。

app和硬件之间通讯,app的角色是central,硬件的角色就是peripheral,如果是app和app之间通讯,那么一个iOS设备是central,另一个iOS设备就是peripheral了。这里要介绍的是app和硬件之间的通讯,也就是介绍central,下一节,我们会通过介绍两个iOS设备之间的蓝牙通讯来详细介绍peripheral。

实现细节
作为一个中心要实现完整的通讯,一般要经过这样几个步骤:

建立中心角色—扫描外设(discover)—连接外设(connect)—扫描外设中的服务和特征(discover)—与外设做数据交互(explore
and interact)—断开连接(disconnect)。

首先在我自己类的头文件中要包含CoreBluetooth的头文件,并继承两个协议CBCentralManagerDelegate,
CBPeripheralDelegate

1.创建我们的中心角色
将CBCentralManagerDelegate协议委托给当前类实例,这里有个@required协议方法
- (void)centralManagerDidUpdateState:(CBCentralManager
*)central;
当中心角色的状态发生变化的时候会触发这个方法。在这个方法中通过获取中心角色的状态来对它发布命令,你只能在central的state是CBCentralManagerStatePoweredOn的时候对它发布命令,当status的值低于CBCentralManagerStatePoweredOn的时候意味着扫描停止,并且任何已连接的外设都已经断开,当status的值低于CBCentralManagerStatePoweredOff时,所有的外设对象将从中心角色中作废,而且必须重新被发现。

_centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];


2.扫描外设

创建中心角色以后,我们在centralManagerDidUpdateState:方法中获取central的status是CBCentralManagerStatePoweredOn的时候来扫描外设

[self.centralManager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]]
                                                options:@{ CBCentralManagerScanOptionAllowDuplicatesKey : @YES }];


这里的TRANSFER_SERVICE_UUID就是硬件厂商提供的服务UUID,如果第一个参数指定为空,那么所有的外设服务将被返回(不推荐)



3.连接外设

当扫描到外设以后,系统会通过以下代理方法
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary
*)advertisementData RSSI:(NSNumber *)RSSI
告诉我们设备的信息,然后我们就可以连接相应的设备,我们可以通过RSSI.integerValue来获取信号强度,在这个方法中,我们可以连接我们想要连接的外设,代码如下:

[self.centralManager connectPeripheral:peripheral options:nil];


如果连接失败,会触发代理方法

- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;

如果连接成功,会触发代理方法:
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;
我们可以在这里扫描外设中的服务。

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

[peripheral discoverServices:@[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]]];


如果参数为空,将会返回外设中所有可用的服务。通过如下代理方法返回被扫描的服务。
- (void)peripheral:(CBPeripheral
*)peripheral didDiscoverServices:(NSError *)error;

如果error不为空,则扫描失败,反之,扫描成功,我们可以继续扫描服务中的特征

[peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID]] forService:service];


这里的TRANSFER_CHARACTERISTIC_UUID就是硬件厂商提供的特征UUID,如果第一个参数指定为空,那么所有的该服务的特征将被返回。
通过如下代理方法返回被扫描的特征。

- (void)peripheral:(CBPeripheral
*)peripheral didDiscoverCharacteristicsForService:(CBService
*)service error:(NSError *)error;

如果error不为空,则扫描失败,反之,扫描成功,我们可以继续枚举服务中的特征,我们通过CBCharacteristic的properties来读取特征的属性,

typedef NS_OPTIONS(NSUInteger, CBCharacteristicProperties) {
	CBCharacteristicPropertyBroadcast									= 0x01,
	CBCharacteristicPropertyRead										= 0x02,
	CBCharacteristicPropertyWriteWithoutResponse								= 0x04,
	CBCharacteristicPropertyWrite										= 0x08,
	CBCharacteristicPropertyNotify										= 0x10,
	CBCharacteristicPropertyIndicate									= 0x20,
	CBCharacteristicPropertyAuthenticatedSignedWrites							= 0x40,
	CBCharacteristicPropertyExtendedProperties								= 0x80,
	CBCharacteristicPropertyNotifyEncryptionRequired NS_ENUM_***AILABLE(NA, 6_0)		= 0x100,
	CBCharacteristicPropertyIndicateEncryptionRequired NS_ENUM_***AILABLE(NA, 6_0)	= 0x200
};


数据的读分为两种,一种是直接读,另外一种是通知。当属性为CBCharacteristicPropertyRead是可读,CBCharacteristicPropertyNotify是可通知,
当特征的属性是可通知的时候,我们通过如下设置来支持通知,如果你希望特征的value发生变化的时候我们被告知,就要设置YES,反之为NO。

[peripheral setNotifyValue:YES forCharacteristic:characteristic];


这个设置会通过以下代理方法来告知是否设置成功,如果设置成功,error为nil,反之失败。
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic
*)characteristic error:(NSError *)error;
设置成功以后,会通过以下代理方法告知我们characteristic的value发生变化,同样地,这个代理方法也会在你调用readValueForCharacteristic:方法的时候被触发。

- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error;

5.与外设做数据交互

读上面已经讲到。
写数据:

[_discoveredPeripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];

通过CBCharacteristicWriteWithResponse属性,我们可以通过以下代码方法被告知数据是否发送成功,error为空则成功,否则失败。

- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error;

6.断开连接

[self.centralManager cancelPeripheralConnection:peripheral];


我们可以通过以下代码方法被告知是否断开成功,error为空则成功,否则失败。

- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;

参考资料

- http://www.2cto.com/kf/201405/303572.html

- /article/1586111.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: