刚踏入iOS大门的小白,由于在深圳工作,所以与硬件设备打交道是不可避免的,尤其是蓝牙设备,所以将自己学习和实践得到的一些经验在这里分享一下,同时感谢众多博文的写手无私的将经验分享给我这样的新人,写的很简陋,望大家批评指正。
环境信息:
Mac OS X 10.11.4
iOS 9.3.2
Xcode 7.3.1
Bluetooth 4.0
正文:
公司主要做车联网方面的业务,正准备做一款可以与下位机实时通讯的app,所以不得不去了解一下蓝牙方面的知识。
最可靠的信息来源当数官方提供的文档,全面,细致,但我一点没看过,因为看英文就想睡觉,如果你有兴趣可以点下方链接:
官方api
IOS蓝牙介绍
苹果自4s之后支持CoreBluetooth框架,可以以central身份去连接支持ble 蓝牙4.0的设备获取数据,在ios6之后可以以peripheral身份被其他设备连接提供数据。蓝牙4.0以低功耗著称,说白了就是更省电,所以在智能家居、车联网、智能穿戴设备上应用广泛,前景不错,值得日后更深入的研究。
其他相关的理论资料网上很多,我也就不再赘述,但建议还是多看看官方文档或者找一两个介绍全面的博文研究,不然收集一大堆散碎的信息会让你很头大。本文就以我写的一个用来测试公司蓝牙模块数据收发能力的小demo为例介绍一下以iphone为central时一些必要的方法。如学习再有斩获,也会实时更新。
第一步 初始化
_manager = [[CBCentralManager alloc]initWithDelegate:self queue:nil];
第一步我们先初始化CBCentralManager,queue设为nil表示在主线程中执行。
当执行完初始化,会调用@method centralManagerDidUpdateState:方法,他会判断当前设备是否适合成为central。而我们也可以通过这个方法检测设备蓝牙是否可用。
-(void)centralManagerDidUpdateState:(CBCentralManager *)central
{
switch (central.state) {
case CBCentralManagerStatePoweredOn:
NSLog(@"蓝牙已打开, 请扫描外设!");
break;
case CBCentralManagerStatePoweredOff:
NSLog(@"蓝牙未打开!");
break;
default:
break;
}
}
第二步 搜索当前可用peripheral
我们通过@method scanForPeripheralsWithServices:options:方法扫描可用外设。
-(void)scan{
NSDictionary* Options = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:CBCentralManagerScanOptionAllowDuplicatesKey];
if(_peripheral != nil){
[_manager cancelPeripheralConnection:_peripheral];
}
[self.manager scanForPeripheralsWithServices:nil options:Options];
}
其中Services:nil表示扫描附近所有可用外设,你也可以传一个uuid的数组或者单个的uuid,但我尝试过设置蓝牙模块的uuid并不能扫描到,不知道为什么。需要说明的一点是,CoreBluetooth搜索到服务和特征的uuid都是经过优化的16位,不是128位,但并不影响使用。options 这个参数可以设为nil,表示多次扫描到同一个外设视为同一个事件,在某些特定场景下可以做如上面代码的处理,比如得到实时rssi,但会耗费设备的电量和性能。
一旦扫描成果,会调用@method centralManager:didDiscoverPeripheral:advertisementData:RSSI:方法。
- (void) centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
[_peripherals addObject:peripheral];
[self.manager stopScan];
}
我们可以将扫描到的外设存在一个数组里,这里有个坑,就是当你想连接外设之前,一定要[self.manager stopScan],否则很有可能点半天没有反应,还以为自己代码有问题。当然,在开始搜索的时候我们也可以设置一个扫描超时的处理。
double delay = 15;
dispatch_time_t Time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay* NSEC_PER_SEC));
dispatch_after(Time, dispatch_get_main_queue(), ^(void){
[self.manager stopScan];
NSLog(@"扫描超时");
});
第三步 连接 peripheral
当我们需要连接外设时调用@method connectPeripheral:options:方法。
[self.manager connectPeripheral:_peripheral options:nil];
连接成功后会回调
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
NSLog(@"Peripheral has connect");
[_peripheral setDelegate:self];
}
**注意**:你需要给搜到的这个Peripheral设置代理,这样才能收到<CBPeripheralDelegate>协议的其它回调。
连接失败后会回调
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
NSLog(@"periheral has fail error : %@", error);
}
而当连接中断时会调用
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
NSLog(@"periheral has disconnect error : %@", error");
}
第四步 搜索 peripheral 的 service
现在已经成功搜索到我们想要的外设了,下一步就是看他给我们提供了哪些service。调用@method peripheral:didDiscoverServices:方法。
[_peripheral discoverServices:nil];
成功后回调@method peripheral:didDiscoverServices:方法。
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
for (CBService *service in peripheral.services)
{
if ([service.UUID isEqual:[CBUUID UUIDWithString:@"xxxx"]]){
NSLog(@"Service found with UUID: %@", service.UUID);
}
}
}
此处我的代码其实有些多余,当你明确知道自己需要的service时(不知道具体service的uuid可以下载个lightblue就啥都知道了),可以在[_peripheral discoverServices:nil]这一步直接搜索,高效且省电。
第五步 搜索 service 的 characteristic
既然知道了service,那就该搜索他的characteristic了,调用@method discoverCharacteristics:forService:方法。
[peripheral discoverCharacteristics:nil forService:service];
同理,如果你明确知道需要哪个characteristic,直接在上面设置就好。
成功找到所需characteristic后会回调@method peripheral:didDiscoverCharacteristicsForService:error:方法。
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
for (CBCharacteristic *characteristic in service.characteristics)
{
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"xxxxx"]]) {
NSLog(@"characteristic found with UUID: %@", characteristic.UUID);
}
}
}
第六步 读取 订阅characteristic数据
当你想读取characteristic数据时可以调用@method readValueForCharacteristic:方法。
[peripheral readValueForCharacteristic:characteristic];
之后会回调@method peripheral:didUpdateValueForCharacteristic:error:方法。
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
NSLog(@"读取到的数据:%@",characteristic.value);
}
但 @method readValueForCharacteristic:并不能实时获取数据,需要我们多次的调用,而实际情景往往需要实时获取数据,这时就需要订阅characteristic。调用@method setNotifyValue:forCharacteristic:方法。
[_peripheral setNotifyValue:YES forCharacteristic:characteristic];
将第一个参数设为yes。订阅成功会回调@method peripheral:didUpdateNotificationStateForCharacteristic:error:方法。
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
}
需要注意的是订阅得到数据还是通过@method peripheral:didUpdateValueForCharacteristic:error:方法回传。
第七步 向 characteristic 写数据
当我们需要向蓝牙模块中写数据时,调用@method writeValue:forCharacteristic:type:方法。
[self.peripheral writeValue:value forCharacteristic:_writeCharacteristic type:CBCharacteristicWriteWithResponse];
type的参数表示当写入成功需要回调。回调的方法是@method peripheral:didWriteValueForCharacteristic:error:。
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
if (error) {
NSLog(@"write characteristic value Error: %@", [error localizedDescription]);
}
}
OK,以上便是以iphone为central时的一些必要方法,按着一步一步来就可以实现一些基本的数据读写操作了,对CoreBluetooth框架也会有一定的了解。第一次写博客,有不足之处还望大家谅解并反馈给我,之后还会有一些补充就写在下一篇博客里吧~