iOS 蓝牙 BlueTooth BLE

iOS 蓝牙 BlueTooth BLE

因为工作的需要, 研究了下iOS端蓝牙方面的知识. 研究的并不深入. 只做到了两台设备互相通讯的程度. 能够满足一般项目需求.

蓝牙基础知识(FAQ)

有经典蓝牙(classic)和低功耗蓝牙(Bluetooth Low Energy BLE).

功能机时代的蓝牙非常耗电. 能待机好几天的手机开启蓝牙几个小时就没电了... 但是蓝牙4.0(BLE) 大大降低了蓝牙的耗电量.

iOS端蓝牙通信框架CoreBlueTooth. 在iPhone4s开始支持,专门用于与BLE设备通讯.

经典蓝牙和BLE的流程是不同的.

经典模式 1.扫描设备. 2.建立设备连接. 3.建立socket连接. 4.发送和接收数据. 5.通讯完毕关闭连接清理缓存.

BLE 比经典模式复杂很多. 下面会详细介绍.

BLE简介

BLE下 设备有四种角色. 这里只介绍两种. 中心设备(center)外围设备(peripheral)

中心设备负责扫描外围设备. 当扫描到外围设备后主动发起连接. 连接成功后扫描外围设备的服务和特征. 根据服务和特征进行通信.

外围设备负责广播自己的服务和特征.

服务和特征

服务(service) 特征(characteristic)

每个蓝牙4.0的设备都是通过服务和特征来展示自己的. 一个设备必然包含一个或多个服务,每个服务下面又包含若干个特征. 特征是与外界交互的最小单位.

例如特征A用于写数据. 特征B用来读数据.

服务和特征都是用UUID来标示自己. (此处UUID只是一种唯一标示符. 和iOS真机调试, AdHoc打包的UUID没什么关系.他们都是一种唯一标示符)

有一些国际通用的服务UUID . 这些在CoreBlueTooth框架中可以找到(宏定义).

当然你也可以使用自己独特的UUID. 使用UUIDDesign生成.

中心设备与外围设备如何通讯.

通讯无非是读(read)和写(write). 但是在BLE中. 读和写都是由中心设备发起的. 中心设备可以向外围设备发起写请求(request中包含写入的数据. ) 也可以像外围设备发起读请求(response中包含外围设备写入的数据.)

还有一种比较特殊. notify 也可以称为订阅模式. 订阅模式是中心设备订阅外围设备的某个特征. 当外围设备更新被订阅的特征时. 中心设备会收到更新的信息. (这种模式一次只能传输20个byte)

编码

中心设备模式

创建中心设备对象

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

检测当前设备是否已经开启蓝牙. 开启蓝牙后开始扫描外围设备

扫描设备时不指定UUID. 会扫描出所有设备. 指定UUID.则只会扫描到提供指定UUID服务的设备.(也就是提供某种指定服务的设备, 如果你不希望扫描到乱七八糟的设备可以自己生成UUID.)

[self.centralManager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]] options:@{ CBCentralManagerScanOptionAllowDuplicatesKey : @YES }];

当扫描到外围设备时自动调用这个方法, 在这个方法中连接外围设备(连接设备是有可能失败的. 连接失败有一个单独的回调方法.).

- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
    // 判断信号强度
    if (RSSI.integerValue > -15) {
        return;
    }
    
    if (RSSI.integerValue < -35) {
        return;
    }
    
    //由于本方法会被反复调用. 所以已经连接过的设备就不用重复connect了
    if (self.discoveredPeripheral != peripheral) {
        self.discoveredPeripheral = peripheral;
        [self.centralManager connectPeripheral:peripheral options:nil];
    }
}

connect成功会调用这个方法

连接成功后扫描这台设备提供的服务.

- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
    //连接成功停止扫描(如果做一对多的情况, 不停止扫描)
    [self.centralManager stopScan];
    
    peripheral.delegate = self;
    
    //扫描服务. 
    [peripheral discoverServices:@[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]]];
}

扫描到指定服务后调用这个方法

扫描到想要的服务后查看这个服务中的各个特征, 寻找想要的特征并且把特征对象存起来!

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
    if (error) {
        return;
    }
    
    //下面扫描了两个特征. 
    for (CBService *service in peripheral.services) {
        [peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID]] forService:service];
        [peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID_CENTER_WRITE]] forService:service];
    }
}

