iOS藍牙開發(fā)(4.0)詳解

最近由于項目需要, 一直在研究藍牙4.0,在這兒分享給大家, 望共同進步.

一、關(guān)于藍牙開發(fā)的一些重要的理論概念:

1.當前ios中開發(fā)藍牙所運用的系統(tǒng)庫是<CoreBluetooth/CoreBluetooth.h>。

2.藍牙外設(shè)必須為4.0及以上(2.0需要MFI認證),否則無法開發(fā),藍牙4.0設(shè)備因為低耗電,所以也叫做BLE。

3.CoreBluetooth框架的核心其實是兩個東西,peripheral和central, 可以理解成外設(shè)和中心,就是你的蘋果手機就是中心,外部藍牙稱為外設(shè)。

4.服務和特征(service and characteristic):簡而言之,外部藍牙中它有若干個服務service(服務你可以理解為藍牙所擁有的能力),而每個服務service下?lián)碛腥舾蓚€特征characteristic(特征你可以理解為解釋這個服務的屬性)。

5.Descriptor(描述)用來描述characteristic變量的屬性。例如,一個descriptor可以規(guī)定一個可讀的描述,或者一個characteristic變量可接受的范圍,或者一個characteristic變量特定的單位。

6.我們使用的藍牙模塊是在淘寶買的, 大概十多元一個, ios大概每次可以接受90個字節(jié), 安卓大概每次可以接收20個字節(jié), 具體數(shù)字可能會浮動, 應該是與藍牙模塊有關(guān)。

二、藍牙連接的主要步驟

     1、創(chuàng)建一個CBCentralManager實例來進行藍牙管理;

     2、搜索掃描外圍設(shè)備;

     3、連接外圍設(shè)備;

     4、獲得外圍設(shè)備的服務;

     5、獲得服務的特征;

     6、從外圍設(shè)備讀取數(shù)據(jù);

     7、給外圍設(shè)備發(fā)送(寫入)數(shù)據(jù)。

三、代碼

// 加入權(quán)限訪問, 否則上傳AppStore會因為權(quán)限不足失敗


WechatIMG4.jpeg
1. 初始化
#import <CoreBluetooth/CoreBluetooth.h>
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
2. 搜索掃描外圍設(shè)備
/**
 *  --  初始化成功自動調(diào)用
 *  --  必須實現(xiàn)的代理,用來返回創(chuàng)建的centralManager的狀態(tài)。
 *  --  注意:必須確認當前是CBCentralManagerStatePoweredOn狀態(tài)才可以調(diào)用掃描外設(shè)的方法:
 scanForPeripheralsWithServices
 */
