iOS藍(lán)牙開發(fā)

iOS的藍(lán)牙數(shù)據(jù)接收以及發(fā)送

  1. 名詞:Central(中心設(shè)備)、Peripheral(外圍設(shè)備)、advertising(廣告)、Services(服務(wù))、Characteristic(特征)
  2. 新建Central Manager實(shí)例進(jìn)行藍(lán)牙管理
  3. 搜索外圍設(shè)備
  4. 連接外圍設(shè)備
  5. 獲得外圍設(shè)備的服務(wù)
  6. 獲得服務(wù)的特征
  7. 給外圍設(shè)備發(fā)送數(shù)據(jù)
  8. 從外圍設(shè)備讀數(shù)據(jù)

藍(lán)牙介紹

本文要介紹的CoreBluetooth,專門用于與BLE設(shè)備通訊。并且現(xiàn)在很多藍(lán)牙設(shè)備都支持4.0,4.0以其低功耗著稱,所以一般也叫BLE(Bluetoothlow energy),所以也是在iOS比較推薦的一種開發(fā)方法。

CoreBluetooth介紹

在CoreBluetooth中有兩個(gè)主要的部分,Central和Peripheral,CBPeripheralManager 作為周邊設(shè)備。CBCentralManager作為中心設(shè)備。所有可用的iOS設(shè)備可以作為周邊(Peripheral)也可以作為中央(Central),但不可以同時(shí)既是周邊也是中央。

  1. 周邊設(shè)備(Peripheral)設(shè)備是廣播設(shè)備的數(shù)據(jù),中央設(shè)備(Central)是管理并且使用這些數(shù)據(jù)的設(shè)備。
  2. 也就是說周邊(Peripheral)向周圍發(fā)送廣播,告訴周圍的中央設(shè)備(Central)它(周邊(Peripheral)這里有數(shù)據(jù),并且說明了能提供的服務(wù)和特征值(連接之后才能獲取),
  3. 其實(shí)藍(lán)牙傳值相當(dāng)于網(wǎng)絡(luò)接口,硬件的service的UUID加上characteristic的UUID,
    打一個(gè)比喻:service的UUID相當(dāng)于主地址,characteristic的UUID相當(dāng)于短鏈接,短鏈接必須是主地址的分支,拼在一起的是接口,你和硬件設(shè)定的藍(lán)牙傳輸格式類似于json,雙方可識(shí)別的數(shù)據(jù),因?yàn)樗{(lán)牙只能支持16進(jìn)制,而且每次傳輸只能20個(gè)字節(jié),所以要把信息流轉(zhuǎn)成雙方可識(shí)別的16進(jìn)制

實(shí)現(xiàn)方法介紹

  • .h 導(dǎo)入頭文件
#import <CoreBluetooth/CoreBluetooth.h>
  • 自定義設(shè)置枚舉狀態(tài)

typedef NS_ENUM(NSInteger, BluetoothState){
    BluetoothStateDisconnect = 0,
    BluetoothStateScanSuccess,
    BluetoothStateScaning,
    BluetoothStateConnected,
    BluetoothStateConnecting
};

typedef NS_ENUM(NSInteger, BluetoothFailState){
    BluetoothFailStateUnExit = 0,
    BluetoothFailStateUnKnow,
    BluetoothFailStateByHW,
    BluetoothFailStateByOff,
    BluetoothFailStateUnauthorized,
    BluetoothFailStateByTimeout
};

  • 設(shè)置代理
<CBCentralManagerDelegate,CBPeripheralDelegate>
  • 添加屬性
@property (strong , nonatomic) UITableView *tableView;
@property (strong , nonatomic) CBCentralManager *manager;//中央設(shè)備
@property (assign , nonatomic) BluetoothFailState bluetoothFailState;
@property (assign , nonatomic) BluetoothState bluetoothState;
@property (strong , nonatomic) CBPeripheral * discoveredPeripheral;//周邊設(shè)備
@property (strong , nonatomic) CBCharacteristic *characteristic1;//周邊設(shè)備服務(wù)特性
@property (strong , nonatomic) NSMutableArray *BleViewPerArr;
  • 創(chuàng)建UITableView
-(void)setTableView{
    _tableView = [[UITableView alloc]initWithFrame:CGRectMake(0, 20, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height) style:UITableViewStyleGrouped];
    _tableView.delegate = self;
    _tableView.dataSource = self;
    _tableView.backgroundColor = [UIColor whiteColor];
    [self.view addSubview:_tableView];
}
  • 接下來寫tableView的dalegate
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:@"IsConnect"];
    if (cell == nil) {
        cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"IsConnect"];
    }
    
    // 將藍(lán)牙外設(shè)對(duì)象接出,取出name,顯示
    //藍(lán)牙對(duì)象在下面環(huán)節(jié)會(huì)查找出來,被放進(jìn)BleViewPerArr數(shù)組里面,是CBPeripheral對(duì)象
    CBPeripheral *per=(CBPeripheral *)_BleViewPerArr[indexPath.row];
    NSString *bleName=[per.name substringWithRange:NSMakeRange(0, 9)];
    cell.textLabel.text = per.name;
    return cell;
}

