OC 原生蓝牙框架CoreBlueTooth

今天心血来潮,想学一下蓝牙开发,然后搜了一下,找了相关的一些文章看了看,大概有了一些了解,在这里做一个记录.

蓝牙模式分为两类,一种是中心模式,一种是外设模式;

基本概念:

  • BLE:(蓝牙低能量)蓝牙4.0设备因为低耗电,也叫BLE
  • 周边,中央:外设和中心设备,发起链接的是中央(一般是指手机),被链接的设备是外围设备(运动手环)
  • service and characteristic:(服务和特征)每个设备会提供服务和特征,类似于服务端的API,但是结构不同。每个设备会有很多服务,每个服务中包含很多字段,这些字段的权限一般分为读(read),写(write),通知(notify)几种,就是我们连接设备后具体需要操作的内容
  • Description:每个characteristic可以对应一个或者多个Description用于描述characteristic的信息或属性(eg.范围,计量单位)

蓝牙的基本流程

BLE中心模式流程

  1. 建立中心角色
  2. 扫描外设(Discover Peripheral)
  3. 连接外设(Connect Peripheral)
  4. 扫描外设中的服务和特征(Discover Services And Characteristics)
    4.1 获取外设的services
    4.2 获取外设的Characteristics,获取characteristics的值,,获取Characteristics的Descriptor和Descriptor的值
  5. 利用特征与外设做数据交互(Explore And Interact)
  6. 订阅Characteristic的通知
  7. 断开连接(Disconnect)

BLE外设模式流程

  1. 启动一个Peripheral管理对象
  2. 本地peripheral设置服务,特征,描述,权限等等
  3. peripheral发送广告
  4. 设置处理订阅,取消订阅,读characteristic,写characteristic的代理方法

蓝牙设备的状态

  1. 待机状态(standby):设备没有传输和发送数据,并且没有连接到任何外设
  2. 广播状态(Advertiser):周期性广播状态
  3. 扫描状态(Scanner):主动搜索正在广播的设备
  4. 发起链接状态(Initiator):主动向扫描设备发起连接
  5. 主设备(Master):作为主设备连接到其它设备.
  6. 从设备(Slave):作为从设备链接到其它设备

蓝牙设备的五种工作状态

  • 准备(Standby)
  • 广播(Advertising)
  • 监听扫描(Scanning)
  • 发起连接(Initiating)
  • 已连接(Connected)

蓝牙和版本使用限制

  • 蓝牙2.0:越狱设备
  • BLE:iOS6以上
  • MFI认证设备:无限制

BLE测试

  1. 两台BLE设备
  2. 如何让iOS模拟器也能测试BLE?
  3. 买一个CSR蓝牙4.0 USB适配器,插在Mac上
  4. 在终端输入sudo nvram bluetoothHostControllerSwitchBehavior="never"
  5. 重启Mac
  6. 用Xcode4.6调试代码,将程序跑在iOS6.1模拟器上
  7. 苹果把iOS7.0模拟器对BLE的支持移除了

