iOS 藍芽

QianEr9312發表於2015-12-22

iOS 開發藍芽4.0的框架是CoreBluetooth

在CoreBluetooth中有兩個主要的部分,Central(中心)和Peripheral(周邊)。

CBPeripheralManager作為周邊裝置是伺服器,CBCentralManager作為中心裝置是客戶端。所有可用的iOS 裝置可以作為周邊 也可以作為中央,但不可以同時既是周邊也是中央。

一般手機是客戶端(中心裝置),裝置(魔法棒)---->硬體裝置,因為是手機去連線手環這個伺服器的。周邊(Peripheral)是生成或者儲存了資料的裝置,中心是使用這些資料的裝置。

可以認為周邊是一個廣播資料的裝置,他廣播到外部世界說他這兒有資料,並且也說明了能提供的服務。另一邊,中央開始掃描附近有沒有服務,如果中央發現了想要的服務,然後中央就會請求連線周邊,一旦連線成功,兩個裝置之間就開始交換資料了。

除了中央和周邊,還要考慮兩者交換的資料結構,這些資料在服務中被結構化,每個服務由不同的特徵組成,特徵是包含一個單一邏輯值的屬性型別。


BLE-----------buletouch low energy , 藍芽4.0裝置耗電低

peripheral--外設  central 中心 發起連線的是central 被連線的裝置是peripheral


service and characteristics -- 服務和特徵 每個裝置都會提供服務和特徵,類似於服務端的API,但是機構不同。每個外設會有很多服務,每個服務中包含很多欄位,這些欄位的許可權一般分為  讀read 和寫write ,通知notify幾種,就是我們連線裝置後具體需要操作的內容。

Description 每個characteristics可以對應一個或多個Descrition使用者描述characteristics的資訊或屬性



藍芽一般有兩種不同的業務場景,一種叫做中心模式,就是以你的app作為中心,連線其他的外設的場景;另一種是外設模式,使用手機作為外設識別其他中心裝置操作的場景。


藍芽中心模式流程

1 建立中心角色

2 掃描外設

3 連線外設

4掃描外設中的服務和特徵

4.1 獲取外設的services

4.2 獲取外設的Characteristics,獲取characteristics的值,獲取characteristics的Descriptor和Descriptor的值

5 與外設做資料互動

6 訂閱characteristics的通知

7 斷開連線(disconnect)


藍芽外設模式流程

1 啟動一個Peripheral管理物件

2 本地Peraipheral設定服務、特性、描述、許可權等等

3 Peripheral傳送廣告

4 設定處理訂閱、取消訂閱、讀Characteristic 寫characteristics的委託方法



藍芽裝置的狀態

CBCentralManagerStateUnknown ------ 初始化中

CBCentralManagerStateResetting ------ 裝置不支援狀態

CBCentralManagerStateUnsupported --------裝置未授權狀態

CBCentralManagerStateUnauthorized ---------裝置未授權狀態

CBCentralManagerStatePoweredOff -------- 尚未開啟藍芽

CBCentralManagerStatePoweredOn ----------藍芽已經成功開啟



藍芽裝置中的五種工作狀態

準備 廣播 監聽掃描 發起連線  已連線


藍芽外設模式

1 開啟peripheralManager,設定peripheralManager的委託
2 建立characteristics,characteristics的description  建立service ,把characteristics新增到service中
3 開啟廣播advertising
4 對central的操作進行響應
     4.1 讀characteristics請求
     4.2 寫characteristics請求
     4.4 訂閱和取消訂閱characteristics



//

//  HB_BindStickViewController.m

//  HomeBridge

//

//  Created by Jason on 15/9/9.

//  Copyright (c) 2015 ___HB___. All rights reserved.

//


#import "HB_BindStickViewController.h"

#import <CoreBluetooth/CoreBluetooth.h>

#import "Header.h"



#define OPENBLUETOOTH 100

#define BINDSUCCESS 101

#define ALREADYBIND 102


@interface HB_BindStickViewController ()<UITableViewDataSource,UITableViewDelegate,CBCentralManagerDelegate,CBPeripheralDelegate,UIAlertViewDelegate>

@property (weak, nonatomic) IBOutlet UITableView *tableView;

@property (weak, nonatomic) IBOutlet UIView *bindView;

@property (nonatomic,retain) NSMutableArray * dataArray;


@property(nonatomic,retain)CBCentralManager * manager;