扫描到指定特征后调用这个方法. 扫描到想要的特征后. 订阅它或者向它们发送读请求或者写请求.

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{   
    for (CBCharacteristic *characteristic in service.characteristics) {
        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID_CENTER_WRITE]]) {
        //这是一个拥于向外围设备写数据的特征
            self.writeCharacteristic = characteristic;      
            [peripheral discoverDescriptorsForCharacteristic:characteristic];
        }
        
        //这个特征用于订阅外围设备.
        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID]]) {
            self.transferCharacteristic = characteristic;
            [peripheral setNotifyValue:YES forCharacteristic:characteristic];
            [peripheral discoverDescriptorsForCharacteristic:characteristic];
        }
    }
}

订阅模式, 当外围设备更新特征时这个方法会被调用.

用这种模式可以很方便的实现一对多通讯. 一个外围设备同时连接多台中心设备. 当外围设备更新特征.所有中心设备都会收到更新的信息.(一次只能传20个字节, 我没有试过传超过20个字节会发生什么. 如果有人试验了请给我回信. 谢谢.)

- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
    if (error) {
        NSLog(@"Error discovering characteristics: %@", [error localizedDescription]);
        return;
    }
    
    NSString *stringFromData = [[NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding];
    
    if ([stringFromData isEqualToString:@"EOM"]) {
        [self.data resetBytesInRange:NSMakeRange(0, self.data.length)];
        [self.data setLength:0];
        return ;
    }
    [self.data appendData:characteristic.value];
    
}

向外围设备的某个特征写数据.

//data是字节数据.
[self.discoveredPeripheral writeValue:data forCharacteristic:self.writeCharacteristic type:CBCharacteristicWriteWithResponse];

外围设备模式

创建外围设备对象

self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];

创建服务和特征

//这是一个用于中心设备订阅外围设备信息的特征
self.transferCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID]
                                                                     properties:CBCharacteristicPropertyNotify
                                                                          value:nil
                                                                    permissions:CBAttributePermissionsReadable];
    
//这是一个用于中心设备向外围设备写数据的特征.
self.receiveDataCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UUID_CENTER_WRITE]
                                                                        properties:CBCharacteristicPropertyWrite value:nil permissions:CBAttributePermissionsWriteable];
    
//创建服务
CBMutableService *transferService = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]
                                                                       primary:YES];
    
transferService.characteristics = @[self.transferCharacteristic, self.receiveDataCharacteristic];

//将服务添加到外围设备对象中
[self.peripheralManager addService:transferService];

开启广播advertising

[self.peripheralManager startAdvertising:@{ CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]] }];

当有中心设备订阅了外围设备的特征时这个方法会被调用

//当中心设备订阅了某个特征时 调用这个方法.
- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic
{
    NSString *string = @"这一条是外围设备发送的测试数据,, 当中心设备订阅了外围设备特征的时候就会自动发送这段话.end";
    self.dataToSend = [string dataUsingEncoding:NSUTF8StringEncoding];
    [self sendData];
}

总结

通信无非是读和写

  • 中心设备向外围设备写数据. 需要外围设备提供可以接收写请求的特征. 中心设备向特征写数据.

  • 中心设备读外围设备的数据. 外围设备提供可以接收读请求的特征. 中心设备向特征发送读请求.

  • 外围设备向中心设备写数据. 这个可以用订阅模式. 如果中心设备订阅了特征. 外围设备更新特征即向中心设备发送了数据.

  • 外围设备读中心设备的数据. 恩这个情况好像只能由中心设备发起吧...
    中心设备向外围设备发送写请求(也就是上面的第一种情况)

<a name="UUIDdesign-zd">UUIDDesign</a>

ARC下生成一个UUID

- (NSString *)gen_uuid
{
    CFUUIDRef uuid_ref = CFUUIDCreate(NULL);
    CFStringRef uuid_string_ref= CFUUIDCreateString(NULL, uuid_ref);
    
    CFRelease(uuid_ref);
    NSString *uuid = [NSString stringWithString:(__bridge NSString*)uuid_string_ref];
    
    CFRelease(uuid_string_ref);
    return uuid;
}

转发请注明出处(简书 行如风).

我的理解有什么错漏之处还请指出谢谢

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

推荐阅读更多精彩内容