(以上内容来自博客CoreBluetooth:baseK,中心模式,外设模式流程,iBeacon

在iOS中,现在提供了一个CoreBlueTooth库,来操作蓝牙。相应代码如下

@interface ViewController ()<CBCentralManagerDelegate, CBPeripheralDelegate>

@property (strong, nonatomic) UIButton *scanButton;

@property (strong, nonatomic) CBCentralManager *cbCentralManager;
@property (strong, nonatomic) CBPeripheral *peripheral;

@end

@implementation ViewController

#pragma mark ====================    懒加载    ====================
- (UIButton *)scanButton
{
    if (!_scanButton)
    {
        _scanButton = [UIButton buttonWithType:UIButtonTypeCustom];
        [_scanButton addTarget:self action:@selector(scanButtonClickAction:) forControlEvents:UIControlEventTouchUpInside];
        [_scanButton setTitle:@"立即扫描" forState:UIControlStateNormal];
        [_scanButton setTitleColor:[UIColor blackColor] forState:UIControlStateNormal];
    }
    return _scanButton;
}



#pragma mark ====================    viewDidLoad    ====================
- (void)viewDidLoad
{
    [super viewDidLoad];
    
    [self setupUI];
}


- (void)setupUI
{
    [self.view addSubview:self.scanButton];
    [self.scanButton mas_makeConstraints:^(MASConstraintMaker *make) {
        make.top.equalTo(self.view.mas_top).offset(20);
        make.centerX.equalTo(self.view.mas_centerX);
        make.height.mas_equalTo(40);
        make.width.mas_equalTo(200);
    }];
}




#pragma mark ====================    点击事件    ====================
- (void)scanButtonClickAction:(UIButton *)button
{
    _cbCentralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
}




#pragma mark ====================    CBCentralManagerDelegate    ====================
//当蓝牙状态发生变化时调用
- (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:
        {
            UIAlertController *alertC = [UIAlertController alertControllerWithTitle:@"前往打开蓝牙设置" message:nil preferredStyle:UIAlertControllerStyleAlert];
            UIAlertAction *confirmAction = [UIAlertAction actionWithTitle:@"确定" style:UIAlertActionStyleDefault handler:nil];
            [alertC addAction:confirmAction];
            [self presentViewController:alertC animated:YES completion:nil];
            
            break;
        }
        case CBCentralManagerStatePoweredOn:
        {
            //开始扫描
//            [self.cbCentralManager scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey : @YES }];
            [self.cbCentralManager scanForPeripheralsWithServices:nil options:nil];
            break;
        }
        default:
            break;
    }
}


/**
 *  扫描到蓝牙设备后调用
 *  central     中心
 *  peripheral      外设
 *  advertisementData   外设广播的数据
 *  RSSI    接受信号强度,根据信号强度可以计算蓝牙设备的距离,计算公式为( d = 10^((abs(RSSI) - A) / (10 * n)) ),其中:d - 计算所得距离, RSSI - 接收信号强度(负值), A - 发射端和接收端相隔1米时的信号强度, n - 环境衰减因子
 */
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI
{
    //拿到小米手环的外设
    if ([peripheral.name containsString:@"MI Band 2"])
    {
        _peripheral = peripheral;
        //停止扫描
        [self.cbCentralManager stopScan];
        //开始连接
        [self.cbCentralManager connectPeripheral:_peripheral options:nil];
    }
}
//当连接外设成功时调用
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
    NSLog(@"连接外设成功");
    //设置代理
    [_peripheral setDelegate:self];
    //扫描外设中的服务
    [peripheral discoverServices:nil];
}
//当连接外设失败时调用
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
    NSLog(@"连接外设失败--%@", error);
}
//当外设时区连接时调用,可以在在这里实现自动重新连接
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
    NSLog(@"丢失连接");
    //重新连接
    [_cbCentralManager connectPeripheral:_peripheral options:nil];
}


#pragma mark ====================    CBPeripheralDelegate    ====================
/**
 *  扫描到的小米手环的服务
 *  "<CBService: 0x1c06682c0, isPrimary = YES, UUID = Device Information>",
 *  "<CBService: 0x1c06684c0, isPrimary = YES, UUID = 00001530-0000-3512-2118-0009AF100700>",
 *  "<CBService: 0x1c0668500, isPrimary = YES, UUID = 1811>",
 *  "<CBService: 0x1c0668540, isPrimary = YES, UUID = 1802>",
 *  "<CBService: 0x1c0668580, isPrimary = YES, UUID = Heart Rate>",
 *  "<CBService: 0x1c06685c0, isPrimary = YES, UUID = FEE0>",
 *  "<CBService: 0x1c0668600, isPrimary = YES, UUID = FEE1>"
 */
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
    if (error)
    {
        NSLog(@"扫描服务失败--%@--%@", peripheral.name, error);
        return;
    }
    NSLog(@"扫描服务成功--%@", peripheral.services);
    //开始扫描服务
    for (CBService *service in peripheral.services)
    {
        [peripheral discoverCharacteristics:nil forService:service];
    
    }
}

