上一篇文章我们讲了iOS开发的理论知识,下面我们就从代码开始讲解。

1.CoreBluetooth.framework导入

1.在General->TARGETS->Linked Framworks and Libraries中点击添加并选择CoreBluetooth.framework导入。

2.在代码中导入CoreBluetooth.framework Swift:import CoreBluetooth Objective-C:#import

3.声明协议:使用CoreBluetooth需要支持CBCentralManagerDelegate(需要蓝牙管理者mgr 管理者可以扫描外围设备), CBPeripheralDelegate协议(mgr扫描到外设,与外设进行连接断开连接信息交流等一系列反馈回调),即前面所说的中心设备和外围设备,并实现相应方法

2.建立一个Central Manager实例进行蓝牙管理

self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];

一旦设置代理在运行程序的时候,就会调用协议里一个必须要完成的方法:
这个方法是查看中心设备是否打开蓝牙。
#pragma mark - 只要中心管理者初始化 就会触发此代理方法 判断手机蓝牙状态
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
    switch (central.state)
    {
            // PoweredOff
        case CBCentralManagerStatePoweredOff:
            break;
            // PoweredOn
        case CBCentralManagerStatePoweredOn: //蓝牙已开启
            // 搜索外设
            [self.centralManager scanForPeripheralsWithServices:nil  options:self.centralManagerOptionDic];
            break;
            // Resetting
        case CBCentralManagerStateResetting:
            break;
            // Unsupported
        case CBCentralManagerStateUnsupported: //不支持蓝牙
            break;
            // Unauthorized
        case CBCentralManagerStateUnauthorized:
            break;
            // Unknown state
        case CBCentralManagerStateUnknown:
            break;
        default:
            break;
    }
}
[manager scanForPeripheralsWithServices:nil options:nil];
第一个参数那里表示扫描带有相关服务的外部设备,例如填写@[[CBUUIDUUIDWithString:@"需要连接的外部设备的服务的UUID"]],即表示带有需要连接的外部设备的服务的UUID的外部设备,nil表示扫描全部设备;
options处以后细讲,暂时可以写一个@{CBCentralManagerScanOptionAllowDuplicatesKey :@YES}这样的参数,YES表示会让中心设备不断地监听外部设备的消息,NO就是不能。

3.扫描周边的蓝牙设备

// 1.中心管理者 2.外设 3.外设携带的数据 4.外设发出的蓝牙信号强度
- (void)centralManager:(CBCentralManager *)central
 didDiscoverPeripheral:(CBPeripheral *)peripheral
     advertisementData:(NSDictionary *)advertisementData
                  RSSI:(NSNumber *)RSSI
{
     /*
     peripheral = <CBPeripheral: 0x166668f0 identifier = C69010E7-EB75-E078-FFB4-421B4B951341, Name = "OBand-75", state = disconnected>, advertisementData = {
     kCBAdvDataChannel = 38;
     kCBAdvDataIsConnectable = 1;
     kCBAdvDataLocalName = OBand;
     kCBAdvDataManufacturerData = <4c69616e 0e060678 a5043853 75>;
     kCBAdvDataServiceUUIDs =     (
     FEE7
     );
     kCBAdvDataTxPowerLevel = 0;
     }, RSSI = -55
     
     */
    
    if ([peripheral.name hasPrefix:@"OBand"]) {
        [self.centralManager stopScan];
        // 在此处对我们的 advertisementData(外设携带的广播数据) 进行一些处理
        // 标记我们的外设,让他的生命周期 = vc
        self.peripheral = peripheral;
        // 发现完之后就是进行连接
        [self.centralManager connectPeripheral:self.peripheral options:nil];
        NSLog(@"%s, line = %d", __FUNCTION__, __LINE__);
    }
}

4.连接外围设备

// 中心管理者连接外设成功
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
    NSLog(@"%s, line = %d, %@=连接成功", __FUNCTION__, __LINE__, peripheral.name);
    // 连接成功之后,可以进行服务和特征的发现
    
    //  设置外设的代理
    self.peripheral.delegate = self;
    
    // 外设发现服务,传nil代表不过滤
    // 这里会触发外设的代理方法 - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
    [self.peripheral discoverServices:nil];
}