@property(nonatomic,retain)CBPeripheral * peripheral;

@property(nonatomic,retain)CBCharacteristic * characteristic;

@property(nonatomic,assign)NSInteger type;

@property (nonatomic,copy) NSString * teacherID;

@property (nonatomic,assign) BOOL isShowAlert;

//@property (nonatomic,copy) NSMutableString * macID;



@end


@implementation HB_BindStickViewController


- (void)viewDidLoad {

    [super viewDidLoad];

   

    if (!_dataArray) {

        _dataArray = [[NSMutableArray alloc]initWithCapacity:1];

    }

    self.manager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];

    

}

- (void)getUserInfoWithID:(NSString *)user_id{

//    HB_UserInfoModel * userinfo = [HB_UserInfoModel defaultManager];

    

    //判斷是否相同_macID

    [HB_UserService customsGetPropWithUserId:[NSNumber numberWithInteger:[user_id integerValue]] tn:@"customs" field:@"rftoken" withBlock:^(NSDictionary *result, NSError *error) {

        if (result) {

            NSString * str = [result valueForKey:@"Content"];

            NSString * _macID = [[NSUserDefaults standardUserDefaults]stringForKey:@"macID"];

            if ([str isEqual:_macID]) {

                [self showAL:user_id];

            }else{

                [self Bindbluetooth];

            }

        }

    }];

}

- (void)showAL:(NSString *)user_id{

    NSString * val = [NSString stringWithFormat:@"%@",user_id];

    [HB_UserService customsGetPropWithUserId:[NSNumber numberWithInteger:[user_id integerValue]] tn:@"customs" field:@"phone" withBlock:^(NSDictionary *result, NSError *error) {

        if (result) {

            NSString * phone = [result valueForKey:@"Content"];

            [HB_UserService getUserInfo:@"id" Val:val More:@1 withBlock:^(NSDictionary *result1, NSError *error1) {

                if (result1) {

                    NSArray * item = [result1 valueForKey:@"Content"];

                    NSString * name;

                    if (item.count > 0 ) {

                        name = [[item objectAtIndex:0] valueForKey:@"name_zh"];

                    }

                    NSString * string = [NSString stringWithFormat:@"此裝置已繫結到其他教師,如需使用請聯絡此教師解鎖此裝置\n%@聯絡電話:%@",name,phone];

                    UIAlertView * alertView = [[UIAlertView alloc]initWithTitle:@"教棒已被使用" message:string delegate:self cancelButtonTitle:@"確定" otherButtonTitles: nil];

                    alertView.tag = ALREADYBIND;

                    if (!_isShowAlert) {

                        _isShowAlert = YES;

                        [alertView show];

                    }

                }

            }];

        }

    }];


}

- (IBAction)ScanBluetoothBtnClick:(UIButton *)sender {

    if (_manager.state != CBCentralManagerStatePoweredOn) {

        UIAlertView * alertView = [[UIAlertView alloc]initWithTitle:@"開啟藍芽" message:nil delegate:self cancelButtonTitle:@"不開啟" otherButtonTitles:@"開啟", nil];

        alertView.tag = OPENBLUETOOTH;

        [alertView show];

    }else{

        [self.manager scanForPeripheralsWithServices:nil options:nil];

    }

}

//藍芽狀態改變

- (void)centralManagerDidUpdateState:(CBCentralManager *)central

{

    NSString * message;

    switch (central.state) {

        case 0:

            message = @"初始化中,請稍後……";

            break;

        case 1:

            message = @"裝置不支援狀態,過會請重試……";

            break;

        case 2:

            message = @"裝置未授權狀態,過會請重試……";

            break;

        case 3:

            message = @"裝置未授權狀態,過會請重試……";

            break;

        case 4:

            message = @"尚未開啟藍芽,請在設定中開啟……";

            break;

        case 5:

            message = @"藍芽已經成功開啟,稍後……";

            break;

        default:

            break;

    }

}

//掃描到裝置

- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{

    NSLog(@"發現藍芽裝置:%@",peripheral.name);

    if (_dataArray.count == 0) {

        [_dataArray addObject:peripheral];

    }else{

        for (NSInteger i = 0 ; i < _dataArray.count; i++) {

            CBPeripheral * ph = _dataArray[i];

            if (i == _dataArray.count - 1 && ph != peripheral) {

                [_dataArray addObject:peripheral];

            }

        }

    }

    

    [_tableView reloadData];

    if ([peripheral.name isEqual:BLUETOOTHNAME]) {

        self.peripheral = peripheral;

    }

}


