iOS開發藍牙4.0的框架是CoreBluetooth,本文主要介紹CoreBluetooth的使用,關於本文中的代碼片斷大多來自github上的一個demo,地址是myz1104/Bluetooth。
在CoreBluetooth中有兩個主要的部分,Central和Peripheral,有一點相似Client Server。CBPeripheralManager 做爲周邊設備是服務器。CBCentralManager做爲中心設備是客戶端。全部可用的iOS設備能夠做爲周邊(Peripheral)也能夠做爲中央(Central),但不能夠同時既是周邊也是中央。
git
通常手機是客戶端, 設備(好比手環)是服務器,由於是手機去鏈接手環這個服務器。周邊(Peripheral)是生成或者保存了數據的設備,中央(Central)是使用這些數據的設備。你能夠認爲周邊是一個廣播數據的設備,他廣播到外部世界說他這兒有數據,而且也說明了能提供的服務。另外一邊,中央開始掃描附近有沒有服務,若是中央發現了想要的服務,而後中央就會請求鏈接周邊,一旦鏈接創建成功,兩個設備之間就開始交換傳輸數據了。
除了中央和周邊,咱們還要考慮他倆交換的數據結構。這些數據在服務中被結構化,每一個服務由不一樣的特徵(Characteristics)組成,特徵是包含一個單一邏輯值的屬性類型。github
首先是建立一個周邊數組
_peripheralManager = [[CBPeripheralManager alloc]initWithDelegate:self queue:nil];
接下來它就會響應代理的peripheralManagerDidUpdateState方法,能夠得到peripheral的狀態等信息,服務器
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral
{
switch (peripheral.state)
{
case CBPeripheralManagerStatePoweredOn:
{
[self setupService];
}
break;
default:
{
NSLog(@"Peripheral Manager did change state");
}
break;
}
}
當發現周邊設備的藍牙是能夠的時候,這就須要去準備你須要廣播給其餘中央設備的服務和特徵了,這裏經過調用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 = @"1C85D7B7-17FA-4362-82CF-85DD0B76A9A5";
static NSString * const kCharacteristicUUID = @"7E887E40-95DE-40D6-9AA0-36EDE2BAE253";
下面就是setupService方法app
- (void)setupService
{
CBUUID *characteristicUUID = [CBUUID UUIDWithString:kCharacteristicUUID];
self.customCharacteristic = [[CBMutableCharacteristic alloc] initWithType:characteristicUUID properties:CBCharacteristicPropertyNotify value:nil permissions:CBAttributePermissionsReadable];
CBUUID *serviceUUID = [CBUUID UUIDWithString:kServiceUUID];
self.customService = [[CBMutableService alloc] initWithType:serviceUUID primary:YES];
[self.customService setCharacteristics:@[self.customCharacteristic]];
[self.peripheralManager addService:self.customService];
}
當調用了CBPeripheralManager的addService方法後,這裏就會響應CBPeripheralManagerDelegate的- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error方法。這個時候就能夠開始廣播咱們剛剛建立的服務了。框架
- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error
{
if (error == nil)
{
[self.peripheralManager startAdvertising:@{ CBAdvertisementDataLocalNameKey : @"ICServer", CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:kServiceUUID]] }];
}
}
固然到這裏,你已經作完了peripheralManager的工做了,中央設備已經能夠接受到你的服務了。不過這是靜止的數據,你還能夠調用- (BOOL)updateValue:(NSData *)value forCharacteristic:(CBMutableCharacteristic *)characteristic onSubscribedCentrals:(NSArray *)centrals方法能夠給中央生成動態數據的地方。async
- (void)sendToSubscribers:(NSData *)data {
if (self.peripheral.state != CBPeripheralManagerStatePoweredOn) {
LXCBLog(@"sendToSubscribers: peripheral not ready for sending state: %d", self.peripheral.state);
return;
}
BOOL success = [self.peripheral updateValue:data
forCharacteristic:self.characteristic
onSubscribedCentrals:nil];
if (!success) {
LXCBLog(@"Failed to send data, buffering data for retry once ready.");
self.pendingData = data;
return;
}
}
central訂閱了characteristic的值,當更新值的時候peripheral會調用updateValue: forCharacteristic: onSubscribedCentrals:(NSArray*)centrals去爲數組裏面的centrals更新對應characteristic的值,在更新事後peripheral爲每個central走一遍下面的代理方法ide
- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic
peripheral接受到一個讀或者寫的請求時,會響應如下兩個代理方法ui
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray *)requests
那麼如今peripheral就已經建立好了。
建立中央而且鏈接周邊
如今,咱們已經有了一個周邊,讓咱們建立咱們的中央。中央就是那個處理周邊發送來的數據的設備。
self.manager = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue()];
當Central Manager被初始化,咱們要檢查它的狀態,以檢查運行這個App的設備是否是支持BLE。實現CBCentralManagerDelegate的代理方法:
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
switch (central.state)
{
case CBCentralManagerStatePoweredOn:
{
[self.manager scanForPeripheralsWithServices:@[ [CBUUID UUIDWithString:kServiceUUID]]
options:@{CBCentralManagerScanOptionAllowDuplicatesKey : @YES }];
}
break;
default:
{
NSLog(@"Central Manager did change state");
}
break;
}
}
當app的設備是支持藍牙的時候,須要調用CBCentralManager實例的- (void)scanForPeripheralsWithServices:(NSArray *)serviceUUIDs options:(NSDictionary *)options方法,用來尋找一個指定的服務的peripheral。一旦一個周邊在尋找的時候被發現,中央的代理會收到如下回調:
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
NSString *UUID = [peripheral.identifier UUIDString];
NSString *UUID1 = CFBridgingRelease(CFUUIDCreateString(NULL, peripheral.UUID));
NSLog(@"----發現外設----%@%@", UUID,UUID1);
[self.manager stopScan];
if (self.peripheral != peripheral)
{
self.peripheral = peripheral;
NSLog(@"Connecting to peripheral %@", peripheral);
[self.manager connectPeripheral:peripheral options:nil];
}
}
這個時候一個附帶着廣播數據和信號質量(RSSI-Received Signal Strength Indicator)的周邊被發現。這是一個很酷的參數,知道了信號質量,你能夠用它去判斷遠近。任何廣播、掃描的響應數據保存在advertisementData 中,能夠經過CBAdvertisementData 來訪問它。
這個時候你用能夠鏈接這個周邊設備了,
[self.manager connectPeripheral:peripheral options:nil];
它會響應下面的代理方法,
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
NSLog(@"----成功鏈接外設----");
[self.peripheral setDelegate:self];
[self.peripheral discoverServices:@[ [CBUUID UUIDWithString:kServiceUUID]]];
}
訪問周邊的服務
上面的CBCentralManagerDelegate代理會返回CBPeripheral實例,它的- (void)discoverServices:(NSArray *)serviceUUIDs方法就是訪問周邊的服務了,這個方法會響應CBPeripheralDelegate的方法。
- (void)peripheral:(CBPeripheral *)aPeripheral didDiscoverServices:(NSError *)error
{
NSLog(@"----didDiscoverServices----Error:%@",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);
if ([service.UUID isEqual:[CBUUID UUIDWithString:kServiceUUID]])
{
[self.peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:kCharacteristicUUID],[CBUUID UUIDWithString:kWrriteCharacteristicUUID]] forService:service];
}
}
}
在上面的方法中若是沒有error,能夠調用discoverCharacteristics方法請求周邊去尋找它的服務所列出的特徵,它會響應下面的方法
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
if (error)
{
NSLog(@"Error discovering characteristic: %@", [error localizedDescription]);
return;
}
if ([service.UUID isEqual:[CBUUID UUIDWithString:kServiceUUID]])
{
for (CBCharacteristic *characteristic in service.characteristics)
{
NSLog(@"----didDiscoverCharacteristicsForService---%@",characteristic);
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kCharacteristicUUID]])
{
[peripheral readValueForCharacteristic:characteristic];
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
}
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kWrriteCharacteristicUUID]])
{
writeCharacteristic = characteristic;
}
}
}
}
這個時候peripheral能夠調用兩個方法,
[peripheral readValueForCharacteristic:characteristic]這個是讀特徵值的,會響應- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error;
[peripheral setNotifyValue:YES forCharacteristic:characteristic];會響應- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error;
- (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]] )
{
// 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];
}
}
}
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
NSLog(@"----Value---%@",characteristic.value);
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kCharacteristicUUID]])
{
if (writeCharacteristic)
{
Byte ACkValue[3] = {0};
ACkValue[0] = 0xe0; ACkValue[1] = 0x00; ACkValue[2] = ACkValue[0] + ACkValue[1];
NSData *data = [NSData dataWithBytes:&ACkValue length:sizeof(ACkValue)];
[self.peripheral writeValue:data
forCharacteristic:writeCharacteristic
type:CBCharacteristicWriteWithoutResponse];
}
}
}
在上面的方法中,- (void)writeValue:(NSData *)data forCharacteristic:(CBCharacteristic *)characteristic type:(CBCharacteristicWriteType)type是一個對周邊設備寫數據的方法,它會響應下面的方法
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
NSLog(@"---didWriteValueForCharacteristic-----");
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kWrriteCharacteristicUUID]])
{
NSLog(@"----value更新----");
}
}
這樣,中央設備也實現了讀寫數據的功能了。
另外,github上有一個封裝的第三方開源藍牙框架,地址是kickingvegas/YmsCoreBluetooth