蓝牙学习之①:调戏小米手环

基本流程

1.将手机作为调试机,用来搜索附近的蓝牙设备(在本次练习中,指小米手环)并连接。所以,手机是中央设备central。

2.调试机先扫描周边蓝牙设备,用UITableview展示所扫描到的周边蓝牙设备。

3.将扫描到的蓝牙设备(小米手环)设置为蓝牙发射器,即是周边设备peripheral,进行连接connect。

4.连接上蓝牙设备(小米手环)后,获取蓝牙设备(小米手环)中的所有服务services。

5.对每一个服务service进行遍历,获取其中所有的特性characteristic。

6.读取每一个特性,获取每个特性的值value。

7.对蓝牙设备(小米手环)的指定服务中的指定特性写入数据,实现我们想要达到的效果。


1.扫描设备

新建一个project,我在最开始的ViewController中添加了一个UIButton,title为@“扫描蓝牙”,点击这个按钮会跳到下一个界面BLEViewController,在这个界面中开始扫描附近的蓝牙设备。

首先在BLEViewController中导入蓝牙框架头文件

#import

遵守蓝牙协议

@interfaceBLEViewController ()<CBCentralManagerDelegate>

定义需要用到的属性

/**

 *  用于管理中央设备的manager

 */

@property (nonatomic,strong)CBCentralManager *centralManager;

/**

 * 用于保存扫描出来的周边设备的数组

 */

@property (nonatomic,strong)NSMutableArray *arrayBLE;

/**

 * 用于处理蓝牙事件的线程

 */

@property (nonatomic)dispatch_queue_t bleGCD;

viewDidLoad方法中进行相关的初始化操作。

//初始化周边设备数组

self.arrayBLE = [[NSMutableArray alloc]init];

//初始化蓝牙线程

self.bleGCD =dispatch_queue_create("BLEgcd",NULL);

//初始化中央设备管理者

self.centralManager = [[CBCentralManager alloc]initWithDelegate:self queue:self.bleGCD];

    实现CBCentralManagerDelegate中的代理方法centralManagerDidUpdateState,该方法会监听centralManager的状态变化,当运行程序的设备处在蓝牙开启状态下才开始扫描附近的蓝牙设备。

-(void)centralManagerDidUpdateState:(CBCentralManager *)central{

    switch (central.state)

    {

        caseCBCentralManagerStatePoweredOn:

           //当前运行程序的设备处于蓝牙开启状态,开始扫描附近的蓝牙设备

            [self.centralManager scanForPeripheralsWithServices:niloptions:nil];

            break;

            

        default:

            NSLog(@"central Manager Did change State");

            break;

    }

}

    - (void)scanForPeripheralsWithServices:(nullableNSArray<CBUUID *> *)serviceUUIDs options:(nullableNSDictionary<NSString *,id> *)options;这个方法会一直不停地调用,在这个方法中,services参数可以传入包含CBUUID的数组,以便只扫描到拥有指定service的蓝牙设备;options参数可以传入包含CBCentralManagerScanOptionAllowDuplicatesKey或

CBCentralManagerScanOptionSolicitedServiceUUIDsKey的字典,前一个键表示是否过滤每次扫描中发现的同一蓝牙设备,后一个键对应的值可以是一个包含CBUUID的数组,以便只扫描指定service,我感觉似乎和本方法第一个参数services有点重复?这里想扫描到附近的所有蓝牙设备,所以都传入nil。

    开始扫描之后,当扫描到蓝牙设备时,会来到下面这个方法。

-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI{

    BLEinfo * discoveredBLEinfo = [[BLEinfoalloc] init];

    //通过discoveredPeripheral这个属性保存了扫描到的peripheral,避免peripheral在当前方法结束后被释放

    discoveredBLEinfo.discoveredPeripheral = peripheral;

    discoveredBLEinfo.rssi = RSSI;

    

    //更新 tableview数据源

    [self saveBLE:discoveredBLEinfo];

}

    BLEinfo是新建的工具类,用来保存扫描发现的蓝牙设备信息,具体如下:

#import

#import

@interface BLEinfo : NSObject

/**

 *  扫描发现的蓝牙设备

 */

@property (nonatomic,strong)CBPeripheral *discoveredPeripheral;

/**

 *  该蓝牙设备的信号质量,可以用来判断其距离中央设备的远近

 */

@property (nonatomic,strong)NSNumber *rssi;