-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
    return 44;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return _BleViewPerArr.count;
}

  • 創(chuàng)建實(shí)例,設(shè)置代理,創(chuàng)建數(shù)組管理外設(shè),
self.manager = [[CBCentralManager alloc]initWithDelegate:self queue:dispatch_get_main_queue()];
self.manager.delegate = self;
self.BleViewPerArr = [[NSMutableArray alloc]initWithCapacity:1];

  • 開始掃描
-(void)scan{
    //判斷狀態(tài)開始掃瞄周圍設(shè)備 第一個(gè)參數(shù)為空則會(huì)掃瞄所有的可連接設(shè)備  你可以
    //指定一個(gè)CBUUID對(duì)象 從而只掃瞄注冊(cè)用指定服務(wù)的設(shè)備
    //scanForPeripheralsWithServices方法調(diào)用完后會(huì)調(diào)用代理CBCentralManagerDelegate的
    //- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI方法
    [self.manager scanForPeripheralsWithServices:nil options:@{ CBCentralManagerScanOptionAllowDuplicatesKey : @NO }];
    //記錄目前是掃描狀態(tài)
    _bluetoothState = BluetoothStateScaning;
    //清空所有外設(shè)數(shù)組
    [self.BleViewPerArr removeAllObjects];
    //如果藍(lán)牙狀態(tài)未開啟,提示開啟藍(lán)牙
    if(_bluetoothFailState==BluetoothFailStateByOff)
    {
        NSLog(@"%@",@"檢查您的藍(lán)牙是否開啟后重試");
    }

}
  • 接下來會(huì)檢測(cè)藍(lán)牙狀態(tài)
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
    if (central.state != CBCentralManagerStatePoweredOn) {
        NSLog(@"fail, state is off.");
        switch (central.state) {
            case CBCentralManagerStatePoweredOff:
                NSLog(@"連接失敗了\n請(qǐng)您再檢查一下您的手機(jī)藍(lán)牙是否開啟,\n然后再試一次吧");
                _bluetoothFailState = BluetoothFailStateByOff;
                break;
            case CBCentralManagerStateResetting:
                _bluetoothFailState=BluetoothFailStateByTimeout;
                break;
            case CBCentralManagerStateUnsupported:
                NSLog(@"檢測(cè)到您的手機(jī)不支持藍(lán)牙4.0\n所以建立不了連接.建議更換您\n的手機(jī)再試試。");
                _bluetoothFailState = BluetoothFailStateByHW;
                break;
            case CBCentralManagerStateUnauthorized:
                NSLog(@"連接失敗了\n請(qǐng)您再檢查一下您的手機(jī)藍(lán)牙是否開啟,\n然后再試一次吧");
                _bluetoothFailState = BluetoothFailStateUnauthorized;
                break;
            case CBCentralManagerStateUnknown:
                _bluetoothFailState = BluetoothFailStateUnKnow;
                break;
            default:
                break;
        }
        return;
    }
    _bluetoothFailState = BluetoothFailStateUnExit;
    // ... so start scanning
}

  • 中央設(shè)備開始掃描之后,我們需要實(shí)現(xiàn) centralManager:didDiscoverPeripheral:advertisementData:RSSI: 通過該回調(diào)來獲取發(fā)現(xiàn)設(shè)備。
    這個(gè)回調(diào)說明著廣播數(shù)據(jù)和信號(hào)質(zhì)量(RSSI-Received Signal Strength Indicator)的周邊設(shè)備被發(fā)現(xiàn)。通過信號(hào)質(zhì)量,可以用判斷周邊設(shè)備離中央設(shè)備的遠(yuǎn)近。
