1.藍(lán)牙的基礎(chǔ)知識
1. iOS中開發(fā)藍(lán)牙常用的系統(tǒng)庫是<CoreBluetooth/CoreBluetooth.h>。
2.藍(lán)牙外設(shè)必需為4.0及以上(2.0需要MFI認(rèn)證),否則無法進(jìn)行開發(fā),藍(lán)牙4.0設(shè)施由于低耗電,所以也叫做BLE。
3. CoreBluetooth框架的核心其實(shí)是倆東西
? ? 3.1 Peripheral
? ? 3.2?Central
4. 服務(wù)和特征(service characteristic):簡而言之,外部藍(lán)牙中它有若干個(gè)服務(wù)service(服務(wù)你能了解為藍(lán)牙所擁有的可以力),而每個(gè)服務(wù)service下?lián)碛腥舾蓚€(gè)特征characteristic(特征你能了解為解釋這個(gè)服務(wù)的屬性)。
5. Descriptor(形容)使用來形容characteristic變量的屬性。例如,一個(gè)descriptor能規(guī)定一個(gè)可讀的形容,或者者一個(gè)characteristic變量可接受的范圍,或者者一個(gè)characteristic變量特定的單位。
2.藍(lán)牙流程圖

3. 藍(lán)牙連接的主要步驟
? ? ?3.1 創(chuàng)建一個(gè)CBCentralManager實(shí)例來進(jìn)行藍(lán)牙管理;
? ? ?3.2 搜索掃描外圍設(shè)備;
?????3.3 連接外圍設(shè)備;
? ? ?3.4 獲得外圍設(shè)備的服務(wù);
? ? ?3.5 獲得服務(wù)的特征;
? ? ?3.6 從外圍設(shè)備讀取數(shù)據(jù);
? ? ?3.7 給外圍設(shè)備發(fā)送(寫入)數(shù)據(jù)。
4.如何掃描藍(lán)牙
?4.1 初始化
dispatch_queue_t centralQueue = dispatch_queue_create(“centralQueue",DISPATCH_QUEUE_SERIAL);
NSDictionary *dic = @{CBCentralManagerOptionRestoreIdentifierKey : restoreIdentifier};
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:centralQueue options:dic];
CBCentralManagerOptionRestoreIdentifierKey對應(yīng)的是一個(gè)唯一標(biāo)識的字符串,用于藍(lán)牙進(jìn)程被殺掉恢復(fù)連接時(shí)使用
4.2 掃描
/*
?*掃描設(shè)備
?*/
- (void)scanForDevices:(NSError**)error
{
? ? if (CBCentralManagerStatePoweredOn == self.centralManager.state) {
? ? ? ? //取回已連接的service設(shè)備
? ? ? ? NSArray* retrievedPeripherals = [self.centralManagerretrieveConnectedPeripheralsWithServices:@[self.serviceUUID]];
? ? ? ? for(CBPeripheral* peripheralinretrievedPeripherals){
? ? ? ? ? ? //NSLog(@"retrieved peripheral:%@", peripheral);
? ? ? ? ? ? [self.delegateclient:selfdidDiscoverDevice:peripheral.identifier];
? ? ? ? }
? ? ? ? //啟動掃描
? ? ? ? if (self.advertisementUUID) {
? ? ? ? ? ? [self.centralManager scanForPeripheralsWithServices:@[ self.advertisementUUID ] options:@{ CBCentralManagerScanOptionAllowDuplicatesKey:@YES }];
? ? ? ? }else{
? ? ? ? ? ? [self.centralManager scanForPeripheralsWithServices:nil options:@{ CBCentralManagerScanOptionAllowDuplicatesKey:@YES }];
//? ? ? ? ? ? [self.centralManager scanForPeripheralsWithServices:nil options:nil];
? ? ? ? }
? ? }else{
? ? ? ? if(error !=NULL) {
? ? ? ? ? ? *error = [NSErrorerrorWithDomain:HCErrorDomaincode:(SRVClientErrorUnknown+self.centralManager.state)userInfo:nil];
? ? ? ? ? ? NSLog(@"[NSError errorWithDomain:HCErrorDomain code:(SRVClientErrorUnknown + self.centralManager.state) userInfo:nil];");
? ? ? ? }
? ? }
}
4.3 發(fā)現(xiàn)外圍設(shè)備
- (void)centralManager:(CBCentralManager*)centraldidDiscoverPeripheral:(CBPeripheral*)peripheraladvertisementData:(NSDictionary*)advertisementDataRSSI:(NSNumber*)RSSI {
? ? NSString*peripheralName = peripheral.name;
? ? if(peripheralName ==nil|| peripheralName.length==0) {
? ? ? ? return;
? ? }
? ? if([peripheralNameisEqualToString:SRV_CLIENT_DEV_NAME] || [peripheralNameisEqualToString:SRV_CLIENT_DFU_NAME]) {
? ?}
}
4.4 連接外圍設(shè)備
//藍(lán)牙連接成功回調(diào)
- (void)centralManager:(CBCentralManager*)centraldidConnectPeripheral:(CBPeripheral*)peripheral {
? ? [self.centralManager stopScan];
? ? peripheral.delegate=self;
? ? self.commandNo=0;
? ? NSLog(@"[D] CentralManager Discover services.");
? ? NSLog(@"%@",self.peripheral);
? ? self.peripheral.delegate=self;
? ? [self.peripheral discoverServices:@[self.serviceUUID]];
? ? NSLog(@"%@",self.serviceUUID);
? ? //定時(shí)獲取RSSI
? ? if (self.needReadRSSI) {
? ? ? ? [self readPeripheralRSSI];
? ? ? ? if(!self.rssiTimer) {
? ? ? ? ? ? self.rssiTimer = [NSTimer scheduledTimerWithTimeInterval:5.0
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? target:self
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? selector:@selector(readPeripheralRSSI)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? userInfo:nil
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? repeats:YES];
? ? ? ? }
? ? }
}
#pragma?mark?連接外設(shè)——失敗
-?(void)centralManager:(CBCentralManager?*)central?didFailToConnectPeripheral:(CBPeripheral?*)peripheral?error:(NSError?*)error{
????NSLog(@"%@",?error);
}
#pragma?mark?取消與外設(shè)的連接回調(diào)
-?(void)centralManager:(CBCentralManager?*)central?didDisconnectPeripheral:(CBPeripheral?*)peripheral?error:(NSError?*)error{
????NSLog(@"%@",?peripheral);
}
4.5?獲得外圍設(shè)備的服務(wù)
//發(fā)現(xiàn)服務(wù)的回調(diào)
- (void)peripheral:(CBPeripheral*)peripheraldidDiscoverServices:(NSError*)error
{
? ? NSLog(@"%@---didDiscoverServices",peripheral);
? ? if(error){
? ? ? ? NSLog(@"[E] peripheral didDiscoverServices error: %@", error.localizedDescription);
? ? ? ? [self cancelConnection];
? ? ? ? return;
? ? }
? ? for(CBService* serviceinperipheral.services){
? ? ? ? NSLog(@"[D] Discover characteristics. For service = %@", service);
? ? ? ? [peripheraldiscoverCharacteristics:nil forService:service];
? ? }
}
//發(fā)現(xiàn)特征的回調(diào)
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
? ? if(error){
? ? ? ? NSLog(@"[E] peripheral didDiscoverCharacteristicsForService error: %@", error.localizedDescription);
? ? ? ? [self cancelConnection];
? ? ? ? return;
? ? }
? ? NSLog(@"[D] peripheral DiscoverCharacteristics = %@", service.characteristics);
? ? //訂閱特征
? ? for(CBCharacteristic*characteristicinservice.characteristics){
? ? ? ? if (characteristic.properties & (CBCharacteristicPropertyNotify|CBCharacteristicPropertyIndicate)){
? ? ? ? ? ? if(!characteristic.isNotifying) {
? ? ? ? ? ? ? ? if([self.ignoreCharacteristicUUIDscontainsObject:characteristic.UUID]) {
? ? ? ? ? ? ? ? ? ? continue;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? NSLog(@"[D] Enable notify value. For characteristic = %@", characteristic);
? ? ? ? ? ? ? ? //d訂閱特性當(dāng)數(shù)據(jù)頻繁改變時(shí)用 setNotifyValue 不頻繁時(shí)用readValueForCharacteristic
? ? ? ? ? ? ? ? [peripheralsetNotifyValue:YESforCharacteristic:characteristic];
? ? ? ? ? ? }
? ? ? ? }
? ? }
}
// 訂閱后的callback
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
? ? if(error){
? ? ? ? NSLog(@"[E] peripheral didUpdateNotificationStateForCharacteristic error: %@", error.localizedDescription);
? ? ? ? [self cancelConnection];
? ? ? ? return;
? ? }
? ? if ([self isAllCharacteristicNotificationEnabled]){
? ? ? ? NSLog(@"訂閱成功");
? ? ? ?//authorizeRequest 授權(quán)認(rèn)證
? ? ? ? [self.delegate clientDidPrepareForOperation:self];
? ? }
//? ? [self.delegate clientDidPrepareForOperation:self];
}
4.6?從外圍設(shè)備讀取數(shù)據(jù)
// peripheral主動發(fā)數(shù)據(jù),包括寫命令后主動返回的狀態(tài) 讀數(shù)據(jù)的回調(diào)
- (void)peripheral:(CBPeripheral*)peripheraldidUpdateValueForCharacteristic:(CBCharacteristic*)characteristicerror:(NSError*)error
{
? ? if(error) {
? ? ? ? NSLog(@"[E] peripheral didUpdateValueForCharacteristic error: %@ %@", error.localizedDescription,characteristic);
? ? ? ? [self cancelConnection];
? ? ? ? [self cleanupOperationUnexpectedly];
? ? ? ? return;
? ? }
? ? NSLog(@"%@",peripheral);
? ? NSLog(@"%@",characteristic);
? ? [self.delegate client:self didUpdateValueForCharacteristic:characteristic.UUID value:characteristic.value];
? ? if([characteristic.UUIDisEqual:self.ctrlptUUID]) {
? ? ? ? ? ? if (CTRLPTProgressWaitResp == self.ctrlptProgress) {
? ? ? ? ? }
? ? }
?}
4.7?給外圍設(shè)備發(fā)送(寫入)數(shù)據(jù)
- (BOOL)performOperationSegment:(CBCharacteristic*)characteristic
{
? ? ? ??BOOLisLastSegment;
? ? ? ? uint8_tsegment[20];
? ? ? ? uint16_tindex =0;
? ? ? ? uint16_tsegLength;
? ? ? ? NSIntegerforwardLength =self.forwardFlow.length;
? ? ? ?if((forwardLength -self.forwardOffset) > (20- index)) {
? ? ? ? ? ? isLastSegment =NO;
? ? ? ? ? ? segLength = (20- index);
? ? ? ? }else{
? ? ? ? ? ? isLastSegment =YES;
? ? ? ? ? ? segLength = (forwardLength -self.forwardOffset);
? ? ? ? }
? ? ? ? memcpy(&segment[index], &self.forwardFlow.bytes[self.forwardOffset], segLength);
? ? ? ? self.forwardOffset+= segLength;
? ? ? ? index += segLength;
? ? ? ? NSData*writeData = [NSDatadataWithBytes:segmentlength:index];
? ? ? ? NSLog(@"[D] Write value = %@. For characteristic = %@", writeData, characteristic);
? ? ? ? [self.peripheral writeValue:writeData forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];//對于操控類藍(lán)牙,數(shù)據(jù)寫入要求非常快,其中writeWithSponce寫入消耗的時(shí)間是writeWithoutSponce的2.3倍,因此盡量改寫成writeWithoutSponce來提升寫入速率
? ? ? ??returnisLastSegment;
?}
//是否寫入成功的回調(diào)
- (void)peripheral:(CBPeripheral*)peripheraldidWriteValueForCharacteristic:(CBCharacteristic*)characteristicerror:(NSError*)error
{
? ? if(error) {
? ? ? ? NSLog(@"[E] peripheral didWriteValueForCharacteristic error: %@", error);
? ? ? ? [self cancelConnection];
? ? ? ? [self cleanupOperationUnexpectedly];
? ? ? ? return;
? ? }
? ? NSLog(@"寫入成功----%@",characteristic);
? ? if([characteristic.UUIDisEqual:self.ctrlptUUID]) {
? ? ? ? if (CTRLPTProgressWritting == self.ctrlptProgress) {
? ? ? ? ? ? if([selfperformOperationSegment:characteristic]) {
? ? ? ? ? ? ? ? self.ctrlptProgress = CTRLPTProgressWaitResp;
? ? ? ? ? ? ? ? self.backwardFlow.length=0;
? ? ? ? ? ? ? ? self.backwardOffset=0;
? ? ? ? ? ? }
? ? ? ? }
? ? }
}
4.8 如何解析藍(lán)牙數(shù)據(jù)
//判斷是否第一個(gè)包,若是,取出包長度
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if(0==self.backwardOffset&& length >=2) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? uint16_tcommandLength;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [characteristicDatagetBytes:&commandLengthlength:sizeof(commandLength)];
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? offset +=sizeof(commandLength);
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? self.backwardLength= commandLength;
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [self.backwardFlowappendData:[characteristicDatasubdataWithRange:NSMakeRange(offset, length - offset)]];
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }else{
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? [self.backwardFlowappendData:characteristicData];
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }