Bluetooth 开发笔记

API Reference

CoreBluetooth中,需要用到的类和协议(完整导图):

CoreBluetooth.png

基础知识

蓝牙分类中心端和外设端(完整导图)。

Bluetooth.png

中心端(接收端)

1 .创建中心端控制器(CBCentralManager)
2 .扫描设备(Discover)
3 .连接 (Connect)
4 .获取Service和Characteristic

  • 扫描Service (一个service中包含一个或多个Characteristic)
  • 获取Service中Characteristic
  • 获取Characteristic的值

5 . 数据交互(explore and interact)

  • 订阅Characteristic的通知

6 . 断开链接

Central.png

外设端(发送端)

  1. 创建Peripheral管理对象
  2. 创建Service和Characteristic树
  3. 发送广告
  4. 处理读写请求和订阅
Peripheral.png

蓝牙状态

typedef NS_ENUM(NSInteger, CBManagerState) {
    CBManagerStateUnknown = 0,
    CBManagerStateResetting,  
    CBManagerStateUnsupported, //不支持
    CBManagerStateUnauthorized, //未授权
    CBManagerStatePoweredOff, //关闭
    CBManagerStatePoweredOn,  //蓝牙打开状态
} NS_ENUM_AVAILABLE(NA, 10_0);

连接状态

/*!
 *  @enum CBPeripheralState
 *
 *  @discussion Represents the current connection state of a CBPeripheral.
 *
 */
typedef NS_ENUM(NSInteger, CBPeripheralState) {
    CBPeripheralStateDisconnected = 0,
    CBPeripheralStateConnecting,
    CBPeripheralStateConnected,
    CBPeripheralStateDisconnecting NS_AVAILABLE(NA, 9_0),
} NS_AVAILABLE(NA, 7_0);

CBCentralManagerDelegate

@required 
 // 更新CentralManager状态,参数central就是当前的Manager。
- (void)centralManagerDidUpdateState:(CBCentralManager *)central;

@optional
// 蓝牙状态的保存和恢复,进入后台和重新启动有关
- (void)centralManager:(CBCentralManager *)central willRestoreState:(NSDictionary<NSString *, id> *)dict;

//扫描外部设备 
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI;

//与外设完成连接
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;

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

//断开连接
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error;

CBPeripheralDelegate

@optional
//peripheral更新
- (void)peripheralDidUpdateName:(CBPeripheral *)peripheral NS_AVAILABLE(NA, 6_0);
//服务更新时触发
- (void)peripheral:(CBPeripheral *)peripheral didModifyServices:(NSArray<CBService *> *)invalidatedServices NS_AVAILABLE(NA, 7_0);
//更新RSSI,过时用下一代替
- (void)peripheralDidUpdateRSSI:(CBPeripheral *)peripheral error:(nullable NSError *)error NS_DEPRECATED(NA, NA, 5_0, 8_0);
// RSSI值
- (void)peripheral:(CBPeripheral *)peripheral didReadRSSI:(NSNumber *)RSSI error:(nullable NSError *)error NS_AVAILABLE(NA, 8_0);
//发现服务
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(nullable NSError *)error;
//发现嵌套服务
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverIncludedServicesForService:(CBService *)service error:(nullable NSError *)error;
//发现服务中的Characteristic
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(nullable NSError *)error;
//更新Characteristic
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error;

- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error;

- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error;

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error;

- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForDescriptor:(CBDescriptor *)descriptor error:(nullable NSError *)error;

- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForDescriptor:(CBDescriptor *)descriptor error:(nullable NSError *)error;

CBPeripheralManagerDelegate

@required
//类似CBCentralManager
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral;
@optional
- (void)peripheralManager:(CBPeripheralManager *)peripheral willRestoreState:(NSDictionary<NSString *, id> *)dict;
//开始广播
- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(nullable NSError *)error;
//添加服务
- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(nullable NSError *)error;
//订阅
- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic;
//未订阅
- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic;
//接收读取请求
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request;
//接收写入请求
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray<CBATTRequest *> *)requests;
//更新订阅
- (void)peripheralManagerIsReadyToUpdateSubscribers:(CBPeripheralManager *)peripheral;

中心端(接收端)

从上面了解了蓝牙开发的基本流程和API结构,那下面我们一起看看中心端的开发步骤。

创建CentralManager

//创建CentralManager
- (void)startUpCentralManager {
    _centralManager = [[CBCentralManager alloc] initWithDelegate: self queue: nil];
    _peripherals = [NSMutableArray array]; //用于存放扫描的外设
}

