本文主要介绍基于蓝牙4.0的智能硬件开发。蓝牙4.0主要基于<CoreBluetooth/CoreBluetooth.h>这个库文件,下面将以此介绍每个类的作用.并提供自己对这些类封装,最后附有代码的连接。
CBCentralManager:负责查询用户蓝牙的状态、扫描周围可见的外设、停止扫描、连接指定的外设等方法通过该类提供,并且通过代理的形式回调当前蓝牙的状态;
-(void)centralManagerDidUpdateState:(CBCentralManager *)central {
}
扫描到的外设;
-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI {
}
连接指定外设
[centralManager connectPeripheral:peripheral options:nil];
以及连接外设的状态。
//断开连接
-(void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
}
//连接失败
-(void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
}
//连接成功
-(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
}
CBCentralManager的扫描和连接通过其.h文件我们都能一目了然看出,说一下查询用户状态这方法,这个方法的触发时机其实在初始化CBCentralManager的时候,系统内部会进行相应Api的调用,我们只要在对应的回调中监听即可。
[[CBCentralManager alloc] initWithDelegate:self queue:nil];
对于queue这个参数我在最后会做一些介绍,一般只需要给nil代表所有回调会在主线程中。
对于作过蓝牙相关开发的,有可能会对这一系列类进行一些封装,够后面使用,而对于CBCentralManager以懒加载的形式提供,这样容易造成一个问题--->用户蓝牙状态的查询加只会进行一次查询,在切换另一个业务页面的时候无法获取当前用户蓝牙状态,造成用户体验不佳。下面介绍CBPeripheral
CBPeripheral:一种对外设的抽象,一个CBPeripheral对应一个外设,它最重要的两个特性是外设的服务地址和特性地址,通过服务地址我们可以找到特性地址,找到特性地址我们就可以写数据了。下面来介绍具体流程。
在何时查找服务,当然是在连接外设成功的时候了
-(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
self.currentperipheral = peripheral;//保存起来,后面用的到
[peripheral setDelegate:self];
[peripheral discoverServices:nil];
}
从方法字面意思我们也可以看出大概delegate用来回调服务地址的查找情况,其中的services:是一个外设服务地的数组,可以传nil,则会查找外设的所有服务地址。
//发现的服务地址
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
}
到了这一步我们就可以比照相应硬件的协议文档了,如果协议文档中的服务地址被发现了,可以继续下一步了-->该服务地址下的特性地址。
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
for (CBService *service in peripheral.services) {
if ([service.UUID isEqual:[CBUUID UUIDWithString:addresString]]) {//协议文档提供的服务地址
[peripheral discoverCharacteristics:nil forService:service];
} else continue;
}
}
//对应服务地址下的特性值,特性值一般包含读和写,读用来接收外设的信息,写:向外设写数据。
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
for (CBCharacteristic *characteristic in service.characteristics) {
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:charactDict[BlueCharacteristicRead]]]) {
#ifdef DEBUG
//NSLog(@"the charact read address:%@",charactDict[BlueCharacteristicRead]);
#endif
[peripheral setNotifyValue:YES forCharacteristic:characteristic];//接收外设的数据
}
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:charactDict[BlueCharacteristicWrite]]]) {
#ifdef DEBUG
// NSLog(@"the charact write address:%@",charactDict[BlueCharacteristicWrite]);
#endif
self.characteristic = characteristic;//把写的特征值保存起来,用来向外设写值、
}
}
}
到这里我们就可以向外设写值和接收数据了
写:
[self.currentperipheral writeValue:data forCharacteristic:self.characteristic type:CBCharacteristicWriteWithResponse];
通过回调可知道写入是否失败(一般不需要处理)
-(void)peripheral:(CBPeripheral *)peripheral didWriteValueForDescriptor:(CBDescriptor *)descriptor error:(NSError *)error {
}
外设发送的数据是通过下面方法来接收
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
}
iOS蓝牙开发的大致流程介绍到此了,前面说过centralManager初始化之后还有个queue参数,该参数可以的作用就是把上面涉及到的一些回调都加入到该队列中,加入到该队列中好处就是可以控制代码的执行顺序,我们只要把链接,扫描等操作也加入到该队列,就达到顺序的控制,不过这个没很大必要。
下面我给一个我自己封装的一个蓝牙库。提供了Mac地址的读取,和搜索同一类型的外设,断开重新连接,使用起来不难。
[YYBlueManger sharedManger].delegate = self;
[[YYBlueManger sharedManger] scan];
#pragma mark - YYBlueMangerDelegate
-(void)blueManagerFailedState:(YYBlueManger *)manager statu:(YYBlueManagerState)statu{
//用户蓝牙状态不正常时候调用。
}
-(void)blueManagerblescanBluDevice:(YYBlueManger *)manager andTheDevice:(CBPeripheral *)peripheral {
//NSLog(@"%@",peripheral.name);
if ([peripheral.name isEqualToString:@"你想连接的外设名字"]) {
if ([YYBlueManger sharedManger].connectState == YYBlueConnectStateConnected || [YYBlueManger sharedManger].connectState ==YYBlueConnectStateConnecting) {
return;
}
[[YYBlueManger sharedManger] connect:peripheral orTheDeviceName:nil];
}
}
-(NSArray *)blueManagerServerAdrres:(YYBlueManger *)manager peripheral:(CBPeripheral *)peripheral {
return @[@"0xxxx"];
}
-(NSDictionary *)blueManagerCharacteristicsAdrres:(YYBlueManger *)manager peripheral:(CBPeripheral *)peripheral {
return @{BlueCharacteristicWrite:@"0xxxxx",BlueCharacteristicRead:@"0xxxxx"};
}
-(void)blueManagerdidConnectSuccees:(YYBlueManger *)manager peripheral:(CBPeripheral *)peripheral {
NSLog(@"连接成功");
[[YYBlueManger sharedManger] stop];
}
-(void)blueManagerFailedConnect:(YYBlueManger *)manager peripheral:(CBPeripheral *)peripheral error:(NSError *)error {
NSLog(@"连接失败");
[[YYBlueManger sharedManger] scan];
}
-(void)blueManagerCutConnect:(YYBlueManger *)manager peripheral:(CBPeripheral *)peripheral error:(NSError *)error {
NSLog(@"断开连接");
[[YYBlueManger sharedManger] scan];
}
[[YYBlueManger sharedManger] writeDataToDevice:nil writeHandle:nil];
github连接:https://github.com/jsonsnow/CoreBlue.git
好久没接触智能硬件了,这几天又开始智能硬件的开发,用自己写的库也发现其中存在几个问题
在resetConnect的时候,发现维持的connect状态混乱,因为在取消前一个已经连接的外设的时候会调用disConnect方法,导致connectSate = connectSateCut,此事发现同名字设备的时候,因为connectSate == connectSateCut,因此会进行连接,解决方法:增加一个cancelSate ,在disConnect中如果connectSate = cancelSate,则直接返回
增加一个打断协议,在将要连接外设的时候调用willConntetn方法,如果实现方返回YES,则不进行此次连接,主要针对多设备绑定而设计
取消Mac地址读取,以及不把已经连接的设备加入数据源中
增加一个搜索外设的方法,可以给定 @"xxx1,xxx2",则会搜索名字为xxx1和xxx2的外设,这个场景主要是针对外设名字不同但是其服务地址和特许值都相同的情形
目前最新的版本还没上传,如有需要可以留下邮箱