//連線成功

- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{

    peripheral.delegate = self;

    [central stopScan];

    [peripheral discoverServices:nil];

}


//返回的藍芽服務通知通過代理實現

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{

    for (CBService * service in peripheral.services) {

        if (error) {

            NSLog(@"Error changing notification state: %@",[error localizedDescription]);

            return;

        }

        NSLog(@"Service found with UUID :%@",service.UUID);

        NSLog(@"%@",service.UUID.UUIDString);

        //        if ([service.UUID isEqual:[CBUUID UUIDWithString:@"FFE0"]]) {

        [peripheral discoverCharacteristics:nil forService:service];

        //        }

    }

}


//查詢到該裝置所對應的服務

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{

    if (error) {

        NSLog(@"Error changing notification state: %@",[error localizedDescription]);

        return;

    }

    NSLog(@"%@",service.UUID.UUIDString);

    for (CBCharacteristic * characteristic in service.characteristics) {

        NSLog(@"查詢到的服務(屬性)%@",characteristic.UUID);

        NSString * str1 = characteristic.UUID.UUIDString;

        NSLog(@"哈哈哈哈哈哈哈哈%@",str1);

        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"2A23"]]) {

            [peripheral readValueForCharacteristic:characteristic];

        }

        //所對應的屬性用於接收和傳送資料

        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"2AF0"]]) {

            [peripheral setNotifyValue:YES forCharacteristic:characteristic];

            [peripheral readValueForCharacteristic:characteristic];

        }

        //        NSLog(@"%@",string);

        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"2AF1"]]) {

            _characteristic = characteristic;

//            [self Bindbluetooth];

            [self LookForID];

        }

    }

}



//接收資料的函式.處理藍芽發過來得資料   讀資料代理

-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{

    if (error) {

        NSLog(@"Error changing notification state: %@",[error localizedDescription]);

        return;

    }

     NSLog(@"---------%@",[characteristic UUID]);

    if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"2AF0"]]) {

        NSLog(@"收到藍芽發來的資料%@",characteristic.value);

        NSString * string = [self hexadecimalString:characteristic.value];

        if ([string isEqual:@"00"]) {

            [self LookForID];

            return;

        }

        if ([string isEqual:@"0000ff00ff00"]) {

            if (_type == 2) {

                [self contect];

            }

        }

        

        NSLog(@"%@",string);

        [self getUserIDWithString:string];

    }else if([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"2A23"]]){

        NSLog(@"收到藍芽發來的資料%@",characteristic.value);

        NSString * string = [self hexadecimalString:characteristic.value];

        NSLog(@"##############################%@",string);

        NSMutableString * _macID = [[NSMutableString alloc]init];

        for (NSInteger i = string.length - 1 ; i >= 0 ; i-= 2) {

            NSString * sr = [string substringWithRange:NSMakeRange(i - 1, 2)];

            if (i== 1) {

//                sr = [NSString stringWithFormat:@"%d",[self to10:sr].integerValue - 16];

                sr = [self to16:[self to10:sr].integerValue - 16];

            }

            [_macID appendString:sr];

            if (i % 2 != 0 && i > 1) {

                [_macID appendString:@":"];

            }

        }

        [[NSUserDefaults standardUserDefaults]setValue:_macID forKey:@"macID"];

    }

}



//寫資料代理

- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{

    sleep(3);

    NSLog(@"%@",characteristic.UUID);

    if (error) {

        NSLog(@"Error changing notification state: %@",[error localizedDescription]);

        [HB_Utils showItoast:@"繫結失敗"];

    }

}



- (void)contect{

    NSString * _macID = [[NSUserDefaults standardUserDefaults]stringForKey:@"macID"];

    [HB_UserService customsEditWithUserId:[HB_UserInfoModel defaultManager].user_id tn:@"customs" field:@"rftoken" val:_macID withBlock:^(NSDictionary *result, NSError *error) {

        if (result) {

            UIAlertView * alertView = [[UIAlertView alloc]initWithTitle:@"魔法棒繫結成功,可以鼓勵孩子了!" message:nil delegate:self cancelButtonTitle:@"確定" otherButtonTitles: nil];

            alertView.tag = BINDSUCCESS;

            if (!_isShowAlert) {

                _isShowAlert = YES;

                [alertView show];

            }

            return;

        }

    }];

}

