Bluetooth 開發(fā)筆記

API Reference

CoreBluetooth中辩棒,需要用到的類和協(xié)議(完整導圖):

CoreBluetooth.png

基礎知識

藍牙分類中心端和外設端(完整導圖)负拟。

Bluetooth.png

中心端(接收端)

1 .創(chuàng)建中心端控制器(CBCentralManager)
2 .掃描設備(Discover)
3 .連接 (Connect)
4 .獲取Service和Characteristic

  • 掃描Service (一個service中包含一個或多個Characteristic)
  • 獲取Service中Characteristic
  • 獲取Characteristic的值

5 . 數(shù)據(jù)交互(explore and interact)

  • 訂閱Characteristic的通知

6 . 斷開鏈接

Central.png

外設端(發(fā)送端)

  1. 創(chuàng)建Peripheral管理對象
  2. 創(chuàng)建Service和Characteristic樹
  3. 發(fā)送廣告
  4. 處理讀寫請求和訂閱
Peripheral.png

藍牙狀態(tài)

typedef NS_ENUM(NSInteger, CBManagerState) {
    CBManagerStateUnknown = 0,
    CBManagerStateResetting,  
    CBManagerStateUnsupported, //不支持
    CBManagerStateUnauthorized, //未授權(quán)
    CBManagerStatePoweredOff, //關(guān)閉
    CBManagerStatePoweredOn,  //藍牙打開狀態(tài)
} NS_ENUM_AVAILABLE(NA, 10_0);

連接狀態(tài)

