iOS CoreBluetooth 教程
去App Store搜索并下載“LightBlue”這個App,對調試你的app和理解Core Bluetooth會很有幫助。
================================
Demo下載地址:http://download.csdn.net/detail/jimoduwu/7146875
================================
Core?Bluetooth?for?iOS?6
CoreBluetooth的API是基于BLE4.0的標準的。這個框架涵蓋了BLE標準的所有細節。僅僅只有新的iOS設備和Mac是和BLE標準兼容的:iPhone4S,iPhone5,MacMini,New?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設備。我將告訴你怎么連接2個iOS設備,通過藍牙交換數據,記住檢查你的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?Manager將Run在主線程中。如果你想用不同的線程做更加復雜的事情,你需要創建一個隊列(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?方法是一個幫助方法,我即將創建它,讓它去準備服務和特征,對于這個例子,我們僅僅需要只有一個特征的一個服務。
每一個服務和特征都需要用一個UUID(unique?identifier)去標識,UUID是一個16bit或者128bit的值。如果你要創建你的中央-周邊App,你需要創建你自己的128bit的UUID。你必須要確定你自己的UUID不能和其他已經存在的服務沖突。如果你正要創建一個自己的設備,需要實現標準委員會需求的UUID;如果你只是創建一個中央-周邊App(就像我們現在做的這樣),我建議你打開Mac?OS?X的Terminal.app,用uuidgen命令生成一個128bit的UUID。你應該用該命令兩次,生成兩個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>
在中央這邊,你的類必須要繼承這兩個協議:CBCentralManagerDelegate和CBPeripheralDelegate,以下代碼:
@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?Manager將Run在主線程中。如果你想用不同的線程做更加復雜的事情,你需要創建一個隊列(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,要開始尋找一個指定的服務了。如果你將第一個參數設置為nil,Central?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.
總結
以上是生活随笔為你收集整理的iOS CoreBluetooth 教程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ipad/iphone启动界面Defau
- 下一篇: ios使用自带git resposito