#pragma mark - NSData and NSString


//將傳入的NSData型別轉換成NSString並返回

- (NSString*)hexadecimalString:(NSData *)data{

    NSString* result;

    const unsigned char* dataBuffer = (const unsigned char*)[data bytes];

    if(!dataBuffer){

        return nil;

    }

    NSUInteger dataLength = [data length];

    NSMutableString* hexString = [NSMutableString stringWithCapacity:(dataLength * 2)];

    for(int i = 0; i < dataLength; i++){

        [hexString appendString:[NSString stringWithFormat:@"%02lx", (unsigned long)dataBuffer[i]]];

    }

    result = [NSString stringWithString:hexString];

    return result;

}

//將傳入的NSString型別轉換成NSData並返回

- (NSData*)dataWithHexstring:(NSString *)hexstring{

    NSData* aData;

    return aData = [hexstring dataUsingEncoding: NSUTF16StringEncoding];

}

// 十六進位制轉換為普通字串的。

- (NSString *)stringFromHexString:(NSString *)hexString { //

    

    char *myBuffer = (char *)malloc((int)[hexString length] / 2 + 1);

    bzero(myBuffer, [hexString length] / 2 + 1);

    for (int i = 0; i < [hexString length] - 1; i += 2) {

        unsigned int anInt;

        NSString * hexCharStr = [hexString substringWithRange:NSMakeRange(i, 2)];

        NSScanner * scanner = [[NSScanner alloc] initWithString:hexCharStr];

        [scanner scanHexInt:&anInt];

        myBuffer[i / 2] = (char)anInt;

    }

    NSString *unicodeString = [NSString stringWithCString:myBuffer encoding:4];

    NSLog(@"------字串=======%@",unicodeString);

    return unicodeString;

}




- (NSString *)to10:(NSString *)num

{

    NSString *result = [NSString stringWithFormat:@"%ld", strtoul([num UTF8String],0,16)];

    return result;

}

- (NSString *)to16:(int)num

{

    NSString *result = [NSString stringWithFormat:@"%@",[[NSString alloc] initWithFormat:@"%1x",num]];

    if ([result length] < 2) {

        result = [NSString stringWithFormat:@"0%@", result];

    }

    return result;

    

}

- (void)LookForID{

    _type = 1;

    if(_characteristic == nil){

        NSLog(@"_characteristic 為空");

        return;

    }

    u_int8_t packet[7] = {0x00, 0x00, 0xFF, 0x03, 0x01, 0x04, 0x00};

    

    NSMutableData *value = [NSMutableData data];

    

    for (NSInteger i = 0; i < 7; i ++) {

        NSData * data ;

        data = [NSData dataWithBytes:&packet[i] length:1];

        NSLog(@"%02lx",(unsigned long)packet[i]);

        [value appendData:data];

    }

    NSLog(@"十六進位制:%@",value);

    sleep(3);

    [_peripheral writeValue:value forCharacteristic:_characteristic type:CBCharacteristicWriteWithResponse];

    NSLog(@"已經向外設%@的特徵值%@寫入資料",_peripheral.name,_characteristic.description);

}

- (void)Bindbluetooth{

    _type = 2;

    if(_characteristic == nil){

        NSLog(@"_characteristic 為空");

        return;

    }

    u_int8_t packet[8] = {0x00, 0x00, 0xFF, 0x07, 0x02, 0x00, 0x00, 0x00};

//    u_int16_t str = 539;

//    NSString * m = [self to16:540];

    NSInteger userid = [[HB_UserInfoModel defaultManager].user_id integerValue];

//    NSLog(@"----%@",[self stringFromHexString:s]);

    NSData * idData = [NSData dataWithBytes:&userid length:4];

    const unsigned char* dataBuffer = (const unsigned char*)[idData bytes];

    NSMutableData * Data5= [NSMutableData data];

    NSInteger length = 0;

    for (NSInteger i = idData.length - 1; i >= 0; i --) {

        [Data5 appendData:[NSData dataWithBytes:&dataBuffer[i] length:1]];

        length += [[self to10:[NSString stringWithFormat:@"%02lx", (unsigned long)dataBuffer[i]]]integerValue];

    }

    NSLog(@"%@",Data5);

    

    NSInteger checkNum = length + 9;

    NSData * checkNumData = [NSData dataWithBytes:&checkNum length:4];

    const unsigned char* dataBuffer2 = (const unsigned char*)[checkNumData bytes];

    NSMutableData * Data6= [NSMutableData data];

    for (NSInteger i = 0; i < 1; i ++) {

        [Data6 appendData:[NSData dataWithBytes:&dataBuffer2[i] length:1]];

    }

    NSLog(@"%@",Data6);

    

    

    NSMutableData *value = [NSMutableData data];


    for (NSInteger i = 0; i < 8; i ++) {

        NSData * data ;

        if (i == 5) {

            data = Data5;

        }else if(i == 6){

            data = Data6;

        }else{

            data = [NSData dataWithBytes:&packet[i] length:1];

        }

        NSLog(@"%02lx",(unsigned long)packet[i]);

        [value appendData:data];

    }

    NSLog(@"十六進位制:%@",value);

    sleep(3);

    

    [_peripheral writeValue:value forCharacteristic:_characteristic type:CBCharacteristicWriteWithResponse];

    NSLog(@"已經向外設%@的特徵值%@寫入資料",_peripheral.name,_characteristic.description);

}



