iOS CoreBluetooth 教程

轉自:http://blog.csdn.net/jimoduwu/article/details/8917104

去App Store搜索並下載“LightBlue”這個App,對調試你的app和理解Core Bluetooth會很有幫助。

================================

Demo下載地址:http://download.csdn.net/detail/jimoduwu/7146875

================================

Core Bluetooth for iOS 6

CoreBluetoothAPI是基於BLE4.0的標準的。這個框架涵蓋了BLE標準的所有細節。僅僅只有新的iOS設備和Mac是和BLE標準兼容的:iPhone4SiPhone5MacMiniNew iPad,MacBook Air,MacBook Pro。還有iPhone iOS6的模擬器也支持。這是非常有用的,在你沒有一個真正的iOS設備而用模擬器去調試你的程序。

相關的類

CoreBluetooth框架中,有兩個主要的角色:周邊和中央(Peripheral and Central ,整個框架都是圍繞這兩個主要角色設計的,他倆之間有一系列的回調交換數據。下圖1展示了周邊和中央(Peripheral and Central),還有他倆之間的關係。


周邊(Peripheral)是生成或者保存了數據的設備,中央(Central)是使用這些數據的設備。所有可用的iOS設備可以作爲周邊(Peripheral)也可以作爲中央(Central),但不可以同時既是周邊也是中央。

周邊和中央這兩個角色在CoreBluetooth框架中是用兩個類來表示的,CBPeripheralManager這個類代表周邊,CBCentralManager 這個類代表中央。

在中央這邊,一個CBPeripheral 對象代表着相應的和中央連接着的周邊;同樣的,在周邊這邊,一個CBCentral 對象代表着相應的和周邊連接着的中央。

你可以認爲周邊是一個廣播數據的設備,他廣播到外部世界說他這兒有數據,並且也說明了能提供的服務。另一邊,中央開始掃描附近有沒有服務,如果中央發現了想要的服務,然後中央就會請求連接周邊,一旦連接建立成功,兩個設備之間就開始交換傳輸數據了。

除了中央和周邊,我們還要考慮他倆交換的數據結構。這些數據在服務中被結構化,每個服務由不同的特徵(Characteristics)組成,特徵是包含一個單一邏輯值的屬性類型。如果你去http://developer.bluetooth.org鏈接,你可以找到標準服務和特徵的列表。

在中央這邊,CBService 類代表服務,CBCharacteristic 類代表特徵。在周邊這邊,CBMutableService 類代表服務,CBMutableCharacteristic 類代表特徵。下圖2展示了到目前爲止我們所講的所有類。


CBUUID CBATTRequest 是兩個蘋果公司給我們提供的幫助類,以便於開發者更簡單地操作數據,稍後你將看到怎麼使用這兩個類。

用法

很不幸,蘋果公司的CoreBluetooth文檔目前不是很完整,一些複雜的類確實沒有文檔說明。你只能通過去看WWCD視頻去理解這個框架是怎麼工作的。因爲在前一段時間我已經做過了這些,所以決定分享給大家。在NeuroSky,我們已經用CoreBluetooth框架開發了一些App。我很希望在這個教程可以幫到你。如果你想學到更多關於CoreBluetooth的知識,你可以出席我們的培訓課,在這兒查看下一堂課: http://training.invasivecode.com

創建一個周邊:

讓我們開始創建一個完整的例子,你需要兩個iOS設備。我將告訴你怎麼連接2iOS設備,通過藍牙交換數據,記住檢查你的iOS設備是不是在上邊列出的支持BLE的列表中。

開始創建一個周邊,跟着以下步驟(計劃):

1.創建並且開始Peripheral Manager

2.設置並且發佈這個周邊的所提供的服務。

3.廣播這個服務。

4.和中央相互作用。

Single-View Application模板創建一個新的Xcode工程。命名爲BlueServer (使用ARC)。工程創建完成後,添加CoreBluetooth.framework 框架。然後打開ViewController.h文件,並且添加以下代碼:

#import <CoreBluetooth/CoreBluetooth.h>

使view controller繼承CBPeripheralManagerDelegate 協議,然後添加這個屬性:

@property (nonatomic, strong) CBPeripheralManager *manager;

ViewController.m中,添加以下代碼到viewDidLoad方法中:

self.manager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];

