蓝牙常用开发步骤

如果你进来了,点下关注行不行_

随说 : 网上资料一大堆, 可以随便google搜一下加深理解, 这里作一个整理, 可收藏起来当资料随时翻查

蓝牙常用框架 : CoreBluetooth
蓝牙常用库 :
BabyBluetooth
YmsCoreBluetooth
基本概念要搞清楚

1. 名词解释#


CoreBluetooth框架的核心其实是两个东西,peripheral(CBCebtralManager类)和central(CBPeripheralManager类), 可以理解成外设和中心
打个比喻.小米手环是peripheral,我们的iphone手机是central,所以就会大量使用CBCebtralManager类。(Iphone理解为连接)
又比如像用一个ipad和一个iphone通讯, ipad可以认为是central,iphone端是peripheral ,这种情况下在iphone端就要使用CBPeripheralManager类来开发了。(Iphone理解为外设)
发起连接的时central,被连接的设备为perilheral


每个蓝牙4.0的设备都是通过服务和特征来展示自己的

service : 一个设备必然包含一个服务以上(至少包含一个服务,有可能包含更多的服务)
characteristic : 包含在服务下, 描述该服务下特定的功能的, 一般拥有多个characteristic, 而且characteristic有类型, 读(read), 写(write), 通知(notify)等
UUID(Universally Unique Identifier) : 设备的每一个service有他相应的UUID, 同时每一个characteristic也有相应的UUID

简单理解 : service and characteristic 每个设备会提供服务和特征,类似于服务端的api,但是机构不同。每个外设会有很多服务,每个服务中包含很多字段,这些字段的权限一般分为 读read,写write,通知notiy几种,就是我们连接设备后具体需要操作的内容。

注 : 蓝牙设备硬件厂商通常都会提供他们的设备里面各个服务(service)和特征(characteristics)的功能,比如哪些是用来交互(读写),哪些可获取模块信息(只读)等, 也就是说, 蓝牙设备硬件厂商, 提供了这些


2. 开发基本步骤#

  1. 建立中心角色
  2. 扫描外设(discover)
  3. 连接外设 (connect)
  4. 扫描外设中的服务和特征(discover)
    • 4.1 获取外设的services
    • 4.2 获取外设的Characteristics,获取Characteristics的值,获取Characteristics的Descriptor和Descriptor的值
  5. 与外设做数据交互(explore and interact)
  6. 订阅Characteristic的通知
  7. 断开连接(disconnect)

一 : 建立中心角色####

// 连接中心角色, 所谓的中心角色,就是蓝牙设备管理对象, 可以把他理解为主设备,通过他,可以去扫描和链接外设
// 初始化并设置委托和线程队列,最好一个线程的参数可以为nil,默认会就main线程
self.centralManager = [[CBCentralManager alloc]initWithDelegate:self queue:dispatch_get_main_queue()];


// 这个是BabyBluetooth第三方库
// 单例模式
+ (instancetype)shareBabyBluetooth {
    static BabyBluetooth *share = nil;
    static dispatch_once_t oneToken;
    dispatch_once(&oneToken, ^{
        share = [[BabyBluetooth alloc]init];
    });
   return share;
}

- (instancetype)init {
    self = [super init];
    if (self) {
        //初始化对象
        babyCentralManager = [[BabyCentralManager alloc]init];// 蓝牙中心模式实现类
        babySpeaker = [[BabySpeaker alloc]init]; // block查找和channel切换
        babyPeripheralManager = [[BabyPeripheralManager alloc]init]; // 外设模式实现类
        // babyPeripheralManager里面有一个成员属性babySpeaker , 将这个赋值于它
        babyPeripheralManager->babySpeaker = babySpeaker;
        // babyCentralManager里面有一个成员属性babySpeaker , 将这个赋值于它
        babyCentralManager->babySpeaker = babySpeaker;
    }
    return self;
    
}