- (void)centralManagerDidUpdateState:(CBCentralManager *)central{
    switch (central.state) {
        case CBCentralManagerStateUnknown:
            NSLog(@">>>CBCentralManagerStateUnknown");
            break;
        case CBCentralManagerStateResetting:
            NSLog(@">>>CBCentralManagerStateResetting");
            break;
        case CBCentralManagerStateUnsupported:
            NSLog(@">>>CBCentralManagerStateUnsupported");
            break;
        case CBCentralManagerStateUnauthorized:
            NSLog(@">>>CBCentralManagerStateUnauthorized");
            break;
        case CBCentralManagerStatePoweredOff:
            NSLog(@">>>CBCentralManagerStatePoweredOff");
            break;
        case CBCentralManagerStatePoweredOn:
        {
            NSLog(@">>>CBCentralManagerStatePoweredOn");
            // 開始掃描周圍的外設(shè)。
            /*
             -- 兩個參數(shù)為Nil表示默認掃描所有可見藍牙設(shè)備。
             -- 注意:第一個參數(shù)是用來掃描有指定服務的外設(shè)。然后有些外設(shè)的服務是相同的,比如都有FFF5服務,那么都會發(fā)現(xiàn);而有些外設(shè)的服務是不可見的,就會掃描不到設(shè)備。
             -- 成功掃描到外設(shè)后調(diào)用didDiscoverPeripheral
             */
            [self.centralManager scanForPeripheralsWithServices:nil options:nil];
        }
            break;
        default:
            break;
    }
}
#pragma mark 發(fā)現(xiàn)外設(shè)
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI{
    NSLog(@"Find device:%@", [peripheral name]);
    if (![_deviceDic objectForKey:[peripheral name]]) {
        NSLog(@"Find device:%@", [peripheral name]);
        if (peripheral!=nil) {
            if ([peripheral name]!=nil) {
                if ([[peripheral name] hasPrefix:@"根據(jù)設(shè)備名過濾"]) {
                    [_deviceDic setObject:peripheral forKey:[peripheral name]];
                     // 停止掃描, 看需求決定要不要加
//                    [_centralManager stopScan];
                    // 將設(shè)備信息傳到外面的頁面(VC), 構(gòu)成掃描到的設(shè)備列表
                    if ([self.delegate respondsToSelector:@selector(dataWithBluetoothDic:)]) {
                        [self.delegate dataWithBluetoothDic:_deviceDic];
                    }
                }
            }
        }
    }
}
3.連接外圍設(shè)備
// 連接設(shè)備(.h中聲明出去的接口, 一般在點擊設(shè)備列表連接時調(diào)用)
- (void)connectDeviceWithPeripheral:(CBPeripheral *)peripheral
{
    [self.centralManager connectPeripheral:peripheral options:nil];
}
#pragma mark 連接外設(shè)--成功
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
    //連接成功后停止掃描,節(jié)省內(nèi)存
    [central stopScan];
    peripheral.delegate = self;
    self.peripheral = peripheral;
    //4.掃描外設(shè)的服務
    /**
     --     外設(shè)的服務、特征、描述等方法是CBPeripheralDelegate的內(nèi)容,所以要先設(shè)置代理peripheral.delegate = self
     --     參數(shù)表示你關(guān)心的服務的UUID,比如我關(guān)心的是"FFE0",參數(shù)就可以為@[[CBUUID UUIDWithString:@"FFE0"]].那么didDiscoverServices方法回調(diào)內(nèi)容就只有這兩個UUID的服務,不會有其他多余的內(nèi)容,提高效率。nil表示掃描所有服務
     --     成功發(fā)現(xiàn)服務,回調(diào)didDiscoverServices
     */
    [peripheral discoverServices:@[[CBUUID UUIDWithString:@"你要用的服務UUID"]]];
    if ([self.delegate respondsToSelector:@selector(didConnectBle)]) {
       // 已經(jīng)連接
        [self.delegate didConnectBle];
    }
}
#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. 獲得外圍設(shè)備的服務
#pragma mark 發(fā)現(xiàn)服務回調(diào)
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
    
    //NSLog(@"didDiscoverServices,Error:%@",error);
    CBService * __nullable findService = nil;
    // 遍歷服務
    for (CBService *service in peripheral.services)
    {
        //NSLog(@"UUID:%@",service.UUID);
        if ([[service UUID] isEqual:[CBUUID UUIDWithString:@"你要用的服務UUID"]])
        {
            findService = service;
        }
    }
    NSLog(@"Find Service:%@",findService);
    if (findService)
        [peripheral discoverCharacteristics:NULL forService:findService];
}
5、獲得服務的特征;
#pragma mark 發(fā)現(xiàn)特征回調(diào)
/**
 --  發(fā)現(xiàn)特征后,可以根據(jù)特征的properties進行:讀readValueForCharacteristic、寫writeValue、訂閱通知setNotifyValue、掃描特征的描述discoverDescriptorsForCharacteristic。
 **/
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{
    for (CBCharacteristic *characteristic in service.characteristics) {
        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"你要用的特征UUID"]]) {
            
            /**
             -- 讀取成功回調(diào)didUpdateValueForCharacteristic
             */
            self.characteristic = characteristic;
            // 接收一次(是讀一次信息還是數(shù)據(jù)經(jīng)常變實時接收視情況而定, 再決定使用哪個)
//            [peripheral readValueForCharacteristic:characteristic];
            // 訂閱, 實時接收
            [peripheral setNotifyValue:YES forCharacteristic:characteristic];
            
            // 發(fā)送下行指令(發(fā)送一條)
            NSData *data = [@"硬件工程師給我的指令, 發(fā)送給藍牙該指令, 藍牙會給我返回一條數(shù)據(jù)" dataUsingEncoding:NSUTF8StringEncoding];
            // 將指令寫入藍牙
                [self.peripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
        }

        /**
         -- 當發(fā)現(xiàn)characteristic有descriptor,回調(diào)didDiscoverDescriptorsForCharacteristic
         */
        [peripheral discoverDescriptorsForCharacteristic:characteristic];
    }
}
6.從外圍設(shè)備讀取數(shù)據(jù)
#pragma mark - 獲取值
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
    // characteristic.value就是藍牙給我們的值(我這里是json格式字符串)
    NSData *jsonData = [characteristic.value dataUsingEncoding:NSUTF8StringEncoding];
        NSDictionary *dataDic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:nil];
    // 將字典傳出去就可以使用了
}