/**
 *   扫描到服务中的特征
 *   2017-10-26 00:44:53.680903+0800 CoreBlueTooth[7135:693724] 扫描特征成功--Device Information--(
 *   "<CBCharacteristic: 0x1c00b9bc0, UUID = Serial Number String, properties = 0x2, value = (null), notifying = NO>",
 *   "<CBCharacteristic: 0x1c00ba640, UUID = Hardware Revision String, properties = 0x2, value = (null), notifying = NO>",
 *   "<CBCharacteristic: 0x1c00b9fe0, UUID = Software Revision String, properties = 0x2, value = (null), notifying = NO>",
 *   "<CBCharacteristic: 0x1c00b7700, UUID = System ID, properties = 0x2, value = (null), notifying = NO>",
 *   "<CBCharacteristic: 0x1c00b8180, UUID = PnP ID, properties = 0x2, value = (null), notifying = NO>"
 *   )
 *   2017-10-26 00:44:53.775460+0800 CoreBlueTooth[7135:693724] 扫描特征成功--00001530-0000-3512-2118-0009AF100700--(
 *   "<CBCharacteristic: 0x1c00ba4c0, UUID = 00001531-0000-3512-2118-0009AF100700, properties = 0x18, value = (null), notifying = NO>",
 *   "<CBCharacteristic: 0x1c00b9f80, UUID = 00001532-0000-3512-2118-0009AF100700, properties = 0x4, value = (null), notifying = NO>"
 *   )
 *   2017-10-26 00:44:53.872212+0800 CoreBlueTooth[7135:693724] 扫描特征成功--1811--(
 *   "<CBCharacteristic: 0x1c00b9c20, UUID = 2A46, properties = 0x8, value = (null), notifying = NO>",
 *   "<CBCharacteristic: 0x1c00ba400, UUID = 2A44, properties = 0x1A, value = (null), notifying = NO>"
 *   )
 *   2017-10-26 00:44:53.969354+0800 CoreBlueTooth[7135:693724] 扫描特征成功--1802--(
 *   "<CBCharacteristic: 0x1c02a0ea0, UUID = 2A06, properties = 0x4, value = (null), notifying = NO>"
 *   )
 *   2017-10-26 00:44:54.067643+0800 CoreBlueTooth[7135:693724] 扫描特征成功--Heart Rate--(
 *   "<CBCharacteristic: 0x1c02a22e0, UUID = 2A37, properties = 0x10, value = (null), notifying = NO>",
 *   "<CBCharacteristic: 0x1c02a06c0, UUID = 2A39, properties = 0xA, value = (null), notifying = NO>"
 *   )
 *   2017-10-26 00:44:54.465547+0800 CoreBlueTooth[7135:693724] 扫描特征成功--FEE0--(
 *   "<CBCharacteristic: 0x1c00b9560, UUID = Current Time, properties = 0x1A, value = (null), notifying = NO>",
 *   "<CBCharacteristic: 0x1c00b93e0, UUID = 00000001-0000-3512-2118-0009AF100700, properties = 0x14, value = (null), notifying = NO>",
 *   "<CBCharacteristic: 0x1c00b8cc0, UUID = 00000002-0000-3512-2118-0009AF100700, properties = 0x10, value = (null), notifying = NO>",
 *   "<CBCharacteristic: 0x1c00ba6a0, UUID = 00000003-0000-3512-2118-0009AF100700, properties = 0x14, value = (null), notifying = NO>",
 *   "<CBCharacteristic: 0x1c00ba700, UUID = Peripheral Preferred Connection Parameters, properties = 0x16, value = (null), notifying = NO>",
 *   "<CBCharacteristic: 0x1c00ba760, UUID = 00000004-0000-3512-2118-0009AF100700, properties = 0x14, value = (null), notifying = NO>",
 *   "<CBCharacteristic: 0x1c00ba7c0, UUID = 00000005-0000-3512-2118-0009AF100700, properties = 0x10, value = (null), notifying = NO>",
 *   "<CBCharacteristic: 0x1c00ba8e0, UUID = 00000006-0000-3512-2118-
 *   2017-10-26 00:44:54.754186+0800 CoreBlueTooth[7135:693724] 扫描特征成功--FEE1--(
 *   "<CBCharacteristic: 0x1c00baac0, UUID = 00000009-0000-3512-2118-0009AF100700, properties = 0x16, value = (null), notifying = NO>",
 *   "<CBCharacteristic: 0x1c00baa60, UUID = FEDD, properties = 0x8, value = (null), notifying = NO>",
 *   "<CBCharacteristic: 0x1c00bac40, UUID = FEDE, properties = 0x2, value = (null), notifying = NO>",
 *   "<CBCharacteristic: 0x1c00baca0, UUID = FEDF, properties = 0x2, value = (null), notifying = NO>",
 *   "<CBCharacteristic: 0x1c00bad00, UUID = FED0, properties = 0xA, value = (null), notifying = NO>",
 *   "<CBCharacteristic: 0x1c00badc0, UUID = FED1, properties = 0xA, value = (null), notifying = NO>",
 *   "<CBCharacteristic: 0x1c00bafa0, UUID = FED2, properties = 0x2, value = (null), notifying = NO>",
 *   "<CBCharacteristic: 0x1c00bad60, UUID = FED3, properties = 0xA, value = (null), notifying = NO>",
 *   "<CBCharacteristic: 0x1c00bb120, UUID = 0000FEC1-0000-3512-2118-0009AF100700, properties = 0x1A, value = (null), notifying = NO>"
 *   )
 */
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
    if (error)
    {
        NSLog(@"扫描特征失败--%@--%@", service.characteristics, error);
        return;
    }
    NSLog(@"扫描特征成功--%@--%@--%@", peripheral.name, service.UUID, service.characteristics);
    
    //这里外设需要订阅特征的通知,否则无法收到外设发送过来的数据,需要为每一个需要读取数据的特征单独设置
//    [peripheral setNotifyValue:YES forCharacteristic:service.characteristics];
    
//    [peripheral readValueForCharacteristic:service.characteristics.firstObject];
    
    //遍历特征
    for (CBCharacteristic *characteristic in service.characteristics)
    {
        NSLog(@"%@", characteristic.UUID.UUIDString);
        //读取步数
        if ([characteristic.UUID.UUIDString isEqualToString:@"FED3"])
        {
            //可以通过两种方式来读取数据,方别进入两个回调
            [peripheral readValueForCharacteristic:characteristic];
            [peripheral setNotifyValue:YES forCharacteristic:characteristic];
        }
    }
}

//获得从外设发来的数据,在方法"readValueForCharacteristic:"执行之后调用,
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
    if (error)
    {
        NSLog(@"读取特征数据失败--%@", error);
        return;
    }
    NSLog(@"%@", characteristic);
}
//中心(本机)读取从外设发来的数据,在方法"setNotifyValue:forCharacteristic:"执行之后调用
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
    if (error)
    {
        NSLog(@"读取特征数据失败--%@", error);
        return;
    }
    NSLog(@"%@", characteristic);
}
//检测数据是否写入成功
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
    
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容

  • 这里我们具体说明一下中心模式的应用场景。主设备(手机去扫描连接外设,发现外设服务和属性,操作服务和属性的应用。一般...
    丶逝水流年阅读 2,243评论 3 4
  • (一) iOS蓝牙开发蓝牙相关基础知识 蓝牙常见名称和缩写 MFI ======= make for ipad ...
    雷鸣1010阅读 4,999评论 2 12
  • 备注:下面说到的内容都基于蓝牙4.0标准以上,主要以实践为主。 ~ CoreBluetooth.framework...
    未_漆小七阅读 1,606评论 1 8
  • 1、进程概念 所谓进程就是指现在系统正在执行的程序,每当输入一个命令时,Shell也会同时启动一个进程。每个进程启...
    ad085d162310阅读 334评论 0 0
  • 相遇 灵儿提着她的碎花裙子,红色对襟小褂被江边的风吹的飘一下,又飘一下…油纸伞下弯弯的眉眼里全是调皮。她伸出手,...
    菜菜是我啊阅读 107评论 2 1