iOS BLE开发

简介

BLE(Bluetooth Low Energy),低功耗蓝牙,使用2.4GHz无线电频率。从蓝牙4.0开始支持。通常我们的手机都已支持蓝牙4.0,iPhone从iPhone4s起已经支持蓝牙4.0(不是iPhone4s就是iPhone5,具体没仔细查),而从iPhone8开始,蓝牙已经使用5.0了,可以支持蓝牙mesh。

宣告与发现

BLE设备通过广播宣告(advertising)数据包的方式被发现,发现设备的等待时间存在概率性,取决于三个参数(宣告间隔、扫描间隔和扫描窗口)。对于iOS开发,并不需要关心这几个参数,系统底层已经为我们做了最佳的选择。

通信

蓝牙技术联盟沿用经典蓝牙的规范内容,为蓝牙低功耗定义了一些profile,这些profile定义了一个设备在特定应用情景下如何工作。profile都基于通用属性规范(GATT)。GATT定义了属性,作为通用的封装数据的单位,并定义了如何通过蓝牙连接传输属性从而达到传输数据的目的。
对于手机端来说,蓝牙设备的数据简单来说就是一堆UUID及其对应的值,iOS就是一堆CBUUID。

Exp:

将一个WiFi+BLE设备接入家庭WiFi,共分几步:

  1. 发现设备
  2. 获取设备信息,即获取设备的services和characters
  3. 连接设备
  4. 与设备交互信息
  5. 断开连接
  • 发现设备

//在你需要的地方初始化
//这里创建了一个串行队列,如果queue为nil,则使用主线程
//实际使用时globle_queue也没出现过问题
self.serial_queue = dispatch_queue_create("ble_queue", DISPATCH_QUEUE_SERIAL);
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:self.serial_queue options:nil];

//开始扫描
/* warn:扫描接口要在下面回调中调用,而不是初始化之后就可以调用。
在初始化之后立刻调用的话,由于当前central.state == CBManagerStateUnknown,会导致扫描失败
*/
//实现CBCentralManagerDelegate
- (void)centralManagerDidUpdateState:(nonnull CBCentralManager *)central {
    if (central.state == CBManagerStatePoweredOn) {
        //在这里启动扫描接口,nil表示获取所有services
        /*CBCentralManagerScanOptionAllowDuplicatesKey 
          YES表示只要收到一个BLE设备的广播包就会上报,不会对相同的BLE设备进行过滤,这样做是比较耗电的,但业务需求,要不断更新周围的BLE设备,所以这里还是选择了YES
          NO为默认值,同一个BLE设备的广播包会被合并成,只通知一次事件
        */
        [self.centralManager scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey:@(YES)}];
    }    
}

//扫描到外围设备时回调此方法
//外围设备:建立连接过程中接受建立一个活跃的物理连接请求的LE设备定义为Peripheral 角色
//中心设备:建立连接过程中发起建立活跃物理连接请求的LE 设备定义为Central 角色
//在我们这个例子中手机就是中心设备,要接入家庭WiFi的这个设备就是外围设备
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI {
    //在该方法中,可以通过advertisementData解析过滤出自己的设备
    //RSSI为信号强度,负数,值越大(越接近0),信号越好

    //找到自己要连接的设备后,可以将peripheral存起来,用于后面在某个点击事件中进行连接
}
  • 连接设备

//property getter
//我们只获取下面两个services
- (NSArray<CBUUID *> *)serviceUUIDs {
    if (_serviceUUIDs == nil) {
        //这里遇到过一个偶现的奇葩问题
        //128bit初始化uuid时偶现过崩溃,原因未知,所以使用16bit初始化
        CBUUID* uuid1 = [CBUUID UUIDWithString:@"FF00"];
        CBUUID* uuid2 = [CBUUID UUIDWithString:@"FF10"];
        _serviceUUIDs = @[uuid1, uuid2];
    }
    return _serviceUUIDs;
}

//在你合适的地方调用连接外围设备
self.centralManager connectPeripheral:self.peripheral options:nil];

//实现CBCentralManagerDelegate
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
    //获取感兴趣的服务及属性
    [peripheral discoverServices:self.serviceUUIDs];
}


//实现CBPeripheralDelegate
//发现所有你要的服务后会回调该方法
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(nullable NSError *)error {
    //获取该属性对应的服务
    for (int i = 0; i < peripheral.services.count; i++) {
        CBService *service = peripheral.services[i];
        [peripheral discoverCharacteristics:nil forService:service];
    }
}

//发现该服务对应的属性时会回调该方法
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(nullable NSError *)error {
    //这里我是做了个标记为,当获取到了所有我想要的属性之后,再开始读写操作
    //但这里对我是不方便的,业务需要我全部获取完之后才能读写,所以不能每次回调进来都进行相应的操作

    if(discoverEnd) {
        //这里就可以按照自己的协议进行读写了
        ...write(ssid+password)
        ...
    }
}
  • 读写设备

//实现相应的delegate
//读回调
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error {
    //这里有一点比较奇怪的是,我对某一属性setNotifyValue
    //只有第一次是通过didUpdateNotificationStateForCharacteristic这个回调上来的
    //而其他时候都是通过读回调上来的

    //ooxxoo
    //在通信完成后断开设备连接
    self.centralManager cancelPeripheralConnection:peripheral];
}

//通知回调
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error {

}


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


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

推荐阅读更多精彩内容