

文章分類:APP行業(yè)動態(tài) 發(fā)布時間:2017-08-12 原文作者:Shi Yongfeng 閱讀( )
之前公司在做一個iOS藍牙項目,在開發(fā)的過程中簡單整理了一些與之相關的基礎知識,在這里廣州iOS APP開發(fā)公司奕環(huán)科技小編分享一下iOS藍牙APP開發(fā),藍牙中心設備的實現(xiàn)及藍牙外設的實現(xiàn)。整理包括以下內容:
1、iOS藍牙開發(fā)的關鍵詞
2、藍牙的簡單介紹
3、CoreBluetooth框架
4、實現(xiàn)iOS藍牙外設(Demo)
5、實現(xiàn)iOS藍牙中心設備(Demo)
點擊查看Demo的運行gif圖,中心設備可以從外設讀取數(shù)據(jù),也可以向外設寫入數(shù)據(jù)。外設也可以向中心設備發(fā)送數(shù)據(jù)。
PS:需要使用真機測試。
iOS的藍牙開發(fā)是圍繞著CoreBluetooth框架來實現(xiàn)的。
下面先從iOS藍牙開發(fā)的基本概念說起
中心設備:就是用來掃描周圍藍牙硬件的設備,比如通過你手機的藍牙來掃描并連接智能手環(huán),這時候你的手機就是中心設備。
外設:被掃描的設備。比如當你用手機的藍牙掃描連接智能手環(huán)的時候,智能手環(huán)就是外設。

廣播:就是外設不停的散播藍牙信號,讓中心設備可以掃描到。

服務(services):外設廣播和運行的時候會有服務,可以理解成一個功能模塊,中心設備可以讀取服務。外設可以有多個服務。
特征(characteristic):在服務中的一個單位,一個服務可以有多個特征,特征會有一個value,一般讀寫的數(shù)據(jù)就是這個value。

UUID:區(qū)分不同的服務和特征,可以理解為服務和特征的身份證。我們可以用UUID來挑選需要的服務和特征。
下面進入iOS藍牙開發(fā)的主要部分,中心設備的實現(xiàn),這也是手機App通常擔任的角色。
實現(xiàn)iOS藍牙中心設備(Demo)