// 注册蓝牙中心后, 必须调用的代理方法, 不然会报错
// 主设备状态改变的委托,在初始化CBCentralManager的适合会打开设备,只有当设备正确打开后才能使用
// 特别注意, 需要在状态CBCentralManagerStatePoweredOn下才能使用任何设备
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
    
    //发送通知
    [[NSNotificationCenter defaultCenter]postNotificationName:BabyNotificationAtCentralManagerDidUpdateState object:@{@"central":central}];
    
    switch (central.state) {
        case CBCentralManagerStateUnknown:
            BabyLog(@">>>CBCentralManagerStateUnknown");
            break;
        case CBCentralManagerStateResetting:
            BabyLog(@">>>CBCentralManagerStateResetting");
            break;
        case CBCentralManagerStateUnsupported:
            BabyLog(@">>>CBCentralManagerStateUnsupported");
            break;
        case CBCentralManagerStateUnauthorized:
            BabyLog(@">>>CBCentralManagerStateUnauthorized");
            break;
        case CBCentralManagerStatePoweredOff:
            BabyLog(@">>>CBCentralManagerStatePoweredOff");
            break;
        case CBCentralManagerStatePoweredOn:
            BabyLog(@">>>CBCentralManagerStatePoweredOn");
            [[NSNotificationCenter defaultCenter] postNotificationName:BabyNotificationAtCentralManagerEnable object:@{@"central":central}];
            break;
        default:
            break;
    }
    //状态改变callback
    if ([currChannel blockOnCentralManagerDidUpdateState]) {
        [currChannel blockOnCentralManagerDidUpdateState](central);
    }
}



二 : 扫描设备####

// 框架提供的方法
/*!
 *  @method scanForPeripheralsWithServices:options:
 *
 *  @param serviceUUIDs UUID列表, 参数有值的话则只扫描改UUID
 *  @param options      An optional dictionary specifying options for the scan.
 *
 *
 *  @see               发现设备就会调用这个代理方法 centralManager:didDiscoverPeripheral:advertisementData:RSSI:
 *  @seealso            CBCentralManagerScanOptionAllowDuplicatesKey
 * @seealso   CBCentralManagerScanOptionSolicitedServiceUUIDsKey
 *
 */
- (void)scanForPeripheralsWithServices:(nullable NSArray<CBUUID *> *)serviceUUIDs options:(nullable NSDictionary<NSString *, id> *)options;

// 当然有扫描方法就有停止扫描的方法
/*!
 *  @method stopScan:
 *
 *  @discussion Stops scanning for peripherals.
 *
 */
- (void)stopScan;



//这个是BabyBluetooth第三方库实现的扫描方法
//扫描Peripherals
- (void)scanPeripherals {
    [centralManager scanForPeripheralsWithServices:[currChannel babyOptions].scanForPeripheralsWithServices options:[currChannel babyOptions].scanForPeripheralsWithOptions];
}

三 : 发现设备 - 连接设备####

//这个是BabyBluetooth第三方库实现的连接方法
//扫描到Peripherals, 当扫描到设备, 就会执行这个代理方法entralManager:didDiscoverPeripheral:advertisementData:RSSI:
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
    
    //日志
    //BabyLog(@"当扫描到设备:%@",peripheral.name);
    [self addDiscoverPeripheral:peripheral];
    
    //发出通知
    [[NSNotificationCenter defaultCenter]postNotificationName:BabyNotificationAtDidDiscoverPeripheral
                                                       object:@{@"central":central,@"peripheral":peripheral,@"advertisementData":advertisementData,@"RSSI":RSSI}];
    //扫描到设备callback
    if ([currChannel filterOnDiscoverPeripherals]) {
        if ([currChannel filterOnDiscoverPeripherals](peripheral.name,advertisementData,RSSI)) {
           // 执行回调Block, 然后就会执行setBlockOnDiscoverToPeripherals:(void (^)(CBCentralManager *central,CBPeripheral *peripheral,NSDictionary *advertisementData, NSNumber *RSSI))block
             if ([currChannel blockOnDiscoverPeripherals]) { 
                [[babySpeaker callbackOnCurrChannel] blockOnDiscoverPeripherals](central,peripheral,advertisementData,RSSI);
            }
        }
    }
    
    //处理连接设备
    if (needConnectPeripheral) {
        if ([currChannel filterOnconnectToPeripherals](peripheral.name,advertisementData,RSSI)) {
            [centralManager connectPeripheral:peripheral options:[currChannel babyOptions].connectPeripheralWithOptions];
            //开一个定时器监控连接超时的情况
            connectTimer = [NSTimer scheduledTimerWithTimeInterval:5.0f target:self selector:@selector(disconnect:) userInfo:peripheral repeats:NO];
        }
    }
}


注意 : 
1, 回调这个代理方法,(CBPeripheral *)peripheral就是返现到的服务, 必须保存下来作为
2, 需要过滤搜索结果, 可以通过filterOnDiscoverPeripherals Block


------------

连接成功与失败,断开连接等会调用的三个代理方法
//连接到Peripherals-成功
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
            NSLog(@">>>连接到名称为(%@)的设备-成功",peripheral.name);
        }

//连接到Peripherals-失败
-(void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
            NSLog(@">>>连接到名称为(%@)的设备-失败,原因:%@",[peripheral name],[error localizedDescription]);
        }

//Peripherals断开连接
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
            NSLog(@">>>外设连接断开连接 %@: %@\n", [peripheral name], [error localizedDescription]);
        }

---------------

//BabyBluetooth第三方库实现的连接情况Block
//设置设备连接成功的委托,同一个baby对象,使用不同的channel切换委托回调
    [baby setBlockOnConnectedAtChannel:channelOnPeropheralView block:^(CBCentralManager *central, CBPeripheral *peripheral) {
        [SVProgressHUD showInfoWithStatus:[NSString stringWithFormat:@"设备:%@--连接成功",peripheral.name]];
    }];
    
    //设置设备连接失败的委托
    [baby setBlockOnFailToConnectAtChannel:channelOnPeropheralView block:^(CBCentralManager *central, CBPeripheral *peripheral, NSError *error) {
        NSLog(@"设备:%@--连接失败",peripheral.name);
        [SVProgressHUD showInfoWithStatus:[NSString stringWithFormat:@"设备:%@--连接失败",peripheral.name]];
    }];

    
    //设置设备断开连接的委托
    [baby setBlockOnDisconnectAtChannel:channelOnPeropheralView block:^(CBCentralManager *central, CBPeripheral *peripheral, NSError *error) {
        NSLog(@"设备:%@--断开连接",peripheral.name);
        [SVProgressHUD showInfoWithStatus:[NSString stringWithFormat:@"设备:%@--断开失败",peripheral.name]];
    }];

一些坑爹的情况#

1.为什么扫描不到外设?
->情况1:设备不支持
babybluetooth支持BLE4.0蓝牙设备,如果是2.0的设备,那肯定扫描不到
模拟器不支持蓝牙调试,必须使用真机调试
ios操作系统至少要在6.0以上

->情况2:外设没有开启广播
默认情况下打开手机或者ipad的蓝牙,在使用babybluetooth是扫描不到设备的,因为手机和ipad只是打开了蓝牙设备的电源而已,并没有发送广播,如果想扫描到设备,需要编程的方式,通过iphone或ipad,mac发送蓝牙广播。demo程序中有一个iphone和一个mac发送蓝牙广播的程序。
已连接的设备会停止广播,所以也搜索不到

->情况3:为什么系统的蓝牙能找到设备,但是使用babybluetooth的demo却无法找到?
这种情况一般是因为那个蓝牙设备是支持MFI的,并不是普通的ble设备。
关于MFI这里补充一下 :
MFI ( make for ipad ,iphone, itouch ), 专们为苹果设备制作的设备, 开发使用的是 ExternalAccessory 框架
所以这篇文章不予谈论

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容

  • (一) iOS蓝牙开发蓝牙相关基础知识 蓝牙常见名称和缩写 MFI ======= make for ipad ...
    雷鸣1010阅读 4,941评论 2 12
  • 这里我们具体说明一下中心模式的应用场景。主设备(手机去扫描连接外设,发现外设服务和属性,操作服务和属性的应用。一般...
    丶逝水流年阅读 2,128评论 3 4
  • 等先生,你真要走,嗯,那你走我不挽留,你真要走,嗯,我放开你的手,你真要走,我在雨的这边守候,等你回首。
    宿命里的我们阅读 151评论 0 0
  • 今天在做论坛之后的一些调研与反馈,有学员对于我主持风格提出了一些建议,希望我能够更棒!我突然想起庞太师第一...
    胡永群阅读 654评论 0 50
  • 董明明阅读 859评论 3 2