/*!
 *  @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狀態(tài),參數(shù)central就是當前的Manager股淡。
- (void)centralManagerDidUpdateState:(CBCentralManager *)central;

@optional
// 藍牙狀態(tài)的保存和恢復瘤泪,進入后臺和重新啟動有關(guān)
- (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);
//服務更新時觸發(fā)
- (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);
//發(fā)現(xiàn)服務
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(nullable NSError *)error;
//發(fā)現(xiàn)嵌套服務
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverIncludedServicesForService:(CBService *)service error:(nullable NSError *)error;
//發(fā)現(xiàn)服務中的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;

中心端(接收端)

從上面了解了藍牙開發(fā)的基本流程和API結(jié)構(gòu)微王,那下面我們一起看看中心端的開發(fā)步驟累提。

創(chuàng)建CentralManager

//創(chuàng)建CentralManager
- (void)startUpCentralManager {
    _centralManager = [[CBCentralManager alloc] initWithDelegate: self queue: nil];
    _peripherals = [NSMutableArray array]; //用于存放掃描的外設
}

創(chuàng)建CentralManager會調(diào)用Delegate方法:

#pragma mark - CBCentralManagerDelegate

// 實現(xiàn): 確保支持BL和有效的Central設備
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
    switch (central.state) {
        case CBManagerStatePoweredOn:
            //只有此狀態(tài)下才可以進行掃描設備
           [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類型的數(shù)組,每個CBUUID代表一個service的UUID。使用這個參數(shù)可以限制掃描內(nèi)容已卸,如果設置為nil,則表示搜索全部外設硼一。
options: 定義掃描 累澡,字典。

  • CBCentralManagerScanOptionAllowDuplicatesKey : 布爾值般贼,無重復過濾的掃描愧哟。默認是NO。如果設置為YES哼蛆,對電池有不利影響蕊梧。
  • CBCentralManagerScanOptionSolicitedServiceUUIDsKey 想要掃描的Service UUIDs數(shù)組(NSArray)。

另外腮介,在bluetooth-central后臺模式下肥矢,option將被忽略。這個問題后面會詳細講解叠洗。

當啟動掃描后甘改,每次掃描到一個外設就會調(diào)用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: 字典包含所有的廣告數(shù)據(jù)
  • RSSI:received signal strength indicator灭抑,單位分貝十艾。表示信號強度。

到這一步我們掃描到外部設備腾节,那接下來就是建立連接了忘嫉。

建立連接

通過掃描,我們發(fā)現(xiàn)了目標外設案腺,接下來建立連接庆冕。

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

self.peripheral : 目標外設
options :字典,用來定制連接行為

  • CBConnectPeripheralOptionNotifyOnConnectionKey
  • CBConnectPeripheralOptionNotifyOnDisconnectionKey
  • CBConnectPeripheralOptionNotifyOnNotificationKey

進行連接通常會出現(xiàn)兩種情況:成功劈榨、失敗愧杯。

連接成功
本地連接成功,會調(diào)用方法:

- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
    NSLog(@"鏈接設備名稱為:%@", peripheral.name);
    // 設置代理
    peripheral.delegate = self;
    //發(fā)現(xiàn)服務
    [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:發(fā)起的,error會給出詳細信息陡舅。需要注意的是抵乓,當斷開連接,所有的services, characteristics和Characteristic descriptions都是無效的。

獲取服務

創(chuàng)建連接成功后灾炭,在delegate方法中

// 設置代理
peripheral.delegate = self;
//發(fā)現(xiàn)服務
[peripheral discoverServices: nil];

可以通過CBUUID指定的服務茎芋。

CBPeripheralDelegate

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
    if (error) {
        NSLog(@"發(fā)現(xiàn)服務錯誤:%@", error.localizedDescription);
        return;
    }
    // 一個外設會發(fā)送多個服務
    for (CBService *service in peripheral.services) {
        //掃描每個service的Characteristic,通過Characteristic的UUID來指定查找那個Characteristic蜈出。
        [peripheral discoverCharacteristics: nil forService: service];
    }
}

注:一個外設包含多個Service田弥,可以通過Service的UUID來區(qū)分。一個Service包含多個Characteristic铡原,通過Characteristic的UUID來區(qū)分偷厦。

獲取特征

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
    if (error) {
        NSLog(@"發(fā)現(xiàn)特征錯誤:%@", error.localizedDescription);
        return;
    }
    for (CBCharacteristic *characteristic in service.characteristics) {
        //直接讀取Characteristic值,此時調(diào)用peripheral: didUpdateValueForCharacteristic: error:
        [peripheral readValueForCharacteristic: characteristic];
        //另一種情況燕刻,訂閱特征只泼。此時調(diào)用 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時調(diào)用
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
    
    //打印出characteristic的UUID和值
    //!注意,value的類型是NSData卵洗,具體開發(fā)時请唱,會根據(jù)外設協(xié)議制定的方式去解析數(shù)據(jù)
    NSLog(@"特性ID::%@  值:%@",characteristic.UUID, characteristic.value);
}

數(shù)據(jù)交互

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

發(fā)送數(shù)據(jù)

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

當type為CBCharacteristicWriteWithResponse時过蹂,調(diào)用delegate方法:peripheral: didWriteValueForCharacteristic: error:籍滴。

外設端(發(fā)送端)

創(chuàng)建PeripheralManager

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

創(chuàng)建服務和特征

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

創(chuàng)建Characteristic

創(chuàng)建Characteristic

  • UUID : 唯一標識
  • properties:屬性
  • value : 設置nil榴啸, 動態(tài)設置孽惰。
  • permission : 權(quán)限

屬性

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
};

屬性特征潜的,可以組合使用骚揍。

權(quán)限

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

創(chuàng)建Service

UUID : 唯一標識
isPrimary:YES:主服務;NO:次服務

添加特征到characteristics數(shù)據(jù)
調(diào)用delegate方法: peripheralManager: didAddService: error:

發(fā)送廣告

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

開始廣告
advertisementData: 可選字典啰挪,包含想要廣告的數(shù)據(jù)信不。兩種類型:

  • CBAdvertisementDataLocalNameKey :對應名稱
  • CBAdvertisementDataServiceUUIDsKey : 對應UUID

調(diào)用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時調(diào)用抽活,取消訂閱回調(diào)。

數(shù)據(jù)交互

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

當接收到中心端的讀寫請求式锰什,會觸發(fā)這個方法下硕。
在該方法被調(diào)用時丁逝,可以在方法內(nèi)通過PeripheralManager調(diào)用:respondToRequest: withResult:來響應中心端的讀寫請求。

藍牙的后臺模式

執(zhí)行模式

plist文件中梭姓,設置UIBackgroundModes:

  • bluetooth-central
  • bluetooth-peripheral

bluetooth-central 執(zhí)行模式

當設置為此模式霜幼,允許APP切入后臺后還能進行藍牙服務。依然能進行掃描誉尖,連接罪既,交互數(shù)據(jù)等。
進入后臺CBCentralManagerScanOptionAllowDuplicatesKey掃描被忽略释牺。

bluetooth-peripheral 執(zhí)行模式

當設置為此模式萝衩,允許APP進行讀寫回挽,連接中心端等没咙。
CBAdvertisementDataLocalNameKey被忽略,本地外設名不在廣播千劈。

實現(xiàn)代碼

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此時會調(diào)用祭刚,peripheralManager: didAddService: error:
    [self.peripheralManager addService:service2];
    [self.peripheralManager addService:service1];

}
#pragma mark - PeripherManagerDelegate

//檢測藍牙狀態(tài),
- (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;
    }
    //添加服務后墙牌,開始廣播
    //自動回調(diào): peripheralManagerDidStartAdvertising: error:
    [self.peripheralManager startAdvertising:@{
                                               CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:ServiceUUID1], [CBUUID UUIDWithString:ServiceUUID2]],
                                               CBAdvertisementDataLocalNameKey : LocalNameKey
                                               }];
}
//通知發(fā)送廣播
- (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(@"發(fā)送成功");
        _index ++;
    } else {
        NSLog(@"發(fā)送數(shù)據(jù)錯誤");
    }
}
//中心設備讀取請求
- (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端外設喜滨。

參考

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末捉捅,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子虽风,更是在濱河造成了極大的恐慌棒口,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辜膝,死亡現(xiàn)場離奇詭異无牵,居然都是意外死亡,警方通過查閱死者的電腦和手機厂抖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進店門茎毁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人忱辅,你說我怎么就攤上這事七蜘。” “怎么了墙懂?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵崔梗,是天一觀的道長。 經(jīng)常有香客問我垒在,道長蒜魄,這世上最難降的妖魔是什么扔亥? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮谈为,結(jié)果婚禮上旅挤,老公的妹妹穿的比我還像新娘。我一直安慰自己伞鲫,他們只是感情好粘茄,可當我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著秕脓,像睡著了一般柒瓣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上吠架,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天芙贫,我揣著相機與錄音,去河邊找鬼傍药。 笑死磺平,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的拐辽。 我是一名探鬼主播拣挪,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼俱诸!你這毒婦竟也來了菠劝?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤睁搭,失蹤者是張志新(化名)和其女友劉穎赶诊,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體介袜,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡甫何,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了遇伞。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辙喂。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖鸠珠,靈堂內(nèi)的尸體忽然破棺而出巍耗,到底是詐尸還是另有隱情,我是刑警寧澤渐排,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布炬太,位于F島的核電站,受9級特大地震影響驯耻,放射性物質(zhì)發(fā)生泄漏亲族。R本人自食惡果不足惜炒考,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望霎迫。 院中可真熱鬧斋枢,春花似錦、人聲如沸知给。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽涩赢。三九已至戈次,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間筒扒,已是汗流浹背怯邪。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留霎肯,地道東北人擎颖。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓榛斯,卻偏偏與公主長得像观游,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子驮俗,可洞房花燭夜當晚...
    茶點故事閱讀 45,515評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 本文主要以藍牙4.0做介紹,因為現(xiàn)在iOS能用的藍牙也就是只僅僅4.0的設備 用的庫就是core bluetoot...
    暮雨飛煙閱讀 845評論 0 2
  • 首先進一則廣告: 藍牙技術(shù)聯(lián)盟(Bluetooth SIG)2010年7月7日宣布懂缕,正式采納藍牙4.0核心規(guī)范(B...
    L澤閱讀 1,456評論 3 4
  • 在寫這個博客之前,空余時間抽看了近一個月的文檔和Demo王凑,系統(tǒng)給的解釋很詳細搪柑,接口也比較實用,唯獨有一點索烹,對于設備...
    木易林1閱讀 3,371評論 3 4
  • 目前在iOS中藍牙開發(fā)框架主要有以下幾種 GameKit.framework:iOS7之前的藍牙通訊框架工碾,從iOS...
    我系哆啦閱讀 5,966評論 27 24
  • 人得自個兒成全自個兒! 記得在《霸王別姬》里面有一句很是經(jīng)典的話:人得自個兒成全自個兒百姓!這句話的出現(xiàn)場景是:小癩子...
    文刀雕心閱讀 465評論 3 4