- (void)centralManager:(CBCentralManager *)central
 didDiscoverPeripheral:(CBPeripheral *)peripheral
     advertisementData:(NSDictionary *)advertisementData
                  RSSI:(NSNumber *)RSSI
{
    if (peripheral == nil||peripheral.identifier == nil/*||peripheral.name == nil*/)
    {
        return;
    }
    NSString *pername=[NSString stringWithFormat:@"%@",peripheral.name];
    //判斷是否存在@"你的設(shè)備名"
    NSRange range=[pername rangeOfString:@"你的設(shè)備名"];
    //如果從搜索到的設(shè)備中找到指定設(shè)備名,和BleViewPerArr數(shù)組沒有它的地址
    //加入BleViewPerArr數(shù)組
    if(range.location!=NSNotFound&&[_BleViewPerArr containsObject:peripheral]==NO){
        [_BleViewPerArr addObject:peripheral];
    }
    _bluetoothFailState = BluetoothFailStateUnExit;
    _bluetoothState = BluetoothStateScanSuccess;
    [_tableView reloadData];
}

掃描設(shè)備輸出臺(tái)log:

<CBPeripheral: 0x14e625f80, identifier = 954DBF72-104A-E041-19F8-D9538DBA7C23, name = brand, state = disconnected>

藍(lán)牙廣播中可以攜帶一些信息,最好將mac 地址也一并在這里廣播出來!?。?br> 掃描設(shè)備advertisementData 輸出臺(tái)log :

Printing description of advertisementData:
{
    kCBAdvDataIsConnectable = 1;
    kCBAdvDataLocalName = "brand";
    kCBAdvDataTxPowerLevel = 2;
}
  • 掃描到設(shè)備之后當(dāng)然是鏈接設(shè)備了,這里會(huì)有一個(gè)UITableView,在tableview點(diǎn)擊方法里寫連接方法
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
    CBPeripheral *peripheral=(CBPeripheral *)_BleViewPerArr[indexPath.row];
    //設(shè)定周邊設(shè)備,指定代理者
    _discoveredPeripheral = peripheral;
    _discoveredPeripheral.delegate = self;
    //連接設(shè)備
    [_manager connectPeripheral:peripheral
                        options:@{CBConnectPeripheralOptionNotifyOnConnectionKey:@YES}];
}

說明 : 點(diǎn)擊單元格連接對(duì)應(yīng)的設(shè)備,連接該設(shè)備 調(diào)用完該方法后會(huì)調(diào)用代理- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral表示連接上了設(shè)備

連接失敗會(huì)調(diào)用- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error

  • 已經(jīng)連接上該設(shè)備,就可以獲取當(dāng)前設(shè)備的信息
// 獲取當(dāng)前設(shè)備
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
    NSLog(@"%@",peripheral);
    
    // 設(shè)置設(shè)備代理
    [peripheral setDelegate:self];
    // 大概獲取服務(wù)和特征
    [peripheral discoverServices:nil];
    
    //或許只獲取你的設(shè)備藍(lán)牙服務(wù)的uuid數(shù)組,一個(gè)或者多個(gè)
    //[peripheral discoverServices:@[[CBUUID UUIDWithString:@""],[CBUUID UUIDWithString:@""]]];
    
    
    NSLog(@"Peripheral Connected");
    
    [_manager stopScan];
    
    NSLog(@"Scanning stopped");
    
    _bluetoothState=BluetoothStateConnected;
    
}

  • 各種服務(wù)

