写在前面:
很久没有做记录,一方面是因为工作比较忙,另一方面也是因为最近在准备马拉松,所以平时空余的时间训练的比较多,个人觉得这一辈子起码要完成一次马拉松嘛~虽然只是个半程=、= 但是对于我等整天坐在公司、躺在家里的程序员们来说,挑战还是不小的,所以平时的训练也不敢懈怠。
在马拉松结束后,差不多也要到明年一月初了,计划的项目会稍微少一些,打算那个时候把这几个月关于BLE的学习与遇到的问题做个汇总。在此立个flag!
为何要获取蓝牙外设的Mac地址?
蓝牙外设的Mac地址,简而言之就是这个物理地址,每个设备都是唯一的,可以理解为省份证号。
一般在做BLE的开发中,会遇到通过app去绑定一个蓝牙外设的需求,由于在app关闭后,BLE就会断开连接,所以第二次打开app的时候,就需要通过绑定时保存在本地或者数据库中的这个外设的信息,来进行定向扫描以及绑定。所以这里就涉及到了这个外设信息的唯一性的问题,如果不能保证保存的这个外设信息的唯一性的话,可能我第二次打开app就连接不到之前的设备,或者连接到别的设备。
然而万恶的苹果在原生的CoreBluetooth中,将设备的Mac进行了封装(通过外设的Mac地址和手机的Mac地址进行了加密计算),最后对外提供了一个UUID,在一台手机上,一般情况,UUID就可以作为这个外设的唯一标识了,但是如果换了一台手机的话,可能就会发生变化,所以如果需求是需要在多台手机上的话,UUID可能就不太实用了。
特别是需要兼容安卓的情况下,安卓是可以获取到Mac地址的,所以我们就只能想办法获取外设的Mac地址了。
这里提供两种方法来获取Mac地址,获取的时机也不一样:
1.在连接后获取
这里参考了这个博客,博主自己对原生的CoreBluetooth做了分装,并且用自己的封装类去实现的,这里我们转换为原生下的Mac获取
//连接成功
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
peripheral.delegate = self;
//查找mac地址
[peripheral discoverServices:@[[CBUUID UUIDWithString:@"180A"]]];
}
#pragma mark - CBPeripheralDelegate
//发现到服务
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
CBService *service = peripheral.services.firstObject;
[peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:@"2A23"]] forService:service];
}
//获得某服务的特征
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{
[peripheral readValueForCharacteristic:characteristic];
}
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
NSString *value = [NSString stringWithFormat:@"%@",characteristic.value];
NSMutableString *macString = [[NSMutableString alloc] init];
[macString appendString:[[value substringWithRange:NSMakeRange(16, 2)] uppercaseString]];
[macString appendString:@":"];
[macString appendString:[[value substringWithRange:NSMakeRange(14, 2)] uppercaseString]];
[macString appendString:@":"];
[macString appendString:[[value substringWithRange:NSMakeRange(12, 2)] uppercaseString]];
[macString appendString:@":"];
[macString appendString:[[value substringWithRange:NSMakeRange(5, 2)] uppercaseString]];
[macString appendString:@":"];
[macString appendString:[[value substringWithRange:NSMakeRange(3, 2)] uppercaseString]];
[macString appendString:@":"];
[macString appendString:[[value substringWithRange:NSMakeRange(1, 2)] uppercaseString]];
DLog(@"mac == %@",macString);
}
用自家的几台设备做了测试,都可以获取到Mac地址,建议在使用前先demo测试下。
这种情况实在连接上了设备之后,通过读取他的180A服务里的2A23特征值,再去进行剪切和拼接的方法来获取Mac地址。那么问题来了,有些时候我的周边有很多蓝牙设备,我不可能一台一台去连接然后获取Mac地址,再判断是不是我保存的设备!这样太耗时了,而且可能会导致一些莫民奇妙的bug,所以我们的需求就变成了如何仅仅在扫描的过程中就获取到每台外设的Mac地址呢?扫描的过程是很快的,可以在短短几秒钟内就可以找到周围的上百台设备。
这个时候我们就只有使用第二种方法了,但是这种方法需要你么硬件工程师的支持了。
2.在扫描的过程中就获取设备的Mac地址
在发现设备的回调里面:
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
可以看到有一个 advertisementData ,这个字典类型的就是广播包,这里面会有一些设备的属性,比如设备的名字啊,服务啊等,但是都是被苹果限制了的,所以并不是你硬件工程师想广播什么都可以的。
这个字典里有一个:
key:kCBAdvDataManufacturerData
只有这个key是可以放入信息的,所以叫硬件工程师将Mac地址写到这个字段里去,这样你就可以在发现设备的过程中得到Mac地址了~!
//查找到正在广播的指定外设
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData: (NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
if (peripheral.name != nil ) {
if (![self.deviceArr containsObject:peripheral]) {
[self.deviceArr addObject:peripheral];
BleDevice *device = [[manridyBleDevice alloc] initWith:peripheral andAdvertisementData:advertisementData andRSSI:RSSI];
}
}
}
//BleDevice.m
- (instancetype)initWith:(CBPeripheral *)cbPeripheral andAdvertisementData:(NSDictionary *)advertisementData andRSSI:(NSNumber *)RSSI
{
BleDevice *per = [[BleDevice alloc] init];
per.peripheral = cbPeripheral;
per.deviceName = cbPeripheral.name;
per.uuidString = cbPeripheral.identifier.UUIDString;
NSData *data = [advertisementData objectForKey:@"kCBAdvDataManufacturerData"];
NSString *mac = [NSStringTool convertToNSStringWithNSData:data];
mac = [mac stringByReplacingOccurrencesOfString:@" " withString:@""];
per.macAddress = mac;
per.RSSI = RSSI;
return per;
}
这就是我目前发现的两种获得蓝牙外设Mac地址的方法,如果有什么更好的方法,也麻烦评论告诉我~大家一起学习!
以上!