@end

    保存扫描中发现的蓝牙设备信息,并将它们展示到UITableView上:

- (BOOL)saveBLE:(BLEinfo *)discoveredBLEinfo{

    for (BLEinfo * infoin self.arrayBLE) {

        if ([info.discoveredPeripheral.identifier.UUIDStringisEqualToString:discoveredBLEinfo.discoveredPeripheral.identifier.UUIDString]) {

            return NO;

        }

    }

    [self.arrayBLEaddObject:discoveredBLEinfo];

    [self.tableViewreloadData];

    return YES;

}

    扫描到的蓝牙设备展示如下:

蓝牙学习之①:调戏小米手环_第1张图片

    2.连接设备

    点击小米手环所在的cell,进入下一个页面BLEInfoViewController,在这个页面中开始连接小米手环。需要注意的是,要把上个页面中的centralManager传入到这个页面来用,并且重新设置delegate:

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

    BLEinfo * info = [self.arrayBLEobjectAtIndex:indexPath.row];

    BLEInfoViewController * BLEInfo = [[BLEInfoViewControlleralloc] init];

    BLEInfo.centralManager =self.centralManager;

    BLEInfo.discoveredPeripheral = info.discoveredPeripheral;

    [self.navigationControllerpushViewController:BLEInfo animated:YES];

}

    用来展示当前连接上的蓝牙设备的service和characteristic的UITableViewController:

#import

#import

@interface BLEInfoViewController :UITableViewController<CBPeripheralDelegate,CBPeripheralManagerDelegate,CBCentralManagerDelegate>

/**

 *  用于管理中央设备的manager

 */

@property (nonatomic,strong)CBCentralManager *centralManager;

/**

 * 被发现的周边设备

 */

@property (nonatomic,strong)CBPeripheral *discoveredPeripheral;

/**

 *  tableview sections,保存蓝牙设备里面的services

 */

@property (nonatomic,strong)NSMutableArray *arrayServices;

/**

 *  tableview rows,保存蓝牙设备里面每一个service中的每一个特性与对应的值

 */

@property (nonatomic,strong)NSMutableArray *arrayCharacteristics;

/**

 * 用来记录有多少特性,当所有特性保存完毕,刷新表格

 */

@property (atomic,assign)int characteristicNum;

@end

    把上个页面中的centralManager传入到这个页面来用,并且重新设置delegate:

- (void)viewDidLoad {

    [superviewDidLoad];

    

    [self.centralManagersetDelegate:self];

    if (self.discoveredPeripheral) {

        //开始连接蓝牙设备

        [self.centralManagerconnectPeripheral:self.discoveredPeripheraloptions:nil];

    }

    self.arrayServices = [[NSMutableArrayalloc] init];

    self.arrayCharacteristics = [[NSMutableArrayalloc] init];

    self.characteristicNum =0;

}

    成功连接到蓝牙设备时会来到下面这个方法:

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

    //与周边设备连接成功时调用的代理方法

    

    //清空原先的服务数组

    [self.arrayServicesremoveAllObjects];

    //清空原先的特征数组

    [self.arrayCharacteristicsremoveAllObjects];

    //设置蓝牙设备的代理

    [self.discoveredPeripheralsetDelegate:self];

   //开始发现当前蓝牙设备中的服务

    [self.discoveredPeripheraldiscoverServices:nil];

}


    3.获取服务

    查找到服务之后会来到下面这个方法,在这个方法中把蓝牙设备中的服务保存到数组中

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

    //成功搜索到蓝牙设备的服务时调用的方法

    if (error) {

        NSLog(@"didDiscoverServices  error : %@",error.localizedDescription);

        return;

    }

    

    //将蓝牙设备中的服务保存到数组中

    for (CBService * servicein peripheral.services) {

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

        NSMutableDictionary * dic = [[NSMutableDictionaryalloc] initWithDictionary:@{SECTION_NAME:service.UUID.description}];

        [self.arrayServicesaddObject:dic];

       //开始发现当前服务中的特性

        [service.peripheraldiscoverCharacteristics:nilforService:service];

    }

}


    4.获取特性

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

   //成功搜索到蓝牙设备服务中的特性时调用的方法

    if (error) {

        NSLog(@"didDiscoverCharacteristicsForService error %@",error.localizedDescription);

        return;

    }

    

    NSMutableArray * characteristics = [[NSMutableArrayalloc] init];

    for (CBCharacteristic * cin service.characteristics) {

        self.characteristicNum ++;

        //读取每个特性对应的值

        [peripheral readValueForCharacteristic:c];

        [characteristics addObject:c];

    }

    [self.arrayCharacteristicsaddObject:characteristics];

}


    5.读取特性值

    在下面这个方法中可以通过characteristic.value获取到每个特性的值,类型是NSData。

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

    //获取到特性值时调用的方法

    if (error) {

        NSLog(@"didUpdateValueForCharacteristic error %@",error.localizedDescription);

        return;

    }

    

    self.characteristicNum --;

    if (self.characteristicNum ==0) {

        //所有服务中的特征值已读取完毕,更新表格

        [self.tableViewreloadData];

    }

}


    6.重新连接

    可能是小米手环中硬件设置的原因,蓝牙连接成功后30秒左右就会断开,需要在下面这个方法中重新进行蓝牙连接。

