iOS BLE 模块开发总结 - Go语言中文社区

iOS BLE 模块开发总结


 本文默认读者对蓝牙开发有基础的了解, 与外设的交互使用 BabyBluetooth.

      一. 总结的要点如下:

1. iOS 蓝牙与外设连接的步骤.

2. 外设过滤, 服务, 特性.

3. 单模,双模蓝牙.

4. 外设的 UUID.


二. 实际应用场景:

通过 APP 控制荣泰按摩椅, 方便用户切换按摩模式.


三. 第一点对应 OC 代码

1. 在 BabyBluetooth 库中, BabyBluetooth Class 封装了与蓝牙外设交互的所有方法,通过舒适化 BabyBluetooth ,实现代理方法,就可以与外设进行通信.

    //1 初始化
    //2 扫描设备
    //3 连接外设
    //3.1 查找服务
    //3.2 查找到Characteristics并筛选
    //4 写入数据
    //5 读取转换数据

2. 在搜索到蓝牙设备后,要知道每个蓝牙设备都会不少于一个的服务,每个服务都会有不少于一个的特性,我们与外设进行通信,是要约定好这个唯一的特性后,才可正确得进行串口通信.其中特性的属性有读,写两种,我们根据硬件工程师提供给我们的标识,可以获取到这两个需要的特性.

//连接Peripherals成功的委托
    [weakSelf.baby setBlockOnConnected:^(CBCentralManager *central, CBPeripheral *peripheral) {
        NSLog(@"已连接的设备:____%@", peripheral);
        //搜索指定服务
        [peripheral discoverServices: @[[CBUUID UUIDWithString:SERVICE_UUID]]];
        self.isConnected = YES;
        
    }];
//查找服务
    [weakSelf.baby setBlockOnDiscoverServices:^(CBPeripheral *peripheral, NSError *error) {
        
        for (CBService *service in peripheral.services) {
            if([service.UUID isEqual:[CBUUID UUIDWithString:SERVICE_UUID]]){
                [peripheral discoverCharacteristics:nil forService:service];
            }
        }
    }];
//查找到Characteristics的block
    [weakSelf.baby setBlockOnDiscoverCharacteristics:^(CBPeripheral *peripheral, CBService *service, NSError *error) {
        
        for (CBCharacteristic *c in service.characteristics) {
            if ([c.UUID isEqual:[CBUUID UUIDWithString:CHARACTERISTIC_RX]]) {
                NSLog(@"CHARACTERISTIC_RX:_____%@", c);
                self.characteristicR_x = c;
                if (c.isNotifying) {
                    [self.baby cancelNotify:peripheral characteristic:c];
                } else {
                    [self.peripheral setNotifyValue:YES forCharacteristic:c];
                }
            }
            
            if ([c.UUID isEqual:[CBUUID UUIDWithString:CHARACTERISTIC_TX]]) {
                
                self.characteristicT_x = c;
                NSLog(@"CHARACTERISTIC_TX:_____%@", c);
                if (c.isNotifying) {
                    [self.baby cancelNotify:peripheral characteristic:c];
                } else {
                    [self.peripheral setNotifyValue:YES forCharacteristic:c];
                }
            }
        }
    }];


3.蓝牙模块有单模,双模之分,蓝牙双模是指其支持传统蓝牙的Basic Rate(BR)和增强数据率(EDR)工作,也支持最新的低功耗(LE)标准。

4.有一些公司的外部设备的名字都是一样的但是进行连接或者其他操作的时候没必要知道具体的是哪一台设备那么就可以使用蓝牙外设的UUIDString(peripheral.identifier.UUIDString)来作为唯一标识。但是需要注意的一点不同的中心设备也可以说是不同的手机对于同一台蓝牙设备获取到的UUIDString是不一样的。举例说明一下对于同一台蓝牙设备我的手机进行扫描然后读取它的UUIDString,和你的手机进行扫描获取到的UUIDString是不同的。


除了这些,在实际测试中,出现了这种情况:一些型号的按摩椅带有蓝牙音箱,因此,想要通过按摩椅播放手机中的歌曲,又要通过手机 APP 控制按摩椅需要进行两次蓝牙连接.首先在 APP 中发现蓝牙设备并连接,这里是对按摩椅进行控制;其次在设置中发现设备并连接,这里相当于连接了蓝牙音箱,连接后在设置中会出现两个蓝牙名称,如下图:




关于这一点,客户在进行测试的时候提出了这样一个问题:为什么需要两次蓝牙连接?而不是通过 APP 直接完成两次连接要达到的目的? 最佳合理的解释是: Apple 并没有赋予 CoreBluetooth 的接口最高的权限,所以通过 APP 进行的连接只可与蓝牙设备进行串口通信,而想要实现最高权限所能完成的功能,还是要依靠 iOS 系统的!


附: demo 主要代码

//
//  ViewController.m
//  BLE_DEMO
//
//  Created by 张闯 on 17/5/10.
//  Copyright © 2017年 张闯. All rights reserved.
//

#import "ViewController.h"
#import "DeviceViewController.h"


@interface ViewController () <UITableViewDelegate, UITableViewDataSource>

@property (nonatomic, strong)  BabyBluetooth       *baby;
@property (nonatomic, strong)  UITableView         *tableView;
@property (nonatomic, strong)  NSMutableArray      *dataSource;

@end

@implementation ViewController

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    [self.baby cancelAllPeripheralsConnection];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    self.navigationItem.title = @"设备列表";
    
    self.tableView = [[UITableView alloc] initWithFrame:self.view.frame style:UITableViewStylePlain];
    self.tableView.delegate = self;
    self.tableView.dataSource = self;
    [self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:@"cell"];
    [self.view addSubview:_tableView];
    
    self.dataSource = [NSMutableArray array];
    //初始化
    self.baby = [BabyBluetooth shareBabyBluetooth];
    //设置蓝牙代理方法
    [self babyDelegate];
    //扫描外设
    self.baby.scanForPeripherals().begin();
    
    self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemRefresh target:self action:@selector(refreshBLE)];
}

- (void)refreshBLE {
    [self.dataSource removeAllObjects];
    [self.baby cancelAllPeripheralsConnection];
    self.baby.scanForPeripherals().begin();
}


//设置蓝牙委托
- (void)babyDelegate{
    
    //设置扫描到设备的委托
    __weak typeof(self) weakSelf = self;
    [weakSelf.baby setBlockOnDiscoverToPeripherals:^(CBCentralManager *central, CBPeripheral *peripheral, NSDictionary *advertisementData, NSNumber *RSSI) {
        __strong typeof(self) strongSelf = self;
        
        if (![strongSelf.dataSource containsObject:peripheral]) {
            [strongSelf.dataSource addObject:peripheral];
            [self.tableView reloadData];
        }
        
    }];
    
    //过滤器
    //设置查找设备的过滤器
    [weakSelf.baby setFilterOnDiscoverPeripherals:^BOOL(NSString *peripheralName, NSDictionary *advertisementData, NSNumber *RSSI) {
        if (peripheralName.length >1) {
            return YES;
        }
        return NO;
    }];
    
    
    //设备状态改变
    [weakSelf.baby setBlockOnCentralManagerDidUpdateState:^(CBCentralManager *central) {
        
        switch (central.state) {
            case CBManagerStatePoweredOn:
                NSLog(@"CBManagerStatePoweredOn");
                [SVProgressHUD showSuccessWithStatus:@"蓝牙已打开"];
                break;
            case CBManagerStatePoweredOff:
                NSLog(@"CBManagerStatePoweredOff");
                [SVProgressHUD showErrorWithStatus:@"蓝牙已关闭"];
                break;
            case CBManagerStateResetting:
                NSLog(@"CBManagerStateResetting");
                break;
                
            case CBManagerStateUnsupported:
                NSLog(@"CBManagerStateUnsupported");
                break;
                
            case CBManagerStateUnauthorized:
                NSLog(@"CBManagerStateUnauthorized");
                break;
                
            case CBManagerStateUnknown:
                NSLog(@"CBManagerStateUnknown");
                break;
        }
    }];
    
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    return _dataSource.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"cell"];
    cell.textLabel.text = [_dataSource[indexPath.row] name];
    return cell;
}


- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
    
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
    DeviceViewController *deviceVC = [[DeviceViewController alloc] init];
    deviceVC.peripheral = _dataSource[indexPath.row];
    [self.navigationController pushViewController:deviceVC animated:YES];
}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}


@end

//
//  DeviceViewController.m
//  BLE_DEMO
//
//  Created by 张闯 on 17/5/11.
//  Copyright © 2017年 张闯. All rights reserved.
//

#import "DeviceViewController.h"

#define SERVICE_UUID            @"XXXX"
#define CHARACTERISTIC_TX       @"XXXX"
#define CHARACTERISTIC_RX       @"XXXX"

@interface DeviceViewController ()

@property (nonatomic, strong) BabyBluetooth         *baby;
@property (nonatomic, strong) CBCharacteristic      *characteristicR_x;
@property (nonatomic, strong) CBCharacteristic      *characteristicT_x;
@property (nonatomic, strong) NSArray               *autoTypeArray;
@property (nonatomic, assign) BOOL                  isConnected;
@property (weak,   nonatomic) IBOutlet UILabel      *receivedDataLabel;

@end

@implementation DeviceViewController



- (NSArray *)autoTypeArray {
    if (!_autoTypeArray) {
        _autoTypeArray = @[@"Wake", @"Energize", @"Perform", @"Recover", @"Upper", @"Lower"];
    }
    return _autoTypeArray;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    

    self.baby = [BabyBluetooth shareBabyBluetooth];
    [self babyDelegate];
    
    //连接外设
    self.baby.having(self.peripheral).connectToPeripherals().begin();
    self.baby.characteristicDetails(self.peripheral,self.characteristicR_x);
    
}


//设置蓝牙委托
- (void)babyDelegate {
    
     __weak typeof(self) weakSelf = self;
    //连接Peripherals成功的委托
    [weakSelf.baby setBlockOnConnected:^(CBCentralManager *central, CBPeripheral *peripheral) {
        NSLog(@"已连接的设备:____%@", peripheral);
        //搜索指定服务
        [peripheral discoverServices: @[[CBUUID UUIDWithString:SERVICE_UUID]]];
        self.isConnected = YES;
        
    }];
    
    //查找服务
    [weakSelf.baby setBlockOnDiscoverServices:^(CBPeripheral *peripheral, NSError *error) {
        
        for (CBService *service in peripheral.services) {
            if([service.UUID isEqual:[CBUUID UUIDWithString:SERVICE_UUID]]){
                [peripheral discoverCharacteristics:nil forService:service];
            }
        }
    }];
    
    //设置获取到最新Characteristics值的block
    [weakSelf.baby setBlockOnReadValueForCharacteristic:^(CBPeripheral *peripheral, CBCharacteristic *characteristic, NSError *error) {
        char rData[999];
        int len = (int)characteristic.value.length;
        [characteristic.value getBytes:rData length:len];
        NSData *d = [[NSData alloc] initWithBytes:rData length:len];
        
        if (d.length == 19) {
            self.receivedDataLabel.text = [self transferData:d];
        }
    }];
    
    
    //查找到Characteristics的block
    [weakSelf.baby setBlockOnDiscoverCharacteristics:^(CBPeripheral *peripheral, CBService *service, NSError *error) {
        
        for (CBCharacteristic *c in service.characteristics) {
            if ([c.UUID isEqual:[CBUUID UUIDWithString:CHARACTERISTIC_RX]]) {
                NSLog(@"CHARACTERISTIC_RX:_____%@", c);
                self.characteristicR_x = c;
                if (c.isNotifying) {
                    [self.baby cancelNotify:peripheral characteristic:c];
                } else {
                    [self.peripheral setNotifyValue:YES forCharacteristic:c];
                }
            }
            
            if ([c.UUID isEqual:[CBUUID UUIDWithString:CHARACTERISTIC_TX]]) {
                
                self.characteristicT_x = c;
                NSLog(@"CHARACTERISTIC_TX:_____%@", c);
                if (c.isNotifying) {
                    [self.baby cancelNotify:peripheral characteristic:c];
                } else {
                    [self.peripheral setNotifyValue:YES forCharacteristic:c];
                }
            }
        }
    }];
    
    
    //characteristic订阅状态改变的block
    [weakSelf.baby setBlockOnDidUpdateNotificationStateForCharacteristic:^(CBCharacteristic *characteristic, NSError *error) {
        NSLog(@"uid:%@,isNotifying:%@",characteristic.UUID,characteristic.isNotifying?@"on":@"off");
    }];
    
    //写Characteristic成功后的block
    [weakSelf.baby setBlockOnDidWriteValueForCharacteristic:^(CBCharacteristic *characteristic, NSError *error) {
        NSLog(@"t_xCBCharacteristic: ____ %@", characteristic);
    }];

    
    //断开Peripherals的连接
    [weakSelf.baby setBlockOnDisconnect:^(CBCentralManager *central, CBPeripheral *peripheral, NSError *error) {
        NSLog(@"%@", peripheral);
        
        if ([peripheral isEqual:self.peripheral]) {
            [SVProgressHUD showErrorWithStatus:@"断开链接了"];
            self.isConnected = NO;
        }

    }];
    
    
}

- (IBAction)send:(id)sender {
    [self writeData:0x01];
}

- (IBAction)auto1:(id)sender {
    [self writeData:0x10];
}

- (IBAction)auto2:(id)sender {
    [self writeData:0x11];
}

- (IBAction)auto3:(id)sender {
    [self writeData:0x12];
}
- (IBAction)auto4:(id)sender {
    [self writeData:0x13];
    
}
- (IBAction)auto5:(id)sender {
    [self writeData:0x14];
}
- (IBAction)auto6:(id)sender {
    [self writeData:0x15];
}

- (void)writeData:(int)senderId {
    
    NSMutableData *data = [NSMutableData data];
    uint8_t tmp[10];
    uint8_t checksum = 0;
    tmp[0] = 0xF0;
    tmp[1] = 0x03;
    tmp[2] = senderId;
    checksum = tmp[1] + tmp[2];
    checksum = ~checksum;
    checksum = checksum & 0x7f;
    tmp[3] = checksum;
    tmp[4] = 0xF1;
    [data appendBytes:(void *)(&tmp) length:5];
    
    //写入数据
    if (self.isConnected) {
        [self.peripheral writeValue:data forCharacteristic:self.characteristicT_x type:CBCharacteristicWriteWithResponse];
    } else {
        [SVProgressHUD showErrorWithStatus:@"连接已断开"];
    }
    
    
}

- (NSString *)transferData:(NSData *)data {
    
    uint8_t *readTempBuffer = (uint8_t*)[data bytes];

    //运行状态
    int nChairRunState = (readTempBuffer[7]) & 0xf;
    //手动判断
    int nChairAutoType = (readTempBuffer[16] >> 2) & 0x0f;
    
    if (nChairAutoType && nChairRunState == 3) {
        return self.autoTypeArray[nChairAutoType-1];
    } else {
        return @"none";
    }
    
}



- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

/*
#pragma mark - Navigation

// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    // Get the new view controller using [segue destinationViewController].
    // Pass the selected object to the new view controller.
}
*/

@end



版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/zclengendary/article/details/72777991
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2020-06-06 09:39:39
  • 阅读 ( 1498 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