1、同外設開發(fā)一樣,首先要導入CoreBluetooth框架。
#import <CoreBluetooth/CoreBluetooth.h>
2、遵守的協(xié)議與外設開發(fā)不同,中心設備的開發(fā)需要遵循如下兩個協(xié)議。
@interface ViewController () <CBCentralManagerDelegate,CBPeripheralDelegate>
3、創(chuàng)建中心管理器并用屬性強引用,創(chuàng)建的時候也會設置代理和選擇線程。
@property (nonatomic, strong) CBCentralManager *centralManager;
// 創(chuàng)建中心設備管理器,會回調centralManagerDidUpdateState
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue()];
4、當創(chuàng)建中心管理對象的時候,會回調如下方法用來判斷中心設備的藍牙狀態(tài)。當藍牙狀態(tài)沒問題的時候,可以根據(jù)外設服務的UUID來掃描需要的外設。所以自然而然的就想到了要定義與外設UUID相同的宏。
/** 判斷手機藍牙狀態(tài) */
#define SERVICE_UUID @"CDD1"
#define CHARACTERISTIC_UUID @"CDD2"
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
// 藍牙可用,開始掃描外設
if (central.state == CBManagerStatePoweredOn) {
NSLog(@"藍牙可用");
// 根據(jù)SERVICE_UUID來掃描外設,如果不設置SERVICE_UUID,則掃描所有藍牙設備
[central scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:SERVICE_UUID]] options:nil];
}
if(central.state==CBCentralManagerStateUnsupported) {
NSLog(@"該設備不支持藍牙");
}
if (central.state==CBCentralManagerStatePoweredOff) {
NSLog(@"藍牙已關閉");
}
}
5、當掃描到外設之后,就會回調下面這個方法,可以在這個方法中繼續(xù)設置篩選條件,例如根據(jù)外設名字的前綴來選擇,如果符合條件就進行連接。
/** 發(fā)現(xiàn)符合要求的外設,回調 */
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI {
// 對外設對象進行強引用
self.peripheral = peripheral;
// if ([peripheral.name hasPrefix:@"WH"]) {
// // 可以根據(jù)外設名字來過濾外設
// [central connectPeripheral:peripheral options:nil];
// }
// 連接外設
[central connectPeripheral:peripheral options:nil];
}
7、當連接成功的時候,就會來到下面這個方法。為了省電,當連接上外設之后,就讓中心設備停止掃描,并且別忘記設置連接上的外設的代理。在這個方法里根據(jù)UUID進行服務的查找。
/** 連接成功 */
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
// 可以停止掃描
[self.centralManager stopScan];
// 設置代理
peripheral.delegate = self;
// 根據(jù)UUID來尋找服務
[peripheral discoverServices:@[[CBUUID UUIDWithString:SERVICE_UUID]]];
NSLog(@"連接成功");
}
8、連接失敗和斷開連接也有各自的回調方法。在斷開連接的時候,我們可以設置自動重連,根據(jù)項目需求來自定義里面的代碼。
/** 連接失敗的回調 */
-(void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
NSLog(@"連接失敗");
}
/** 斷開連接 */
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error {
NSLog(@"斷開連接");
// 斷開連接可以設置重新連接
[central connectPeripheral:peripheral options:nil];
}
9、下面開始處理代理方法。
最開始就是發(fā)現(xiàn)服務的方法。這個方法里可以遍歷服務,找到需要的服務。由于上面做的外設只有一個服務,所以我這里直接取服務中的最后一個lastObject就行了。
找到服務之后,連貫的動作繼續(xù)根據(jù)特征的UUID尋找服務中的特征。
/** 發(fā)現(xiàn)服務 */
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
// 遍歷出外設中所有的服務
for (CBService *service in peripheral.services) {
NSLog(@"所有的服務:%@",service);
}
// 這里僅有一個服務,所以直接獲取
CBService *service = peripheral.services.lastObject;
// 根據(jù)UUID尋找服務中的特征
[peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:CHARACTERISTIC_UUID]] forService:service];
}
10、下面這個方法里做的事情不少。
當發(fā)現(xiàn)特征之后,與服務一樣可以遍歷特征,根據(jù)外設開發(fā)人員給的文檔找出不同特征,做出相應的操作。
我的外設只設置了一個特征,所以也是直接通過lastObject拿到特征。
再重復一遍,一般開發(fā)中可以設置兩個特征,一個用來發(fā)送數(shù)據(jù),一個用來接收中心設備寫過來的數(shù)據(jù)。
這里用一個屬性引用特征,是為了后面通過這個特征向外設寫入數(shù)據(jù)或發(fā)送指令。
readValueForCharacteristic方法是直接讀一次這個特征上的數(shù)據(jù)。
/** 發(fā)現(xiàn)特征回調 */
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
// 遍歷出所需要的特征
for (CBCharacteristic *characteristic in service.characteristics) {
NSLog(@"所有特征:%@", characteristic);
// 從外設開發(fā)人員那里拿到不同特征的UUID,不同特征做不同事情,比如有讀取數(shù)據(jù)的特征,也有寫入數(shù)據(jù)的特征
}
// 這里只獲取一個特征,寫入數(shù)據(jù)的時候需要用到這個特征
self.characteristic = service.characteristics.lastObject;
// 直接讀取這個特征數(shù)據(jù),會調用didUpdateValueForCharacteristic
[peripheral readValueForCharacteristic:self.characteristic];
// 訂閱通知
[peripheral setNotifyValue:YES forCharacteristic:self.characteristic];
}
setNotifyValue:(BOOL)enabled forCharacteristic:(CBCharacteristic *)characteristic方法是對這個特征進行訂閱,訂閱成功之后,就可以監(jiān)控外設中這個特征值得變化了。
11、當訂閱的狀態(tài)發(fā)生改變的時候,下面的方法就派上用場了。
/** 訂閱狀態(tài)的改變 */
-(void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
if (error) {
NSLog(@"訂閱失敗");
NSLog(@"%@",error);
}
if (characteristic.isNotifying) {
NSLog(@"訂閱成功");
} else {
NSLog(@"取消訂閱");
}
}
12、外設可以發(fā)送數(shù)據(jù)給中心設備,中心設備也可以從外設讀取數(shù)據(jù),當發(fā)生這些事情的時候,就會回調這個方法。通過特種中的value屬性拿到原始數(shù)據(jù),然后根據(jù)需求解析數(shù)據(jù)。
/** 接收到數(shù)據(jù)回調 */
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
// 拿到外設發(fā)送過來的數(shù)據(jù)
NSData *data = characteristic.value;
self.textField.text = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
13、中心設備可以向外設寫入數(shù)據(jù),也可以向外設發(fā)送請求或指令,當需要進行這些操作的時候該怎么辦呢。
首先把要寫入的數(shù)據(jù)轉化為NSData格式,然后根據(jù)上面拿到的寫入數(shù)據(jù)的特征,運用方法writeValue:(NSData )data forCharacteristic:(CBCharacteristic )characteristic type:(CBCharacteristicWriteType)type來進行數(shù)據(jù)的寫入。
當寫入數(shù)據(jù)的時候,系統(tǒng)也會回調這個方法peripheral:(CBPeripheral )peripheral didWriteValueForCharacteristic:(nonnull CBCharacteristic )characteristic error:(nullable NSError *)error 。
/** 寫入數(shù)據(jù) */
- (IBAction)didClickPost:(id)sender {
// 用NSData類型來寫入
NSData *data = [self.textField.text dataUsingEncoding:NSUTF8StringEncoding];
// 根據(jù)上面的特征self.characteristic來寫入數(shù)據(jù)
[self.peripheral writeValue:data forCharacteristic:self.characteristic type:CBCharacteristicWriteWithResponse];
}
/** 寫入數(shù)據(jù)回調 */
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(nonnull CBCharacteristic *)characteristic error:(nullable NSError *)error {
NSLog(@"寫入成功");
}
14、中心設備如何主動從外設讀取數(shù)據(jù)呢。
用正在連接的外設對象來調用readValueForCharacteristic方法,并且把將要讀取數(shù)據(jù)的特征作為參數(shù),這樣就可以主動拿一次數(shù)據(jù)了。
去到第12步的回調方法中,在特征的value屬性中拿到這次的數(shù)據(jù)。
/** 讀取數(shù)據(jù) */
- (IBAction)didClickGet:(id)sender {
[self.peripheral readValueForCharacteristic:self.characteristic];
}
? 廣州奕環(huán)信息科技有限公司所有??粵ICP備16005555號-1?????
友情鏈接: 北京軟件開發(fā)公司 廣州營銷公司 鞋業(yè)ERP軟件 移動OA 小程序開發(fā) ehr系統(tǒng)