這行代碼創建了一個Peripheral Manager(計劃中的第一項)。第一個參數是設置代理,這兒就是view controller,第二個參數設置爲nil,因爲Peripheral ManagerRun在主線程中。如果你想用不同的線程做更加複雜的事情,你需要創建一個隊列(queue)並將它放在這兒。

一旦Peripheral Manager被創建,我們應該及時地檢查它的狀態,看正在運行App的這個設備是不是支持BLE標準。所以要實現以下這個代理方法,在這兒你可以做更復雜的一些事情和友好地提醒用戶,如果設備不支持BLE

這個代理方法用戶檢測設備的藍牙狀態

- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {

    switch (peripheral.state) {

        case CBPeripheralManagerStatePoweredOn:

            [self setupService];

            break;

        default:

            NSLog(@"Peripheral Manager did change state");

            break;

    }

}

這兒,我檢查了周邊的狀態,如果他的狀態是CBPeripheralManagerStatePoweredOn,這個設備是支持BLE並且可用的。

服務和特徵(Service and Characteristic)

setupService 方法是一個幫助方法,我即將創建它,讓它去準備服務和特徵,對於這個例子,我們僅僅需要只有一個特徵的一個服務。

每一個服務和特徵都需要用一個UUIDunique identifier)去標識UUID是一個16bit或者128bit的值。如果你要創建你的中央-周邊App,你需要創建你自己的128bitUUID你必須要確定你自己的UUID不能和其他已經存在的服務衝突。如果你正要創建一個自己的設備,需要實現標準委員會需求的UUID;如果你只是創建一箇中央-周邊App(就像我們現在做的這樣),我建議你打開Mac OS XTerminal.app,用uuidgen命令生成一個128bitUUID。你應該用該命令兩次,生成兩個UUID,一個是給服務用的,一個是給特徵用的。然後,你需要添加他們到中央和周邊App中。現在,在view controller的實現之前,我們添加以下的代碼:

static NSString * const kServiceUUID = @"312700E2-E798-4D5C-8DCF-49908332DF9F";

static NSString * const kCharacteristicUUID = @"FFA28CDE-6525-4489-801C-1C060CAC9767";

注意:你自己用uuidgen生成的UUID和我的是不一樣的。

現在,以下是setupService 方法的實現:

- (void)setupService {

    // Creates the characteristic UUID

    CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID];

    // Creates the characteristic

    self.customCharacteristic = [[CBMutableCharacteristic alloc] initWithType:

characteristicUUID properties:CBCharacteristicPropertyNotify 

value:nil permissions:CBAttributePermissionsReadable];

    // Creates the service UUID

    CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID];

    // Creates the service and adds the characteristic to it

    self.customService = [[CBMutableService alloc] initWithType:serviceUUID

 primary:YES];

    // Sets the characteristics for this service

    [self.customService setCharacteristics:

@[self.customCharacteristic]];

    // Publishes the service

    [self.peripheralManager addService:self.customService];

首先,我用+UUIDWithString:方法給特徵創建了一個UUID對象,然後我用這個UUID對象創建了一個特徵。注意:我給初始化特徵方法的第三個參數賦值nil,這是,我告訴CoreBluetooth,我將稍後添加一個特徵的值。當你想要創建一個動態的數據,一般都這麼做。如果你已經有了一直靜態的值要,你可以將它賦值在這兒。

在初始化特徵的方法中,第一個參數是剛剛創建的UUID,第二個參數決定這個特徵怎麼使用。一下是所有可能的值:

 ■CBCharacteristicPropertyBroadcast: permits broadcasts of 

the characteristic value using a characteristic configuration

 descriptor. Not allowed for local characteristics.

 

 CBCharacteristicPropertyRead: permits reads of the 