创建CentralManager会调用Delegate方法:

#pragma mark - CBCentralManagerDelegate

// 实现: 确保支持BL和有效的Central设备
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
    switch (central.state) {
        case CBManagerStatePoweredOn:
            //只有此状态下才可以进行扫描设备
           [self scan];
            break;
        case CBManagerStateUnknown:
            break;
        case CBManagerStateResetting:
            break;
        case CBManagerStateUnsupported:
            break;
        case CBManagerStateUnauthorized:
            break;
        case CBManagerStatePoweredOff:
            break;
        default:
            break;
    }
}

扫描外部设备

- (void)scan {
    [self.centralManager scanForPeripheralsWithServices: nil options: nil];
    NSLog(@"Scanning started");
}

serviceUUIDs: 是一个CBUUID类型的数组,每个CBUUID代表一个service的UUID。使用这个参数可以限制扫描内容,如果设置为nil,则表示搜索全部外设。
options: 定义扫描 ,字典。

  • CBCentralManagerScanOptionAllowDuplicatesKey : 布尔值,无重复过滤的扫描。默认是NO。如果设置为YES,对电池有不利影响。
  • CBCentralManagerScanOptionSolicitedServiceUUIDsKey 想要扫描的Service UUIDs数组(NSArray)。

另外,在bluetooth-central后台模式下,option将被忽略。这个问题后面会详细讲解。

当启动扫描后,每次扫描到一个外设就会调用delegate方法:centralManager: didDiscoverPeripheral: advertisementData: RSSI:

- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI {
    NSLog(@"%@", peripheral.name);
    //查找到设备并持有它,否则不会保存
    [self.peripherals addObject: peripheral];
}
  • peripheral : 扫描到的设备
  • advertisementData: 字典包含所有的广告数据
  • RSSI:received signal strength indicator,单位分贝。表示信号强度。

到这一步我们扫描到外部设备,那接下来就是建立连接了。

建立连接

通过扫描,我们发现了目标外设,接下来建立连接。

- (void)startUpConnect {
    [self.centralManager connectPeripheral: self.peripheral options: nil];
}

self.peripheral : 目标外设
options :字典,用来定制连接行为

  • CBConnectPeripheralOptionNotifyOnConnectionKey
  • CBConnectPeripheralOptionNotifyOnDisconnectionKey
  • CBConnectPeripheralOptionNotifyOnNotificationKey

进行连接通常会出现两种情况:成功、失败。

连接成功
本地连接成功,会调用方法:

- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
    NSLog(@"链接设备名称为:%@", peripheral.name);
    // 设置代理
    peripheral.delegate = self;
    //发现服务
    [peripheral discoverServices: nil];
}

连接失败

//链接失败
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
    NSLog(@"连接名称:%@ 失败,原因:%@", peripheral.name, error.localizedDescription);
}

另外,既然可以建立连接,那么肯定可以断开连接。

** 断开连接**

//断开链接
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
    NSLog(@"与外设断开链接:%@, %@", peripheral.name, error.localizedDescription);
}

如果断开连接不是由cancelPeripheralConnection:发起的,error会给出详细信息。需要注意的是,当断开连接,所有的services, characteristics和Characteristic descriptions都是无效的。

获取服务

创建连接成功后,在delegate方法中

// 设置代理
peripheral.delegate = self;
//发现服务
[peripheral discoverServices: nil];

可以通过CBUUID指定的服务。

CBPeripheralDelegate

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
    if (error) {
        NSLog(@"发现服务错误:%@", error.localizedDescription);
        return;
    }
    // 一个外设会发送多个服务
    for (CBService *service in peripheral.services) {
        //扫描每个service的Characteristic,通过Characteristic的UUID来指定查找那个Characteristic。
        [peripheral discoverCharacteristics: nil forService: service];
    }
}

注:一个外设包含多个Service,可以通过Service的UUID来区分。一个Service包含多个Characteristic,通过Characteristic的UUID来区分。

获取特征

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
    if (error) {
        NSLog(@"发现特征错误:%@", error.localizedDescription);
        return;
    }
    for (CBCharacteristic *characteristic in service.characteristics) {
        //直接读取Characteristic值,此时调用peripheral: didUpdateValueForCharacteristic: error:
        [peripheral readValueForCharacteristic: characteristic];
        //另一种情况,订阅特征。此时调用 peripheral: didUpdateNotificationStateForCharacteristic: error:
        [peripheral setNotifyValue:YES forCharacteristic: characteristic];
    }
}