-(void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{

    NSLog(@"%s",__FUNCTION__);

    //与周边设备连接断开时调用的代理方法

    

    //清空原先的服务数组

    [self.arrayServicesremoveAllObjects];

    //清空原先的特征数组

    [self.arrayCharacteristicsremoveAllObjects];

    //刷新表格,避免在连接断开时滑动表格出现的崩溃

    [self.tableViewreloadData];

    

    //重新与周边设备建立连接

    [central connectPeripheral:peripheral options:nil];

}


    7.写入数据

蓝牙学习之①:调戏小米手环_第2张图片

    在获取到的小米手环的service中,最后一个service的UUID是0x1802,代表“Immediate Alert”。该service中有一个characteristic的UUID是0x2A06,代表“Alert level”。对这个characteristic写入不同的数值,可以使小米手环有不同的效果。点击这个characteristic所在的cell,进入下一个页面XiaoMiViewController,在这个页面中开始控制小米手环。需要注意的是,要把上个页面中的centralManager传入到这个页面来用,并且重新设置delegate:

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

    NSMutableArray * characteristics = [self.arrayCharacteristicsobjectAtIndex:indexPath.section];

    CBCharacteristic * characteristic = [characteristicsobjectAtIndex:indexPath.row];

    if ([characteristic.UUID.descriptionisEqualToString:@"2A06"]) {

        XiaoMiViewController * XiaoMi = [[XiaoMiViewControlleralloc] init];

        XiaoMi.centralManager =self.centralManager;

        XiaoMi.discoveredPeripheral =self.discoveredPeripheral;

        [self.navigationControllerpushViewController:XiaoMi animated:YES];

    }

}


    把上个页面一些必要的方法再实现一下。

- (void)viewDidLoad {

    [superviewDidLoad];

    

    self.title =@"小米手环";

    

    [self.centralManagersetDelegate:self];

}

-(void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{

    //重新与周边设备建立连接

    [central connectPeripheral:peripheral options:nil];

}

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

    //设置蓝牙设备的代理

    [self.discoveredPeripheralsetDelegate:self];

    //开始发现当前蓝牙设备中的服务

    [self.discoveredPeripheraldiscoverServices:nil];

}

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

    //开始发现当前蓝牙设备中每个服务里的每个特性

    for (CBService * servicein peripheral.services) {

        [service.peripheraldiscoverCharacteristics:nilforService:service];

    }

}


    接下来就是想要达到的效果啦。通过以下这个方法,对小米手环的指定service中的指定characteristic写入数值。

- (IBAction)weakShock {

    for ( CBService *servicein self.discoveredPeripheral.services ) {

        if ([service.UUID.descriptionisEqual:@"1802"]) {

            for ( CBCharacteristic *characteristic in service.characteristics ) {

                if ([characteristic.UUID.descriptionisEqual:@"2A06"]) {

                    /* EVERYTHING IS FOUND, WRITE characteristic ! */

                    //int转成NSData

                    int i = 1;

                    NSData *data = [NSDatadataWithBytes: &i length: sizeof(i)];

                    [self.discoveredPeripheralwriteValue:data forCharacteristic:characteristictype:CBCharacteristicWriteWithoutResponse];

                }

            }

        }

    }

}


    数值不同会有什么不一样的效果呢?传入1的话,小米手环会轻微震动;传入2的话,小米手环会强烈震动.....

你可能感兴趣的:(蓝牙学习之①:调戏小米手环)