characteristic value.

 CBCharacteristicPropertyWriteWithoutResponse:

 permits writes of the characteristic value, 

without a response.

 CBCharacteristicPropertyWrite: permits writes of

 the characteristic value.

 CBCharacteristicPropertyNotify: permits notifications 

of the characteristic value, without a response.

 CBCharacteristicPropertyIndicate: permits indications

 of the characteristic value.

 CBCharacteristicPropertyAuthenticatedSignedWrites

permits signed writes of the characteristic value

 CBCharacteristicPropertyExtendedProperties: if set, 

additional characteristic properties are defined in 

the characteristic extended properties descriptor. 

Not allowed for local characteristics.

 CBCharacteristicPropertyNotifyEncryptionRequired  : 

if set, only trusted devices can enable notifications

 of the characteristic value.

 CBCharacteristicPropertyIndicateEncryptionRequired:

 if set, only trusted devices can enable indications 

of the characteristic value.

 

最後一個參數是屬性的讀、寫、加密的權限,可能的值是以下的:

 CBAttributePermissionsReadable

 CBAttributePermissionsWriteable

 CBAttributePermissionsReadEncryptionRequired

 CBAttributePermissionsWriteEncryptionRequired

 

在創建了一個特徵以後,又用了一次+UUIDWithString:方法,創建了一個服務。最後,我將特徵添加到了服務上。記住,每一個服務可以包含多個特徵。參見下圖3.


因此,我們需要創建一個特徵的數組並且把這個數組傳給服務。在這個例子中,數組僅僅包含一個特徵。

代碼的最後一行把服務添加到周邊管理者(Peripheral Manager)是用於發佈服務。一旦完成這個,周邊管理者會通知他的代理方法-peripheralManager:didAddService:error:。現在,如果沒有Error,你可以開始廣播服務了

- (void)peripheralManager:(CBPeripheralManager *)peripheral

 didAddService:(CBService *)service error:(NSError *)error {

    if (error == nil) {

        // Starts advertising the service

        [self.peripheralManager startAdvertising:

@{ CBAdvertisementDataLocalNameKey : 

@"ICServer", CBAdvertisementDataServiceUUIDsKey :

 @[[CBUUID UUIDWithString:kServiceUUID]] }];

    }

}

當週邊管理者開始廣播服務,他的代理接收-peripheralManagerDidStartAdvertising:error: 消息,並且當中央預定了這個服務,他的代理接收 -peripheralManager:central:didSubscribeToCharacteristic:消息,這兒是你給中央生成動態數據的地方。

現在,要發送數據到中央,你需要準備一些數據,然後發送updateValue:forCharacteristic:onSubscribedCentrals: 到周邊。


創建一箇中央

現在,我們已經有了一個周邊,讓我們創建我們的中央。中央就是那個處理周邊發送來的數據的設備。在下圖1中,CBCentralManager對象代表中央。

創建一個新的Xcode工程,命名爲BlueClient,記得使用ARC。添加CoreBluetooth.framework框架到你的工程,並且導入頭文件到view controller,以下代碼:

#import <CoreBluetooth/CoreBluetooth.h>

在中央這邊,你的類必須要繼承這兩個協議:CBCentralManagerDelegateCBPeripheralDelegate,以下代碼:

@interface ViewController : UIViewController <CBCentralManagerDelegate,

 CBPeripheralDelegate>

並且添加以下兩個屬性:

@property (nonatomic, strong) CBCentralManager *manager;

@property (nonatomic, strong) NSMutableData *data;

現在,我要創建一箇中央對象了:

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

和創建周邊時一樣,第一個參數代表CBCentralManager 代理,在這個例子中就是這個view controller;第二個參數設置爲nil,因爲Peripheral ManagerRun在主線程中。如果你想用不同的線程做更加複雜的事情,你需要創建一個隊列(queue)並將它放在這兒。

Central Manager被初始化,我們要檢查它的狀態,以檢查運行這個App的設備是不是支持BLE。實現以下的代理方法:

- (void)centralManagerDidUpdateState:

(CBCentralManager *)central {

    switch (central.state) {

        case CBCentralManagerStatePoweredOn:

            // Scans for any peripheral

            [self.manager scanForPeripheralsWithServices:

@[ [CBUUID UUIDWithString:kServiceUUID] ]

 options:@{CBCentralManagerScanOptionAllowDuplicatesKey :

 @YES }];

            break;

        default:

            NSLog(@"Central Manager did change state");

            break;

    }

}

尋找服務:-scanForPeripheralsWithServices:options: 方法是用於告訴Central Manager,要開始尋找一個指定的服務了。如果你將第一個參數設置爲nilCentral Manager就會開始尋找所有的服務。

kServiceUUID 和創建周邊的那個工程中用的是一樣的UUID。所以,再添加一次以下兩行代碼:

static NSString * const kServiceUUID = @"312700E2-E798-4D5C-8DCF-49908332DF9F";

static NSString * const kCharacteristicUUID = @"FFA28CDE-6525-4489-801C-1C060CAC9767";

記住UUID是你自己用uuidgen命令生成的。

發現服務:一旦一個周邊在尋找的時候被發現,中央的代理會收到以下回調:

- (void)centralManager:(CBCentralManager *)

central didDiscoverPeripheral:(CBPeripheral *)peripheral

 advertisementData:(NSDictionary *)advertisementData

 RSSI:(NSNumber *)RSSI   

這個調用通知Central Manager代理(在這個例子中就是view controller),一個附帶着廣播數據和信號質量(RSSI-Received Signal Strength Indicator)的周邊被發現。這是一個很酷的參數,知道了信號質量,你可以用它去判斷遠近。

任何廣播、掃描的響應數據保存在advertisementData 中,可以通過CBAdvertisementData 來訪問它。現在,你可以停止掃描,而去連接周邊了:

- (void)centralManager:(CBCentralManager *)central

 didDiscoverPeripheral:(CBPeripheral *)peripheral

 advertisementData:(NSDictionary *)advertisementData

 RSSI:(NSNumber *)RSSI {

    // Stops scanning for peripheral

    [self.manager stopScan];

    if (self.peripheral != peripheral) {

        self.peripheral = peripheral;

        NSLog(@"Connecting to peripheral %@", peripheral);

        // Connects to the discovered peripheral

        [self.manager connectPeripheral:peripheral options:nil];//連接設備

    }

}

options 參數是一個可選的NSDictionary,如果需要,可以用以下的鍵(Key),它們的值始終是一個boolean

 CBConnectPeripheralOptionNotifyOnConnectionKey

This is a NSNumber (Boolean) indicating that the 

system should display an alert for a given peripheral,

 if the application is suspended when a successful 

connection is made. This is useful for applications

 that have not specified the Central background mode 

and cannot display their own alert. If more than one 

application has requested notification for a given 

peripheral, the one that was most recently in the 

foreground will receive the alert.

 CBConnectPeripheralOptionNotifyOnDisconnectionKey

This is a NSNumber (Boolean) indicating that the 

system should display a disconnection alert for a 

given peripheral, if the application is suspended 

at the time of the disconnection. This is useful for

applications that have not specified the Central 

background mode and cannot display their own 

alert. If more than one application has requested

 notification for a given peripheral, the one that was

 most recently in the foreground will receive the alert.

 CBConnectPeripheralOptionNotifyOnNotificationKey

This is a NSNumber (Boolean) indicating that the 

system should display an alert for all notifications 

received from a given peripheral, if the application

is suspended at the time. This is useful for 

applications that have not specified the Central 

background mode and cannot display their own

alert. If more than one application has requested

notification for a given peripheral, the one that 

was most recently in the foreground will receive the alert.

 

基於連接的結果,代理(這個例子中是view controller)會接收centralManager:didFailToConnectPeripheral:error:或者centralManager:didConnectPeripheral:。如果成功了,你可以訪問廣播服務的那個周邊。因此,在didConnectPeripheral 回調中,你可以寫以下代碼:

- (void)centralManager:(CBCentralManager *)central 

didConnectPeripheral:(CBPeripheral *)peripheral {

    // Clears the data that we may already have

    [self.data setLength:0];

    // Sets the peripheral delegate

    [self.peripheral setDelegate:self];

    // Asks the peripheral to discover the service

    [self.peripheral discoverServices:

@[ [CBUUID UUIDWithString:kServiceUUID] ]];

}

現在,周邊開始用一個回調通知它的代理。在上一個方法中,我請求周邊去尋找服務,周邊代理接收-peripheral:didDiscoverServices:。如果沒有Error,可以請求周邊去尋找它的服務所列出的特徵,像以下這麼做:

- (void)peripheral:(CBPeripheral *)aPeripheral

 didDiscoverServices:(NSError *)error {

    if (error) {

        NSLog(@"Error discovering service:

 %@", [error localizedDescription]);

        [self cleanup];

        return;

    }

    for (CBService *service in aPeripheral.services) {

        NSLog(@"Service found with UUID: %@",

 service.UUID);

        // Discovers the characteristics for a given service

        if ([service.UUID isEqual:[CBUUID 

UUIDWithString:kServiceUUID]]) {

            [self.peripheral discoverCharacteristics:

@[[CBUUID UUIDWithString:

kCharacteristicUUID]] forService:service];

        }

    }

}   

現在,如果一個特徵被發現,周邊代理會接收-peripheral:didDiscoverCharacteristicsForService:error:。現在,一旦特徵的值被更新,用-setNotifyValue:forCharacteristic:,周邊被請求通知它的代理。

- (void)peripheral:(CBPeripheral *)peripheral 

didDiscoverCharacteristicsForService:

(CBService *)service error:(NSError *)error {

    if (error) {

        NSLog(@"Error discovering characteristic:

 %@", [error localizedDescription]);

        [self cleanup];

        return;

    }

    if ([service.UUID isEqual:[CBUUID UUIDWithString:

kServiceUUID]]) {

        for (CBCharacteristic *characteristic in 

service.characteristics) {

            if ([characteristic.UUID isEqual:[CBUUID 

UUIDWithString:kCharacteristicUUID]]) {

                [peripheral setNotifyValue:YES 

forCharacteristic:characteristic];

            }

        }

    }

}

這兒,如果一個特徵的值被更新,然後周邊代理接收-peripheral:didUpdateNotificationStateForCharacteristic:error:。你可以用-readValueForCharacteristic:讀取新的值:

- (void)peripheral:(CBPeripheral *)peripheral 

didUpdateNotificationStateForCharacteristic:

(CBCharacteristic *)characteristic error:(NSError *)error {

    if (error) {

        NSLog(@"Error changing notification state:

 %@", error.localizedDescription);

    }

    // Exits if it's not the transfer characteristic

    if (![characteristic.UUID isEqual:[CBUUID 

UUIDWithString:kCharacteristicUUID]]) {

        return;

    }

    // Notification has started

    if (characteristic.isNotifying) {

        NSLog(@"Notification began on %@", characteristic);

        [peripheral readValueForCharacteristic:characteristic];

    } else { // Notification has stopped

        // so disconnect from the peripheral

        NSLog(@"Notification stopped on %@.

  Disconnecting", characteristic);

        [self.manager cancelPeripheralConnection:self.peripheral];

    }

}

當週邊發送新的值,周邊代理接收-peripheral:didUpdateValueForCharacteristic:error:。這個方法的第二個參數包含特徵。你可以用value屬性讀取他的值。這是一個包含特徵的值的NSData

 結語:

I showed you a basic example on how to use the 

Core Bluetooth framework. I hope that this 

small tutorial, the WWDC videos and the 

documentation (the available one) can help 

you create new iOS applications using the 

Bluetooth LE. Check also the examples 

coming with the documentation. There, 

you will find all the delegate methods

 I used in this tutorial.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章