订阅Characteristic

//给指定的特征设置通知
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
    
    if (error) {
        NSLog(@"Error changing notification state : %@", error.localizedDescription);
    }
    if (![characteristic.UUID isEqual:[CBUUID UUIDWithString:@""]]) {
        return;
    }
    
    if (characteristic.isNotifying) {
        NSLog(@"Notification began on :%@", characteristic);
    } else {
        NSLog(@"Notification stoped on : %@ Disconnecting", characteristic);
        [self.centralManager cancelPeripheralConnection: peripheral];
    }   
}

获取特征值

//readValueCharacteristic时调用
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
    
    //打印出characteristic的UUID和值
    //!注意,value的类型是NSData,具体开发时,会根据外设协议制定的方式去解析数据
    NSLog(@"特性ID::%@  值:%@",characteristic.UUID, characteristic.value);
}

数据交互

- (void)writeValue {
    NSData *data = [@"Test" dataUsingEncoding:NSUTF8StringEncoding];
    [self.peripheral writeValue: data forCharacteristic: self.characteristic type: CBCharacteristicWriteWithResponse];
}

发送数据

  • value: 写入值
  • characteristic : 被写入的特征
  • type: 写入类型,是否有应答
typedef NS_ENUM(NSInteger, CBCharacteristicWriteType) {
    CBCharacteristicWriteWithResponse = 0,
    CBCharacteristicWriteWithoutResponse,
};

当type为CBCharacteristicWriteWithResponse时,调用delegate方法:peripheral: didWriteValueForCharacteristic: error:

外设端(发送端)

创建PeripheralManager

//创建PeripheralManager
- (void)startUpPeripheralManager {
    _peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue: nil];
}
//唯一@required方法
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {
    switch (peripheral.state) {
        case CBManagerStatePoweredOn:
            // 创建服务和特征
            [self buildService];
            break;
        case CBManagerStateUnknown :
            break;
        case CBManagerStateResetting:
            break;
        case CBManagerStateUnsupported:
            break;
        case CBManagerStateUnauthorized:
            break;
        case CBManagerStatePoweredOff:
            break;
        default:
            break;
    }
}

创建服务和特征

- (void)buildService {
    //创建Characteristic
    self.transferCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID] properties:CBCharacteristicPropertyNotify value: nil permissions:CBAttributePermissionsReadable];
    
    //创建服务
    CBMutableService *transferService = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID] primary:YES];
    //将特征添加到服务中
    transferService.characteristics = @[self.transferCharacteristic];
    
    //将服务添加到PeripheralManager中,调用delegate方法: peripheralManager: didAddService: error:
    [self.peripheralManager addService: transferService];
}

创建Characteristic

创建Characteristic

  • UUID : 唯一标识
  • properties:属性
  • value : 设置nil, 动态设置。
  • permission : 权限

属性

typedef NS_OPTIONS(NSUInteger, CBCharacteristicProperties) {
    //特征值可以使用特征描述广播
    CBCharacteristicPropertyBroadcast                                               = 0x01,
    //特征值可以被读取,通过readValueForCharacteristic: 来读取
    CBCharacteristicPropertyRead                                                    = 0x02,
    // 可以被读写,通过writeValue: forCharacteristic: type: 写入特征值,无应答标识写入成功。
    CBCharacteristicPropertyWriteWithoutResponse                                    = 0x04,
    // 可以被写入,有应答标识写入成功
    CBCharacteristicPropertyWrite                                                   = 0x08,
    //允许通知特征值,无应答表示接收
    CBCharacteristicPropertyNotify                                                  = 0x10,
    //允许通知特征值,有应答表示接收
    CBCharacteristicPropertyIndicate                                                = 0x20,
    CBCharacteristicPropertyAuthenticatedSignedWrites                               = 0x40,
    CBCharacteristicPropertyExtendedProperties                                      = 0x80,
    //只有信任的设备接收特征值通知
    CBCharacteristicPropertyNotifyEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0)     = 0x100,
    CBCharacteristicPropertyIndicateEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0)   = 0x200
};

属性特征,可以组合使用。

权限

