在寫這個部落格之前,空餘時間抽看了近一個月的文件和Demo,系統給的解釋很詳細,介面也比較實用,唯獨有一點,對於裝置的唯一標示,網上眾說紛紜,在這裡我目前也還沒有自己的見解,只是在不斷的測試各種情況,親測同一裝置的UUID對於每臺iPhone裝置都不一樣,只能儘量保證裝置的唯一性,特別是自動重連的過程,讓使用者沒有感知。我之前也找了很久,發現CBCentralManager和CBPeripheral裡邊都找不到和Mac地址有關的東西,後來發現一般是外設在Device Information服務中的某個特徵返回的。經過與硬體工程師的協商,決定APP端將從這個服務中獲取到藍芽裝置以及我的iPhone手機的藍芽Mac地址,為自動連線的唯一性做準備。
這裡經過和硬體工程師的測試,發現裝置端在獲取手機藍芽MAC地址的時候,當使用者手機重啟之後,這個地址也是會隨機變化的,也就是說,作為開發者,只有裝置的MAC地址能夠保持唯一性不變化。
有疑問的朋友可以先去這裡瞅一瞅
一個關於藍芽4.0的智慧硬體Demo詳解
- 下面是兩臺iPhone6連線同一臺藍芽裝置的結果:
1 2 3 4 |
**成功連線**** peripheral: with UUID: 50084F69-BA5A-34AC-8A6E-6F0CEADB21CD** **** **** **成功連線**** peripheral: with UUID: 55B7D759-0F1E-6271-EA14-BC5A9C9EEEEC** |
進入正題
iOS的藍芽開發很簡單,只要包含一個庫,建立CBCentralManager例項,實現代理方法,然後就可以直接和裝置進行通訊。
1 |
#import <CoreBluetooth/CoreBluetooth.h> |
首先可以定義一些即將使用到的UUID的巨集
1 2 3 |
#define kPeripheralName @"360qws Electric Bike Service" //外圍裝置名稱 #define kServiceUUID @"7CACEB8B-DFC4-4A40-A942-AAD653D174DC" //服務的UUID #define kCharacteristicUUID @"282A67B2-8DAB-4577-A42F-C4871A3EEC4F" //特徵的UUID |
如果不是把手機作為中心裝置的話,這些沒有必要設定。
這裡我也沒有用到,僅僅是提了一下,具體操作後續新增。
對於生成UUID,大家可以谷歌一下,直接通過mac終端生成32位UUID。
1.宣告屬性
1 2 3 4 5 6 7 8 9 10 11 12 13 |
@property (weak, nonatomic) IBOutlet UITableView *bluetoothTable; @property (weak, nonatomic) IBOutlet UITextView *resultTextView; @property BOOL cbReady; @property(nonatomic) float batteryValue; @property (nonatomic, strong) CBCentralManager *manager; @property (nonatomic, strong) CBPeripheral *peripheral; @property (strong ,nonatomic) CBCharacteristic *writeCharacteristic; @property (strong,nonatomic) NSMutableArray *nDevices; @property (strong,nonatomic) NSMutableArray *nServices; @property (strong,nonatomic) NSMutableArray *nCharacteristics; |
2.遵守協議(這裡我用到了table)
1 |
@interface ViewController () <CBCentralManagerDelegate, CBPeripheralDelegate, UITableViewDataSource, UITableViewDelegate> |
3.初始化資料
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. self.manager = [[CBCentralManager alloc] initWithDelegate:self queue:nil]; _cbReady = false; _nDevices = [[NSMutableArray alloc]init]; _nServices = [[NSMutableArray alloc]init]; _nCharacteristics = [[NSMutableArray alloc]init]; _bluetoothTable.delegate = self; _bluetoothTable.dataSource = self; count = 0; } |
4.實現藍芽的協議方法
- (1)檢測藍芽狀態
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
//開始檢視服務,藍芽開啟 -(void)centralManagerDidUpdateState:(CBCentralManager *)central { switch (central.state) { case CBCentralManagerStatePoweredOn: { [self updateLog:@"藍芽已開啟,請掃描外設"]; [_activity startAnimating]; [_manager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:@"FF15"]] options:@{CBCentralManagerScanOptionAllowDuplicatesKey : @YES }]; } break; case CBCentralManagerStatePoweredOff: [self updateLog:@"藍芽沒有開啟,請先開啟藍芽"]; break; default: break; } } |
注:[_manager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:@"FF15"]] options:@{CBCentralManagerScanOptionAllowDuplicatesKey : @YES }];
中間的@[[CBUUID UUIDWithString:@"FF15"]]
是為了過濾掉其他裝置,可以搜尋特定標示的裝置。
- (2)檢測到外設後,停止掃描,連線裝置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
//查到外設後,停止掃描,連線裝置 -(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI { [self updateLog:[NSString stringWithFormat:@"已發現 peripheral: %@ rssi: %<a href='http://www.jobbole.com/members/Famous_god'>@,</a> UUID: %@ advertisementData: %@ ", peripheral, RSSI, peripheral.identifier, advertisementData]]; _peripheral = peripheral; [_manager connectPeripheral:_peripheral options:nil]; [self.manager stopScan]; [_activity stopAnimating]; BOOL replace = NO; // Match if we have this device from before for (int i=0; i < _nDevices.count; i++) { CBPeripheral *p = [_nDevices objectAtIndex:i]; if ([p isEqual:peripheral]) { [_nDevices replaceObjectAtIndex:i withObject:peripheral]; replace = YES; } } if (!replace) { [_nDevices addObject:peripheral]; [_bluetoothTable reloadData]; } } |
- (3)連線外設後的處理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
//連線外設成功,開始發現服務 - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral { NSLog(@"%@", [NSString stringWithFormat:@"成功連線 peripheral: %@ with UUID: %@",peripheral,peripheral.identifier]); [self updateLog:[NSString stringWithFormat:@"成功連線 peripheral: %@ with UUID: %@",peripheral,peripheral.identifier]]; [self.peripheral setDelegate:self]; [self.peripheral discoverServices:nil]; [self updateLog:@"掃描服務"]; } //連線外設失敗 -(void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error { NSLog(@"%@",error); } -(void)peripheralDidUpdateRSSI:(CBPeripheral *)peripheral error:(NSError *)error { NSLog(@"%s,%@",__PRETTY_FUNCTION__,peripheral); int rssi = abs([peripheral.RSSI intValue]); CGFloat ci = (rssi - 49) / (10 * 4.); NSString *length = [NSString stringWithFormat:@"發現BLT4.0熱點:%@,距離:%.1fm",_peripheral,pow(10,ci)]; [self updateLog:[NSString stringWithFormat:@"距離:%@", length]]; } |
- (4)發現服務和搜尋到的Characteristice
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
//已發現服務 -(void) peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{ [self updateLog:@"發現服務."]; int i=0; for (CBService *s in peripheral.services) { [self.nServices addObject:s]; } for (CBService *s in peripheral.services) { [self updateLog:[NSString stringWithFormat:@"%d :服務 UUID: %@(%@)",i,s.UUID.data,s.UUID]]; i++; [peripheral discoverCharacteristics:nil forService:s]; if ([s.UUID isEqual:[CBUUID UUIDWithString:@"FF15"]]) { BOOL replace = NO; // Match if we have this device from before for (int i=0; i < _nDevices.count; i++) { CBPeripheral *p = [_nDevices objectAtIndex:i]; if ([p isEqual:peripheral]) { [_nDevices replaceObjectAtIndex:i withObject:peripheral]; replace = YES; } } if (!replace) { [_nDevices addObject:peripheral]; [_bluetoothTable reloadData]; } } } } //已搜尋到Characteristics -(void) peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{ [self updateLog:[NSString stringWithFormat:@"發現特徵的服務:%@ (%@)",service.UUID.data ,service.UUID]]; for (CBCharacteristic *c in service.characteristics) { [self updateLog:[NSString stringWithFormat:@"特徵 UUID: %@ (%@)",c.UUID.data,c.UUID]]; if ([c.UUID isEqual:[CBUUID UUIDWithString:@"FF01"]]) { _writeCharacteristic = c; } if ([c.UUID isEqual:[CBUUID UUIDWithString:@"FF02"]]) { [_peripheral readValueForCharacteristic:c]; [_peripheral setNotifyValue:YES forCharacteristic:c]; } if ([c.UUID isEqual:[CBUUID UUIDWithString:@"FF04"]]) { [_peripheral readValueForCharacteristic:c]; } if ([c.UUID isEqual:[CBUUID UUIDWithString:@"FF05"]]) { [_peripheral readValueForCharacteristic:c]; [_peripheral setNotifyValue:YES forCharacteristic:c]; } if ([c.UUID isEqual:[CBUUID UUIDWithString:@"FFA1"]]) { [_peripheral readRSSI]; } [_nCharacteristics addObject:c]; } } - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error { [self updateLog:[NSString stringWithFormat:@"已斷開與裝置:[%@]的連線", peripheral.name]]; } |
- (5)獲取外設發來的資料
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
//獲取外設發來的資料,不論是read和notify,獲取資料都是從這個方法中讀取。 - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"FF02"]]) { NSData * data = characteristic.value; Byte * resultByte = (Byte *)[data bytes]; for(int i=0;i<[data length];i++) printf("testByteFF02[%d] = %d\n",i,resultByte[i]); if (resultByte[1] == 0) { switch (resultByte[0]) { case 3: // 加解鎖 { if (resultByte[2] == 0) { [self updateLog:@"撤防成功!!!"]; }else if (resultByte[2] == 1) { [self updateLog:@"設防成功!!!"]; } } break; case 4: // 開坐桶 { if (resultByte[2] == 0) { [self updateLog:@"關坐桶成功!!!"]; }else if (resultByte[2] == 1) { [self updateLog:@"開坐桶成功!!!"]; } } break; case 5: // 鎖定電機 { if (resultByte[2] == 0) { [self updateLog:@"解鎖電機控制器成功!!!"]; }else if (resultByte[2] == 1) { [self updateLog:@"鎖定電機控制器成功!!!"]; } } break; default: break; } }else if (resultByte[1] == 1) { [self updateLog:@"未知錯誤"]; }else if (resultByte[1] == 2) { [self updateLog:@"鑑權失敗"]; } } if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"FF04"]]) { NSData * data = characteristic.value; Byte * resultByte = (Byte *)[data bytes]; for(int i=0;i<[data length];i++) printf("testByteFF04[%d] = %d\n",i,resultByte[i]); if (resultByte[0] == 0) { // 未繫結 -》寫鑑權碼 [self updateLog:@"當前車輛未繫結,請鑑權"]; [self authentication]; // 鑑權 [self writePassword:nil newPw:nil]; }else if (resultByte[0] == 1) { // 已繫結 -》鑑權 [self updateLog:@"當前車輛已經繫結,請鑑權"]; [self writePassword:nil newPw:nil]; }else if (resultByte[0] == 2) { // 允許繫結 [self updateLog:@"當前車輛允許繫結"]; } } if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"FF05"]]) { NSData * data = characteristic.value; Byte * resultByte = (Byte *)[data bytes]; for(int i=0;i<[data length];i++) printf("testByteFF05[%d] = %d\n",i,resultByte[i]); if (resultByte[0] == 0) { // 裝置加解鎖狀態 0 撤防 1 設防 [self updateLog:@"當前車輛撤防狀態"]; }else if (resultByte[0] == 1) { // 裝置加解鎖狀態 0 撤防 1 設防 [self updateLog:@"當前車輛設防狀態"]; } } } //中心讀取外設實時資料 - (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { if (error) { NSLog(@"Error changing notification state: %@", error.localizedDescription); } // Notification has started if (characteristic.isNotifying) { [peripheral readValueForCharacteristic:characteristic]; } else { // Notification has stopped // so disconnect from the peripheral NSLog(@"Notification stopped on %<a href='http://www.jobbole.com/members/WANGXIAOHUI1879'>@.</a> Disconnecting", characteristic); [self updateLog:[NSString stringWithFormat:@"Notification stopped on %<a href='http://www.jobbole.com/members/WANGXIAOHUI1879'>@.</a> Disconnecting", characteristic]]; [self.manager cancelPeripheralConnection:self.peripheral]; } } //用於檢測中心向外設寫資料是否成功 -(void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { if (error) { NSLog(@"=======%@",error.userInfo); [self updateLog:[error.userInfo JSONString]]; }else{ NSLog(@"傳送資料成功"); [self updateLog:@"傳送資料成功"]; } /* When a write occurs, need to set off a re-read of the local CBCharacteristic to update its value */ [peripheral readValueForCharacteristic:characteristic]; } |
- (6)其他輔助性的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
#pragma mark - 藍芽的相關操作 - (IBAction)bluetoothAction:(UIButton *)sender { switch (sender.tag) { case 201: { // 搜尋裝置 [self updateLog:@"正在掃描外設..."]; [_activity startAnimating]; [_manager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:@"FF15"]] options:@{CBCentralManagerScanOptionAllowDuplicatesKey : @YES }]; double delayInSeconds = 30.0; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ [self.manager stopScan]; [_activity stopAnimating]; [self updateLog:@"掃描超時,停止掃描"]; }); } break; case 202: { // 連線 if (_peripheral && _cbReady) { [_manager connectPeripheral:_peripheral options:nil]; _cbReady = NO; } } break; case 203: { // 斷開 if (_peripheral && !_cbReady) { [_manager cancelPeripheralConnection:_peripheral]; _cbReady = YES; } } break; case 204: { // 暫停搜尋 [self.manager stopScan]; } break; case 211: { // 車輛上鎖 [self lock]; } break; case 212: { // 車輛解鎖 [self unLock]; } break; case 213: { // 開啟坐桶 [self open]; } break; case 214: { // 立即尋車 [self find]; } break; default: break; } } -(NSOperationQueue *)queue { if (!_queue) { // 請求佇列 self.queue = [[NSOperationQueue alloc]init]; [self.queue setMaxConcurrentOperationCount:1]; } return _queue; } #pragma mark - 命令 #pragma mark - 鑑權 -(void)authentication { Byte byte[] = {1, 1, 2, 3, 4, 5, 6, 7, 8}; if (_peripheral.state == CBPeripheralStateConnected) { [_peripheral writeValue:[NSData dataWithBytes:byte length:9] forCharacteristic:_writeCharacteristic type:CBCharacteristicWriteWithoutResponse]; } } #pragma mark - 寫密碼 -(void)writePassword:(NSString *)initialPw newPw:(NSString *)newPw { Byte byte[] = {2, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8}; if (_peripheral.state == CBPeripheralStateConnected) { [_peripheral writeValue:[NSData dataWithBytes:byte length:17] forCharacteristic:_writeCharacteristic type:CBCharacteristicWriteWithoutResponse]; } } #pragma mark - 車輛上鎖 -(void)lock { Byte byte[] = {3, 1}; if (_peripheral.state == CBPeripheralStateConnected) { [_peripheral writeValue:[NSData dataWithBytes:byte length:2] forCharacteristic:_writeCharacteristic type:CBCharacteristicWriteWithoutResponse]; } } #pragma mark - 車輛解鎖 -(void)unLock { Byte byte[] = {3, 0}; if (_peripheral.state == CBPeripheralStateConnected) { [_peripheral writeValue:[NSData dataWithBytes:byte length:2] forCharacteristic:_writeCharacteristic type:CBCharacteristicWriteWithoutResponse]; } } #pragma mark - 開啟坐桶 -(void)open { Byte byte[] = {4, 1}; if (_peripheral.state == CBPeripheralStateConnected) { [_peripheral writeValue:[NSData dataWithBytes:byte length:2] forCharacteristic:_writeCharacteristic type:CBCharacteristicWriteWithoutResponse]; } } #pragma mark - 立即尋車 -(void)find { Byte byte[] = {7}; if (_peripheral.state == CBPeripheralStateConnected) { [_peripheral writeValue:[NSData dataWithBytes:byte length:1] forCharacteristic:_writeCharacteristic type:CBCharacteristicWriteWithoutResponse]; } } -(NSData *)hexString:(NSString *)hexString { int j=0; Byte bytes[20]; ///3ds key的Byte 陣列, 128位 for(int i=0; i<[hexString length]; i++) { int int_ch; /// 兩位16進位制數轉化後的10進位制數 unichar hex_char1 = [hexString characterAtIndex:i]; ////兩位16進位制數中的第一位(高位*16) int int_ch1; if(hex_char1 >= '0' && hex_char1 <='9') int_ch1 = (hex_char1-48)*16; //// 0 的Ascll - 48 else if(hex_char1 >= 'A' && hex_char1 <='F') int_ch1 = (hex_char1-55)*16; //// A 的Ascll - 65 else int_ch1 = (hex_char1-87)*16; //// a 的Ascll - 97 i++; unichar hex_char2 = [hexString characterAtIndex:i]; ///兩位16進位制數中的第二位(低位) int int_ch2; if(hex_char2 >= '0' && hex_char2 <='9') int_ch2 = (hex_char2-48); //// 0 的Ascll - 48 else if(hex_char1 >= 'A' && hex_char1 <='F') int_ch2 = hex_char2-55; //// A 的Ascll - 65 else int_ch2 = hex_char2-87; //// a 的Ascll - 97 int_ch = int_ch1+int_ch2; NSLog(@"int_ch=%d",int_ch); bytes[j] = int_ch; ///將轉化後的數放入Byte陣列裡 j++; } NSData *newData = [[NSData alloc] initWithBytes:bytes length:20]; return newData; } |
在和硬體之間的資料傳送和接受,用的都是byte陣列。最後,新增一個儲存已連線過得裝置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
- (void) addSavedDevice:(CFUUIDRef)uuid { NSArray *storedDevices = [[NSUserDefaults standardUserDefaults] arrayForKey:@"StoredDevices"]; NSMutableArray *newDevices = nil; CFStringRef uuidString = NULL; if (![storedDevices isKindOfClass:[NSArray class]]) { NSLog(@"Can't find/create an array to store the uuid"); return; } newDevices = [NSMutableArray arrayWithArray:storedDevices]; uuidString = CFUUIDCreateString(NULL, uuid); if (uuidString) { [newDevices addObject:(__bridge NSString*)uuidString]; CFRelease(uuidString); } /* Store */ [[NSUserDefaults standardUserDefaults] setObject:newDevices forKey:@"StoredDevices"]; [[NSUserDefaults standardUserDefaults] synchronize]; } - (void) loadSavedDevices { NSArray *storedDevices = [[NSUserDefaults standardUserDefaults] arrayForKey:@"StoredDevices"]; if (![storedDevices isKindOfClass:[NSArray class]]) { NSLog(@"No stored array to load"); return; } for (id deviceUUIDString in storedDevices) { if (![deviceUUIDString isKindOfClass:[NSString class]]) continue; CFUUIDRef uuid = CFUUIDCreateFromString(NULL, (CFStringRef)deviceUUIDString); if (!uuid) continue; [CBCentralManager retrievePeripherals:[NSArray arrayWithObject:(__bridge id)uuid]]; CFRelease(uuid); } } //And the delegate function for the Retrieve Peripheral - (void) centralManager:(CBCentralManager *)central didRetrieveConnectedPeripherals:(NSArray *)peripherals { CBPeripheral *peripheral; /* Add to list. */ for (peripheral in peripherals) { [central connectPeripheral:peripheral options:nil]; } } - (void) centralManager:(CBCentralManager *)central didRetrievePeripheral:(CBPeripheral *)peripheral { [central connectPeripheral:peripheral options:nil]; } |
後記
- 最主要是用UUID來確定你要乾的事情,特徵和服務的UUID都是外設定義好的。我們只需要讀取,確定你要讀取什麼的時候,就去判斷UUID是否相符。 一般來說我們使用的iPhone都是做centralManager的,藍芽模組是peripheral的,所以我們是want datas,需要接受資料。
1.判斷狀態為powerOn,然後執行掃描
2.停止掃描,連線外設
3.連線成功,尋找服務
4.在服務裡尋找特徵
5.為特徵新增通知
5.通知新增成功,那麼就可以實時的讀取value[也就是說只要外設傳送資料[一般外設的頻率為10Hz],代理就會呼叫此方法]。
6.處理接收到的value,[hex值,得轉換] 之後就自由發揮了,在這期間都是通過代理來實現的,也就是說你只需要處理你想要做的事情,代理會幫你呼叫方法。[別忘了新增代理]
2015-07-28 更
關於write我這裡還有些注意的地方要強調!!!!
並不是每一個Characteristic都可以通過回撥函式來檢視它寫入狀態的。就比如針對 immediateAlertService(1802) 的 alertLevelCharacteristic(2A06),就是一個不能有response的Characteristic。剛開始我就一直用CBCharacteristicWriteType.WithResponse來進行寫入始終不成功,鬱悶壞了,最後看到每個Characteristic還有個屬性值是指示這個的,我將每個Characteristic列印出來有如下資訊:
1 2 |
immediateAlertService Discover characteristic linkLossAlertService Discover characteristic |
這個的properties是什麼剛開始不知道,覺得他沒意義,後面才注意到properties是Characteristic的一個引數,具體解釋如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
Declaration SWIFT struct CBCharacteristicProperties : RawOptionSetType { init(_ value: UInt) var value: UInt static var Broadcast: CBCharacteristicProperties { get } static var Read: CBCharacteristicProperties { get } static var WriteWithoutResponse: CBCharacteristicProperties { get } static var Write: CBCharacteristicProperties { get } static var Notify: CBCharacteristicProperties { get } static var Indicate: CBCharacteristicProperties { get } static var AuthenticatedSignedWrites: CBCharacteristicProperties { get } static var ExtendedProperties: CBCharacteristicProperties { get } static var NotifyEncryptionRequired: CBCharacteristicProperties { get } static var IndicateEncryptionRequired: CBCharacteristicProperties { get } } OBJECTIVE-C typedef enum { CBCharacteristicPropertyBroadcast = 0x01, CBCharacteristicPropertyRead = 0x02, CBCharacteristicPropertyWriteWithoutResponse = 0x04, CBCharacteristicPropertyWrite = 0x08, CBCharacteristicPropertyNotify = 0x10, CBCharacteristicPropertyIndicate = 0x20, CBCharacteristicPropertyAuthenticatedSignedWrites = 0x40, CBCharacteristicPropertyExtendedProperties = 0x80, CBCharacteristicPropertyNotifyEncryptionRequired = 0x100, CBCharacteristicPropertyIndicateEncryptionRequired = 0x200, } CBCharacteristicProperties; |
可以看到0x04
對應的是CBCharacteristicPropertyWriteWithoutResponse
0x0A
對應的是CBCharacteristicPropertyNotify
所以 immediateAlertService(1802) 的 alertLevelCharacteristic(2A06)是不能用CBCharacteristicWriteType.WithRespons進行寫入,只能用CBCharacteristicWriteType.WithOutRespons。這樣在以後的開發中可以對每個Characteristic的這個引數進行檢查再進行設定。
最後講一下關於藍芽繫結的過程,在iOS中,沒有講當繫結的過程,直接就是掃描、連線、互動。從而很多人會認為,連線就是繫結了,其實不然。在iOS開發中,連線並沒有完成繫結,在網上找到了個很好的解釋:
you cannot initiate pairing from the iOS central side. Instead, you have to read/write a characteristic value,
and then let your peripheral respond with an “Insufficient Authentication” error.
iOS will then initiate pairing, will store the keys for later use (bonding) and encrypts the link. As far as I know,
it also caches discovery information, so that future connections can be set up faster.
就是當發生讀寫互動時,系統在會和外設進行繫結操作!!!
2016-02-20 更
如題,手機作為主裝置,在使用CoreBluetooth時候,想獲取藍芽的資料廣播包。在使用
1 |
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)aPeripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI |
方法時候獲取的advertisementData列印出來只有
1 |
** ****kCBAdvDataIsConnectable****kCBAdvDataLocalName****kCBAdvDataManufacturerData** |
三個屬性對應的值,但這並非廣播包的資料。例如安卓可以通過scandata來獲取到廣播包的值,那麼iOS這邊我應該怎麼做呢?
好像蘋果這邊禁止讀取這種廣播內容的的,真要的話你可以讓硬體那邊把資料做到kCBAdvDataManufacturerData這個欄位裡面。