一、关于蓝牙开发的一些重要的理论概念
1.当前蓝牙开发所用的系统库为
2.蓝牙外设必须为4.0及以上(2.0需要MFI认证),否则无法开发,蓝牙4.0设备因为低耗电,所以也叫做BLE。
3.CoreBluetooth框架的核心其实是两个东西,peripheral和central, 可以理解成外设和中心,就是你的苹果手机就是中心,外部蓝牙称为外设。
4.服务和特征(service and characteristic):简而言之,外部蓝牙中它有若干个服务service(服务你可以理解为蓝牙所拥有的能力),而每个服务service下拥有若干个特征characteristic(特征你可以理解为解释这个服务的属性)。
5.Descriptor(描述)用来描述characteristic变量的属性。例如,一个descriptor可以规定一个可读的描述,或者一个characteristic变量可接受的范围,或者一个characteristic变量特定的单位。
二、蓝牙连接的主要步骤
1、创建一个CBCentralManager实例来进行蓝牙管理;
2、搜索扫描外围设备;
3、连接外围设备;
4、获得外围设备的服务;
5、获得服务的特征;
6、从外围设备读取数据;
7、给外围设备发送(写入)数据。
三、代码
//在Info.plist里加入访问蓝牙权限,否则上传AppStore会因为权限不足失败
//iOS13之前的蓝牙权限为Privacy - Bluetooth Peripheral Usage Description
//iOS13废弃了之前的蓝牙的权限,新加Privacy - Bluetooth Always Usage Description权限
1.初始化CBCentralManager实例
- (void)initCenterManager {
if (self.centralManager == nil) {
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
}
}
#pragma mark - CBCentralManagerDelegate
/// 初始化成功后自动调用
/// 必须实现的代理,用来返回创建的centralManager的状态。
/// 通常在CBManagerStatePoweredOn状态下直接调用扫描外设的方法:
/// @param central 中心
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
self.isBlueToothPowerOn = NO;
switch (central.state) {
case CBManagerStateUnknown:
NSLog(@">>>CBCentralManagerStateUnknown");
break;
case CBManagerStateResetting:
NSLog(@">>>CBCentralManagerStateResetting");
break;
case CBManagerStateUnsupported:
NSLog(@">>>CBCentralManagerStateUnsupported");
break;
case CBManagerStateUnauthorized:
NSLog(@">>>CBCentralManagerStateUnauthorized");
break;
case CBManagerStatePoweredOff:
NSLog(@">>>CBCentralManagerStatePoweredOff");
break;
case CBManagerStatePoweredOn:
NSLog(@">>>CBCentralManagerStatePoweredOn");
self.isBlueToothPowerOn = YES;
// 搜索扫描外围设备
[self scanDevices];
break;
default:
break;
}
}
2.搜索扫描外围设备
- (void)scanDevices {
[self.scanDeviceArr removeAllObjects];
if (self.isBlueToothPowerOn) {
// 开始扫描周围的外设。
/*
-- 两个参数为nil表示默认扫描所有可见蓝牙设备。
-- 注意:第一个参数是用来扫描有指定服务的外设。然后有些外设的服务是相同的,比如都有FFF5服务,那么都会发现;而有些外设的服务是不可见的,就会扫描不到设备。
-- 成功扫描到外设后调用didDiscoverPeripheral
*/
[self.centralManager scanForPeripheralsWithServices:nil options:nil];
} else {
NSLog(@">>>请到系统设置中打开蓝牙");
}
}
#pragma mark - CBCentralManagerDelegate
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
NSLog(@"%@",peripheral.name);
if (!peripheral.name || !peripheral) {
return;
}
//针对设备进行过滤(根据设备名称进行过滤,具体过滤规则看需求)
if (![peripheral.name hasPrefix:@"xxx_xxx"]) {
return;
}
if (self.scanDeviceArr.count == 0) {
NSLog(@"Find New Device:%@", peripheral.description);
[self.scanDeviceArr addObject:peripheral];
} else {
for (int i = 0 ; i < self.scanDeviceArr.count ; i ++) {
CBPeripheral *peri = [self.scanDeviceArr objectAtIndex:i];
if ([peri.name isEqualToString:peripheral.name]) {
break;
} else if (i == self.scanDeviceArr.count - 1) {
NSLog(@"Find New Device:%@", peripheral.description);
[self.scanDeviceArr addObject:peripheral];
}
}
}
//将设备信息传到外面的页面(VC), 构成扫描到的设备列表
if (self.delegate && [self.delegate respondsToSelector:@selector(updateDeviceDataWithBluetoothArr:)]) {
[self.delegate updateDeviceDataWithBluetoothArr:self.scanDeviceArr];
}
}
3.连接外围设备
// 连接设备(.h中声明出去的接口, 一般在点击设备列表连接时调用)
- (void)connectPeripheral: (CBPeripheral *)peripheral {
//连接成功后回调didConnectPeripheral
//连接失败后回调didFailToConnectPeripheral
[self.centralManager connectPeripheral:peripheral options:nil];
}
#pragma mark - CBCentralManagerDelegate
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
NSLog(@">>>连接外设成功");
//连接成功后停止扫描,节省内存
[central stopScan];
peripheral.delegate = self;
self.peripheral = peripheral;
//获取外围设备服务
[peripheral discoverServices:nil];
if ([self.delegate respondsToSelector:@selector(didConnectBleDevice)]) {
[self.delegate didConnectBleDevice];
}
}
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
NSLog(@">>>连接外设失败");
NSLog(@"%@", error);
self.peripheral = nil;
}
4.取消连接外围设备
// 断开设备连接
- (void)disconnectPeripheral: (CBPeripheral *)peripheral {
//断开连接后回调didDisconnectPeripheral
[self.centralManager cancelPeripheralConnection:peripheral];
}
#pragma mark - CBCentralManagerDelegate
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
NSLog(@">>>取消与外设的连接回调");
self.peripheral = nil;
if (self.delegate && [self.delegate respondsToSelector:@selector(didDisconnectBleDevice)]) {
[self.delegate didDisconnectBleDevice];
}
}
5.获得外围设备的服务
- (void)getPeripheralServices: (CBPeripheral *)peripheral {
//扫描外设的服务
/**
--外设的服务、特征、描述等方法是CBPeripheralDelegate的内容,所以要先设置代理peripheral.delegate = self
--参数表示你关心的服务的UUID,比如我关心的是"FFE0",参数就可以为@[[CBUUID UUIDWithString:@"FFE0"]].那么didDiscoverServices方法回调内容就只有这两个UUID的服务,不会有其他多余的内容,提高效率。nil表示扫描所有服务
--成功发现服务,回调didDiscoverServices
*/
[peripheral discoverServices:nil];
}
#pragma mark - CBPeripheralDelegate
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
NSLog(@">>>发现服务回调");
CBService * __nullable findService = nil;
//遍历服务
for (CBService *service in peripheral.services) {
NSLog(@"UUID:%@",service.UUID.UUIDString);
if ([service.UUID.UUIDString isEqualToString:@"xxx_xxx"]) {
findService = service;
break;
}
}
if (findService) {
// 获取服务下的特征
[peripheral discoverCharacteristics:NULL forService:findService];
}
}
6.获得服务下的特征
- (void)getCharacteristicsForService:(CBService *)service peripheral:(CBPeripheral *)peripheral {
// 成功发现特征,回调didDiscoverCharacteristicsForService
[peripheral discoverCharacteristics:NULL forService:service];
}
#pragma mark - CBPeripheralDelegate
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
NSLog(@">>>发现特征回调");
for (CBCharacteristic *characteristic in service.characteristics) {
NSLog(@"UUID:%@",characteristic.UUID.UUIDString);
if ([characteristic.UUID.UUIDString isEqualToString:@"xxx_xxx"]) {
//特征
self.character = characteristic;
//回调didUpdateNotificationStateForCharacteristic
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
//回调didUpdateValueForCharacteristic
[peripheral readValueForCharacteristic:characteristic];
//当发现characteristic有descriptor,回调didDiscoverDescriptorsForCharacteristic
[peripheral discoverDescriptorsForCharacteristic:characteristic];
}
}
}
- 获取特征下的描述
- (void)getDescriptorsForCharacteristics:(CBCharacteristic *)characteristic peripheral:(CBPeripheral *)peripheral {
// 成功发现描述,回调didDiscoverDescriptorsForCharacteristic
[peripheral discoverDescriptorsForCharacteristic:characteristic];
}
#pragma mark - CBPeripheralDelegate
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
NSLog(@">>>发现特征描述回调");
}
8.从外围设备读取数据
#pragma mark - CBPeripheralDelegate
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
NSLog(@">>>读取setNotifyValue成功");
}
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
NSLog(@">>>读取setNotifyValue成功");
}
9.给外围设备发送(写入)数据。
- (void)writeData: (NSData *)data {
//data必须经过16进制数字符串转换
if (data == nil) {
return ;
}
// 将指令写入蓝牙
if (self.writeCharacter == nil) {
return ;
}
//写入数据成功后回调didWriteValueForCharacteristic
[self.peripheral writeValue:data forCharacteristic:self.writeCharacter type:CBCharacteristicWriteWithResponse];
}
#pragma mark - CBPeripheralDelegate
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
NSLog(@">>>数据写入外围设备成功回调");
NSLog(@"----------%@",characteristic);
}
参考文献:https://www.jianshu.com/p/0c31487bb7c5