typedef NS_OPTIONS(NSUInteger, CBAttributePermissions) {
    // 可读
    CBAttributePermissionsReadable                  = 0x01,
    // 可写
    CBAttributePermissionsWriteable                 = 0x02,
    // 信任可读
    CBAttributePermissionsReadEncryptionRequired    = 0x04,
    // 信任可写
    CBAttributePermissionsWriteEncryptionRequired   = 0x08
} NS_ENUM_AVAILABLE(NA, 6_0);

创建Service

UUID : 唯一标识
isPrimary:YES:主服务;NO:次服务

添加特征到characteristics数据
调用delegate方法: peripheralManager: didAddService: error:

发送广告

//开始广告
- (void)startAdvertise {
    [self.peripheralManager startAdvertising:@{CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]]}];
}
//停止广告
- (void)stopAdvertise {
    [self.peripheralManager stopAdvertising];
}

开始广告
advertisementData: 可选字典,包含想要广告的数据。两种类型:

  • CBAdvertisementDataLocalNameKey :对应名称
  • CBAdvertisementDataServiceUUIDsKey : 对应UUID

调用delegate方法:peripheralManagerDidStartAdvertising: error:

处理订阅

- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic {
    NSLog(@"当前Characteristic被订阅");
}

当characteristic配置为notify或indicate,将唤起次方法。

- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic {
    NSLog(@"Central unsubscribed from characteristic");
}

当central移除特征的notification/indications时调用,取消订阅回调。

数据交互

- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request {
  }
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray<CBATTRequest *> *)requests { 
}

当接收到中心端的读写请求式,会触发这个方法。
在该方法被调用时,可以在方法内通过PeripheralManager调用:respondToRequest: withResult:来响应中心端的读写请求。

蓝牙的后台模式

执行模式

plist文件中,设置UIBackgroundModes:

  • bluetooth-central
  • bluetooth-peripheral

bluetooth-central 执行模式

当设置为此模式,允许APP切入后台后还能进行蓝牙服务。依然能进行扫描,连接,交互数据等。
进入后台CBCentralManagerScanOptionAllowDuplicatesKey扫描被忽略。

bluetooth-peripheral 执行模式

当设置为此模式,允许APP进行读写,连接中心端等。
CBAdvertisementDataLocalNameKey被忽略,本地外设名不在广播。

实现代码

PeripheralManager

#import "LQPeripheralManager.h"
#import <CoreBluetooth/CoreBluetooth.h>
static NSString *const ServiceUUID1 = @"FFF0";
static NSString *const notifyCharacteristicUUID = @"FFF1";
static NSString *const readwriteCharacteristicUUID = @"FFF2";

static NSString *const ServiceUUID2 = @"FFE0";
static NSString *const readCharacteristicUUID = @"FFE1";

static NSString *const LocalNameKey = @"Owenli";


@interface LQPeripheralManager ()<CBPeripheralManagerDelegate>
@property (nonatomic, strong) CBPeripheralManager *peripheralManager;
@property (nonatomic, strong) NSTimer *timer;
@property (nonatomic, assign) NSInteger index;
@end

@implementation LQPeripheralManager

+ (instancetype)shareInstance {
    static LQPeripheralManager *peripheral;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        peripheral = [[self alloc] init];
    });
    return peripheral;
}
- (instancetype)init {
    if (self = [super init]) {
        _peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];
        _index = 0;
    }
    return self;
}
/**
 * @Description 初始化化UUID和服务信息
 */
- (void)setup {
    
    //characteristic字段描述
    CBUUID *cbuuidCharacteristicUserDescriptionStringUUID = [CBUUID UUIDWithString:CBUUIDCharacteristicUserDescriptionString];
    /*
     可以通知的Characteristic
     properities : CBCharacteristicPropertyNotify
     permissions : CBAttributePermissionsReadable
     */
    CBMutableCharacteristic *notifyCharacteristic  = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:notifyCharacteristicUUID] properties:CBCharacteristicPropertyNotify value:nil permissions:CBAttributePermissionsReadable];
    
    
    /*
     可读写的characteristic
     prperitise: CBCharacteristicPropertyWrite | CBCharacteristicPropertyRead
     permisssions : CBAttributePermissionsReadable | CBAttributePermisssionWriteable
     */
    
    CBMutableCharacteristic * readwriteCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:readwriteCharacteristicUUID] properties:CBCharacteristicPropertyWrite | CBCharacteristicPropertyRead value:nil permissions:CBAttributePermissionsReadable | CBAttributePermissionsWriteable];
    // 设置descriptor
    CBMutableDescriptor *readwriteCharacteristicDescription1 = [[CBMutableDescriptor alloc] initWithType:cbuuidCharacteristicUserDescriptionStringUUID value:@"name"];
    
    [readwriteCharacteristic setDescriptors:@[readwriteCharacteristicDescription1]];
    /*
     只读Characteristic
     properties: CBCharacteristicPropertyRead
     permisssions: CBAttributePermissionsReadable
     */
    CBMutableCharacteristic *readCharacteristic  = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:readCharacteristicUUID] properties:CBCharacteristicPropertyRead value:nil permissions:CBAttributePermissionsReadable];
    // 第一个Service
    CBMutableService *service1  = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:ServiceUUID1] primary:YES];
    
    [service1 setCharacteristics:@[notifyCharacteristic, readwriteCharacteristic]];
    
    //第二个Service
    
    CBMutableService *service2 = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:ServiceUUID2] primary:YES];
    [service2 setCharacteristics:@[readCharacteristic]];
    //添加到periperal此时会调用,peripheralManager: didAddService: error:
    [self.peripheralManager addService:service2];
    [self.peripheralManager addService:service1];

}
#pragma mark - PeripherManagerDelegate

//检测蓝牙状态,
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {
    switch (peripheral.state) {
        case CBManagerStatePoweredOn:
            //初始化
            [self setup];
            break;
        case CBManagerStatePoweredOff:
            NSLog(@"powered off");
            break;
        default:
            break;
    }
}
- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error {
    if (error) {
        NSLog(@"%@", error.localizedDescription);
        return;
    }
    //添加服务后,开始广播
    //自动回调: peripheralManagerDidStartAdvertising: error:
    [self.peripheralManager startAdvertising:@{
                                               CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:ServiceUUID1], [CBUUID UUIDWithString:ServiceUUID2]],
                                               CBAdvertisementDataLocalNameKey : LocalNameKey
                                               }];
}
//通知发送广播
- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error {
    if (error) {
        NSLog(@"%@", error.localizedDescription);
    }
    NSLog(@"start advert");
}
- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic {
    NSLog(@"subscribe data of : %@", characteristic.UUID);
    //分配定时任务
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(sendData:) userInfo:characteristic repeats:YES];
}

- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic {
    NSLog(@"unsubscrible: %@", characteristic.UUID);
    [self.timer invalidate];
}

- (void)sendData:(NSTimer *)timer {
    
    CBMutableCharacteristic *characteristic = timer.userInfo;
    if ([self.peripheralManager updateValue:[[NSString stringWithFormat:@"send data : %ld", _index] dataUsingEncoding: NSUTF8StringEncoding] forCharacteristic:characteristic onSubscribedCentrals:nil]) {
        NSLog(@"发送成功");
        _index ++;
    } else {
        NSLog(@"发送数据错误");
    }
}
//中心设备读取请求
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request {
    NSLog(@"readRequest");
    if (request.characteristic.properties & CBCharacteristicPropertyRead) {
        NSData *data = [[NSString stringWithFormat:@"by characteristic request"] dataUsingEncoding:NSUTF8StringEncoding];
        [request setValue:data];
        //对请求作出成功响应
        [self.peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
    } else {
        [self.peripheralManager respondToRequest:request withResult:CBATTErrorWriteNotPermitted];
    }
}

//写入请求
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray<CBATTRequest *> *)requests {

    NSLog(@"writeRequest");
    
    CBATTRequest *request = requests.firstObject;
    
    if (request.characteristic.properties & CBCharacteristicPropertyWrite) {
        CBMutableCharacteristic *charateristic  = (CBMutableCharacteristic *)request.characteristic;
        charateristic.value = request.value;
        NSLog(@"receive data :%@", [[NSString alloc] initWithData:charateristic.value encoding:NSUTF8StringEncoding]);
        [self.peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
    } else {
        [self.peripheralManager respondToRequest:request withResult:CBATTErrorWriteNotPermitted];
    }
}
- (void)peripheralManagerIsReadyToUpdateSubscribers:(CBPeripheralManager *)peripheral {
    NSLog(@"peripheralManagerIsReadyToUpdateSubscribers");
}
@end

使用LighBlue测试外设端

备注

命令行生成UUID方法:uuidgen
Mac测试软件:LighBlue,可以用来测试iOS端外设。

参考

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,671评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,442评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,524评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,623评论 1 275
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,642评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,584评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,953评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,621评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,865评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,608评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,698评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,378评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,958评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,940评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,173评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,419评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,425评论 2 342

推荐阅读更多精彩内容