// 连接失败
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(nullableNSError *)error;(连接失败)

// 丢失连接
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
    NSLog(@"%s, line = %d, %@=断开连接", __FUNCTION__, __LINE__, peripheral.name);
}

5.获得外围设备的服务 & 6.获得服务的特征

#pragma mark - CBPeripheralDelegate
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(nullable NSError *)error{
    
    if (error)
    {
        NSLog(@"error:%@",error.localizedDescription);
        return ;
    }
    
    for (CBService *service in peripheral.services)
    {
         NSLog(@"Discovered service %@", service);

        [peripheral discoverCharacteristics:nil forService:service];
    }
}
当我们扫描到特征的时候,就会跳入发现特征的协议方法里去:

// 发现外设服务里的特征的时候调用的代理方法(这个是比较重要的方法,你在这里可以通过事先知道UUID找到你需要的特征,订阅特征,或者这里写入数据给特征也可以)
- (void)peripheral:(CBPeripheral *)peripheral
  didDiscoverCharacteristicsForService:(CBService *)service
                    error:(nullable NSError *)error
{
    
    if (error) {
        NSLog(@"error:%@",error.localizedDescription);
        return ;
    }
    
    NSLog(@"%s, line = %d", __FUNCTION__, __LINE__);

    // 下面的36F6和36F5根据需求自己定义
    for (CBCharacteristic *characteristic in service.characteristics)
    {
        if ([[[characteristic UUID] UUIDString] isEqualToString:@"36F6"])
        {
            [peripheral setNotifyValue:YES forCharacteristic:characteristic];
        }
        else if([[[characteristic UUID] UUIDString] isEqualToString:@"36F5"] )
        {
            // 记录要之后每次要写入的特征
            writeCBCharacteristic = characteristic;
        }
    }    
}

7.给外围设备发送数据(也就是写入数据到蓝牙)

发送数据只需要在指定的service和characteristic组合下发送即可,如果是以CBCharacteristicWriteWithResponse模式发送,发送完后还会调用
CBPeripheralDelegate的peripheral:(CBPeripheral *) didWriteValueForCharacteristic:(CBCharacteristic *) error:(NSError *),实现该协议方法可判断发送是否成功。
以CBCharacteristicWriteWithoutResponse模式则不会有回调。

[connectPeripheral writeValue:data forCharacteristic:writeCBCharacteristic type:CBCharacteristicWriteWithResponse];
//第一个参数是已连接的蓝牙设备 ;第二个参数是要写入到哪个特征; 第三个参数是通过此响应记录是否成功写入

- (void)peripheral:(CBPeripheral *)peripheral
    didWriteValueForCharacteristic:(CBCharacteristic *)characteristic
    error:(nullable NSError *)error
{
    
    if (error)
    {
        NSLog(@"error:%@, %@",error.localizedDescription, characteristic);
        return ;
    }

    if (!(characteristic.properties & CBCharacteristicPropertyNotify))
    {
        [peripheral readValueForCharacteristic:characteristic];
    }
}

8.从外围设备读数据

// 更新特征的value的时候会调用 (凡是从蓝牙传过来的数据都要经过这个回调,简单的说这个方法就是你拿数据的唯一方法) 你可以判断是否修改密码成功, 获取电量信息等, 以及getToken(以我工程蓝牙锁为例子)
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
    NSLog(@"%s, line = %d", __FUNCTION__, __LINE__);
    if (characteristic == @"你要的特征的UUID或者是你已经找到的特征") {
    //characteristic.value就是你要的数据, 类型是NSData, 二进制数据
    }
}

// 如果有写特征将notift设置为yes之后: [peripheral setNotifyValue:YES forCharacteristic:c],订阅的通知消息会走下面这个接口
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{}

以上内容有写的不对的地方,请大家指出,有理解不了的地方可以同我交流。

参考资料: iOS蓝牙开发:蓝牙连接和数据读写