說明:在這個(gè)方法中我們要查找到我們需要的服務(wù) 然后調(diào)用discoverCharacteristics方法查找我們需要的特性
該discoverCharacteristics方法調(diào)用完后會(huì)調(diào)用代理CBPeripheralDelegate的- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error

// 獲取當(dāng)前設(shè)備服務(wù)services
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
    if (error) {
        NSLog(@"Error discovering services: %@", [error localizedDescription]);
        return;
    }
    
    NSLog(@"所有的servicesUUID%@",peripheral.services);

    //遍歷所有service
    for (CBService *service in peripheral.services)
    {
        
        NSLog(@"服務(wù)%@",service.UUID);
        
        //找到你需要的servicesuuid
        if ([service.UUID isEqual:[CBUUID UUIDWithString:@"你的設(shè)備服務(wù)的uuid"]])
        {
            //監(jiān)聽它
            [peripheral discoverCharacteristics:nil forService:service];
        }
        
        
        
    }
    NSLog(@"此時(shí)鏈接的peripheral:%@",peripheral);
    
}

  • 特征獲取

說明:在這個(gè)方法中我們要找到我們所需的服務(wù)的特性 然后調(diào)用setNotifyValue方法告知我們要監(jiān)測(cè)這個(gè)服務(wù)特性的狀態(tài)變化 當(dāng)setNotifyValue方法調(diào)用后調(diào)用代理CBPeripheralDelegate的- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
    
    if (error)
    {
        NSLog(@"Discovered characteristics for %@ with error: %@", service.UUID, [error localizedDescription]);
        return;
    }
    NSLog(@"服務(wù):%@",service.UUID);
    // 特征
    for (CBCharacteristic *characteristic in service.characteristics)
    {
        NSLog(@"%@",characteristic.UUID);
        //發(fā)現(xiàn)特征
        //注意:uuid 分為可讀,可寫,要區(qū)別對(duì)待!??!
        
        
        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"你的特征uuid"]])
        {
            NSLog(@"監(jiān)聽:%@",characteristic);//監(jiān)聽特征
            //保存characteristic特征值對(duì)象
            //以后發(fā)信息也是用這個(gè)uuid
            _characteristic1 = characteristic;
            
            [_discoveredPeripheral setNotifyValue:YES forCharacteristic:characteristic];
        }
        
        //當(dāng)然,你也可以監(jiān)聽多個(gè)characteristic特征值對(duì)象
        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"你的特征uuid"]])
        {
            //同樣用一個(gè)變量保存,demo里面沒有聲明變量,要去聲明
//            _characteristic2 = characteristic;
//            [peripheral setNotifyValue:YES forCharacteristic:_characteristic2];
//            NSLog(@"監(jiān)聽:%@",characteristic);//監(jiān)聽特征
        }
    }
}

- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
    if (error)
    {
        NSLog(@"Error updating value for characteristic %@ error: %@", characteristic.UUID, [error localizedDescription]);
        return;
    }
    
    NSLog(@"收到的數(shù)據(jù):%@",characteristic.value);
}

藍(lán)牙傳值