#pragma mark - 中心讀取外設(shè)實時數(shù)據(jù)
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
    if (characteristic.isNotifying) {
        [peripheral readValueForCharacteristic:characteristic];
    } else { 
        NSLog(@"Notification stopped on %@.  Disconnecting", characteristic);
        NSLog(@"%@", characteristic);
        [self.centralManager cancelPeripheralConnection:peripheral];
    }
}
7. 給外圍設(shè)備發(fā)送(寫入)數(shù)據(jù)
// 上文中發(fā)現(xiàn)特征之后, 發(fā)送下行指令的時候其實就是向藍牙中寫入數(shù)據(jù)
// 例:
// 發(fā)送檢查藍牙命令
- (void)writeCheckBleWithBle
{
    _style = 1;
    // 發(fā)送下行指令(發(fā)送一條)
    NSData *data = [@"硬件工程師提供給你的指令, 類似于5E16010203...這種很長一串" dataUsingEncoding:NSUTF8StringEncoding];
    [self.peripheral writeValue:data forCharacteristic:self.characteristic type:CBCharacteristicWriteWithResponse];
}
#pragma mark 數(shù)據(jù)寫入成功回調(diào)
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
    NSLog(@"寫入成功");
    if ([self.delegate respondsToSelector:@selector(didWriteSucessWithStyle:)]) {
        [self.delegate didWriteSucessWithStyle:_style];
    }
}
8. 另外
- (void)scanDevice
{
    if (_centralManager == nil) {
    self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
    [_deviceDic removeAllObjects];
     }
}
#pragma mark 斷開連接
- (void)disConnectPeripheral{
    /**
     -- 斷開連接后回調(diào)didDisconnectPeripheral
     -- 注意斷開后如果要重新掃描這個外設(shè),需要重新調(diào)用[self.centralManager scanForPeripheralsWithServices:nil options:nil];
     */
    [self.centralManager cancelPeripheralConnection:self.peripheral];
}
#pragma mark 停止掃描外設(shè)
- (void)stopScanPeripheral{
    [self.centralManager stopScan];
}

由于硬件方面剛開始用藍牙2.0跟我對接, 導致程序一直搜索不到設(shè)備.希望小伙伴們注意一下這個問題. 如果有不清楚的地方歡迎留言提問.
覺得有幫助的小伙伴, 可以動動勤勞的小手點個喜歡或者關(guān)注哦~
你們的肯定對我很重要!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,882評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,208評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,746評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,666評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,477評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,960評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,047評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,200評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,726評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,617評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,807評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,327評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,049評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,425評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,674評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,432評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,769評論 2 372

推薦閱讀更多精彩內(nèi)容