#pragma mark -tableViewDelegate

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{

    return _dataArray.count;

}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{

    UITableViewCell * cell = [tableView dequeueReusableCellWithIdentifier:@"Cell"];

    CBPeripheral * peripheral = _dataArray[indexPath.row];

    

    //NSLog(@"%@",peripheral.RSSI);

    cell.textLabel.text = peripheral.name;

    if ([peripheral.name isEqual:BLUETOOTHNAME]) {

        cell.detailTextLabel.text = @"魔法棒";

    }else{

        cell.detailTextLabel.text = nil;

    }

    

    return cell;

}

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{

    CBPeripheral * peripheral = _dataArray[indexPath.row];

    if ([peripheral.name isEqual:BLUETOOTHNAME]) {

        self.peripheral = peripheral;

        [self.manager connectPeripheral:self.peripheral options:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:CBConnectPeripheralOptionNotifyOnDisconnectionKey]];

    }else{

        UIAlertView * alertView = [[UIAlertView alloc]initWithTitle:@"選擇的裝置有誤" message:nil delegate:self cancelButtonTitle:@"重新選擇" otherButtonTitles: nil];

        [alertView show];

    }

}

- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{

    return 44;

}

- (CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section{

    return 0.01;

}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section{

    return 0.01;

}

- (void)didReceiveMemoryWarning {

    [super didReceiveMemoryWarning];

    // Dispose of any resources that can be recreated.

}

//應答回覆

- (void)answerBack{

    if(_characteristic == nil){

        NSLog(@"_characteristic 為空");

        return;

    }

    u_int8_t packet[6] = {0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00};

    NSMutableData *value = [NSMutableData data];

    for (NSInteger i = 0; i < 6; i ++) {

        NSData * data = [NSData dataWithBytes:&packet[i] length:1];

        [value appendData:data];

    }

    NSLog(@"十六進位制:%@",value);

    sleep(3);

    [_peripheral writeValue:value forCharacteristic:_characteristic type:CBCharacteristicWriteWithResponse];

    NSLog(@"已經向外設%@的特徵值%@回覆資料",_peripheral.name,_characteristic.description);

}

#pragma mark - alertViewDelegate

- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex{

    if (alertView.tag == OPENBLUETOOTH) {

        if (buttonIndex == 1) {

            [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"prefs:root=Bluetooth"]];

        }

    }else if (alertView.tag == BINDSUCCESS){

        [[NSUserDefaults standardUserDefaults]setBool:YES forKey:ISBINGING];

        if ([self.delegate respondsToSelector:@selector(updateUIWithBindSuccess:)]) {

            [self.delegate updateUIWithBindSuccess:YES];

        }

        [self.navigationController popViewControllerAnimated:YES];

    }else if (alertView.tag == ALREADYBIND){

        [self.navigationController popViewControllerAnimated:YES];

    }

}



- (void)getUserIDWithString:(NSString *)string{

    if (string.length > 15 && _type == 1) {

        _teacherID = [self to10:[string substringWithRange:NSMakeRange(8, 8)]];

        if (_teacherID.integerValue == 0) {

            [self Bindbluetooth];

        }else{

            [self getUserInfoWithID:_teacherID];

        }

        [self answerBack];

    }

    

}


@end




相關文章