藍(lán)牙寫到這里,基本用法已經(jīng)說明,代碼千變?nèi)f變,思路不變,接下來介紹傳值,因?yàn)槲业捻?xiàng)目是手環(huán),我就以我們硬件工程師的藍(lán)牙接口文檔來介紹如何和硬件交互,
其實(shí)藍(lán)牙傳值相當(dāng)于網(wǎng)絡(luò)接口,硬件的service的UUID加上characteristic的UUID,
打一個(gè)比喻:service的UUID相當(dāng)于主地址,characteristic的UUID相當(dāng)于短鏈接,短鏈接必須是主地址的分支,拼在一起的是接口,你和硬件設(shè)定的藍(lán)牙傳輸格式類似于json,雙方可識(shí)別的數(shù)據(jù),因?yàn)樗{(lán)牙只能支持16進(jìn)制,而且每次傳輸只能20個(gè)字節(jié),所以要把信息流轉(zhuǎn)成雙方可識(shí)別的16進(jìn)制

  1. 下面是手環(huán)的接口文檔

APP請(qǐng)求運(yùn)動(dòng)模式基礎(chǔ)數(shù)據(jù)傳輸時(shí)拆分的總包數(shù)

字節(jié)序號(hào) 參數(shù)值
0 0xa5
1 0x06
2 0x03
3~4 2字節(jié)的時(shí)間,如1月2日用0x0102表示。
5 異或校驗(yàn)和

由此看出:0、1、2字節(jié)都是固定的,3字節(jié)是月(16進(jìn)制),4字節(jié)是日(16進(jìn)制),5字節(jié)是異或校驗(yàn)和

那么:

// APP請(qǐng)求運(yùn)動(dòng)模式基礎(chǔ)數(shù)據(jù)傳輸時(shí)拆分的總包數(shù)
- (NSData *)sportBao
{
    Byte reg[6];
    reg[0]=0xa5;
    reg[1]=0x06;
    reg[2]=0x03;
    reg[3]=0x01;
    reg[4]=0x02;
    reg[5]=(Byte)(reg[0]^reg[1]^reg[2]^reg[3]^reg[4]);
    NSData *data=[NSData dataWithBytes:reg length:6];
    return data;
}

這時(shí)候,要把請(qǐng)求命令發(fā)送給手環(huán),發(fā)送給剛才紀(jì)錄的discoveredPeripheral的藍(lán)牙設(shè)備的characteristic1的特征值

// 獲取總包數(shù)
- (void)getAltogether
{

    //生成總包數(shù)data
    NSData *d1 = [self sportBao];
    NSLog(@"寫%@",d1);
    NSLog(@"%@",discoveredPeripheral);
  [discoveredPeripheral writeValue:d1 forCharacteristic:characteristic1 type:CBCharacteristicWriteWithResponse];
  
}
  

發(fā)送完成,手環(huán)會(huì)返回?cái)?shù)據(jù)
數(shù)據(jù)是以下格式:

手環(huán)返回運(yùn)動(dòng)模式基礎(chǔ)數(shù)據(jù)傳輸時(shí)拆分的總包數(shù)

字節(jié)序號(hào) 參數(shù)值
0 0xa5
1 0x06
2 0x83
3~4 該運(yùn)動(dòng)模式數(shù)據(jù)傳輸時(shí)拆分的總包數(shù)
5 異或校驗(yàn)和

由此看出:0、1、2字節(jié)都是固定的,3-4字節(jié)是總包數(shù)(16進(jìn)制),5字節(jié)是異或校驗(yàn)和

那么:我們需要轉(zhuǎn)換3-4字節(jié)的16進(jìn)制,得到總包數(shù)

- (void)SetAltogether:(NSData *)DayData
{
    Byte *testByte = (Byte *)[DayData bytes];
    if (DayData.length == 6) {
        //收到數(shù)據(jù)之后,要異或校驗(yàn),看數(shù)據(jù)是否完整以及正確
        if (testByte[5]==(testByte[0]^testByte[1]^testByte[2]^testByte[3]^testByte[4]))
        {
            // 這里記錄總包數(shù)
            int totalBao = 0;
            totalBao = testByte[4]*256+testByte[3];
        }
    }
}

解析數(shù)據(jù)的格式已經(jīng)有了,那么在接收數(shù)據(jù)的時(shí)候

- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
    if (error)
    {
        NSLog(@"讀失敗");
        
        return;
    }
    NSLog(@"收到的數(shù)據(jù) :%@",characteristic.value);

    NSString *str = [NSString stringWithFormat:@"%@",characteristic.value];
    
    
    // 運(yùn)動(dòng)總包數(shù)
    if (str.length>7&&[[str substringWithRange:NSMakeRange(1, 2)]isEqualToString:@"a5"]&&[[str substringWithRange:NSMakeRange(5, 2)]isEqualToString:@"83"]) {
        //調(diào)用解析總包數(shù)方法
        [self SetAltogether:characteristic.value];

    }
    
}

數(shù)據(jù)傳輸介紹完畢,基本用法已經(jīng)說明

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容