iOS【蓝牙开发】实践一 数据接发

说明:这次实践的主要目的是简单掌握蓝牙的一些基本信息,实现数据的接发,中心与外设互相发送数据,大数据分块发送。最后为项目链接。

中心1.png
外设2.png
蓝牙基础(常见缩写)
MFI: 专们为苹果设备制作的设备,详见:关于MFi认证你所必须要知道的事情;
BLE:蓝牙4.0设备因为低耗电,所以也叫做BLE;
MFI:开发使用ExternalAccessory 框架;
4.0 BLE:开发使用CoreBluetooth 框架;

Peripheral:外设,被连接的设备为Peripheral;
Central:中心,发起连接的是Central;
Service:服务,每个外设会有很多服务,每个服务中包含很多字段,这些字段的权限一般分为 读read,写write,通知notiy几种;
Characteristic:一个服务下面也可以存在多个特征,特征可以理解成具体实现功能的窗口,一般特征都会有value,也就是特征值,特征是与外界交互的最小单位;
Description:每个特征可以对应一个或多个Description用户描述characteristic的信息或属性;
UUID:可以理解成蓝牙上的唯一标识符,为了区分不同的服务和特征,或者给服务和特征取名字,我们就用UUID来代表服务和特征;


快速生成UUID:
打开Mac OS X的Terminal.app,用uuidgen命令生成一个128bit的UUID

//服务id
#define TRANSFER_SERVICE_UUID  @"0FB51F75-C9D5-45DC-BA61-065BD4A5E3E8"
//特征id
#define TRANSFER_CHARACTERISTIC_UP_UUID @"B678C8E2-9B1A-4952-A320-EF6D42F0831A"

-------------------------------
蓝牙中心模式流程:
1. 建立中心角色
2. 扫描外设(discover)
3. 连接外设(connect)
4. 扫描外设中的服务和特征(discover)
5. 与外设做数据交互(explore and interact)
6. 订阅Characteristic的通知
7. 断开连接(disconnect)

中心:首先要导入蓝牙库
#import <CoreBluetooth/CoreBluetooth.h>

遵守协议
<CBCentralManagerDelegate,CBPeripheralDelegate>

1. 建立中心角色 创建CBCentralManager对象
_centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];

2. 扫描外设(discover)
//查看设备是否支持 ,若支持则扫描
-(void)centralManagerDidUpdateState:(CBCentralManager *)central{
    NSArray *uuidArray = [NSArray arrayWithObjects:[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID],nil];
    NSDictionary *optionsDic = [[NSDictionary alloc] initWithObjectsAndKeys:[NSNumber numberWithBool:NO],CBCentralManagerScanOptionAllowDuplicatesKey, nil];
    [_centralManager scanForPeripheralsWithServices:uuidArray options:optionsDic];
}

3. 连接外设(connect)
-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {

    if (_discoveredPeripheral != peripheral) {
        _discoveredPeripheral = peripheral;
        [_centralManager connectPeripheral:peripheral options:nil];
    }
}

4. 扫描外设中的服务和特征(discover)
//当连接成功后,系统会通过回调函数告诉我们,然后我们就在这个回调里去扫描设备下所有的服务和特征
-(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {

    peripheral.delegate = self;
    //指定服务 
    [peripheral discoverServices:[[NSArray alloc] initWithObjects:[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID], nil]];
    //[peripheral discoverServices:nil];
}

-4.1 获取外设的services  6. 订阅Characteristic的通知
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{

    //处理我们需要的特征
    for (int i=0; i<peripheral.services.count; i++) {
        CBService *service = peripheral.services[i];       
    
        //指定需要查找的特征(对应外设中订阅特征)
        [peripheral discoverCharacteristics:[[NSArray alloc] initWithObjects:[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_Up_UUID],[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_Image_UUID], nil] forService:service];
        //[peripheral discoverCharacteristics:nil forService:service];
    }
}

- 4.2 获取外设的Characteristics,获取Characteristics的值,获取Characteristics的Descriptor和Descriptor的
// 5 已搜索到Characteristics
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {

    //属于那个服务 根据不同的特征执行不同的命令
    if ([service.UUID isEqual:[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]]) {
    
        for (int i=0; i<service.characteristics.count; i++) {
            CBCharacteristic *characteristic = service.characteristics[i];

            if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_Up_UUID]]) {
                _upCharacteristic = characteristic;
                //监听设备
                [peripheral setNotifyValue:YES forCharacteristic:characteristic];
            }
        }
    }
}

5. 与外设做数据交互(explore and interact)
///获取外设发来的数据,不论是read和notify,获取数据都是从这个方法中读取。
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
    NSData *dataValue = characteristic.value;
}
//给外设发数据
- (void)wrietPeripheral:(CBPeripheral *)peripheral characteristic:(CBCharacteristic *)characteristic value:(NSData *)data {

    if(_imageCharacteristic.properties & CBCharacteristicPropertyWrite || _imageCharacteristic.properties & CBCharacteristicPropertyWriteWithoutResponse) {
       [self.discoveredPeripheral writeValue:data forCharacteristic:_imageCharacteristic type:CBCharacteristicWriteWithResponse];
    }
}
// 写入数据后的回调方法
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
}
停止扫描
[_centralManager stopScan];
主动断开设备
[_centralManager cancelPeripheralConnection:_discoveredPeripheral];

大数据,分块发包
- (void)actionSend:(UIButton *)btn {//发送大数据 分包处理
    NSInteger BLE_SEND_MAX_LEN = 512;
    imgName = [imgName isEqualToString:@"2.jpg"] ? @"3.jpg" : @"2.jpg";
    testImgView.image = [UIImage imageNamed:imgName];

    NSData *msgData = UIImageJPEGRepresentation([UIImage imageNamed:imgName], 1.0);

    for (int i = 0; i < [msgData length]; i += BLE_SEND_MAX_LEN) {
        // 预加 最大包长度,如果依然小于总数据长度,可以取最大包数据大小
        if ((i + BLE_SEND_MAX_LEN) < [msgData length]) {
            NSString *rangeStr = [NSString stringWithFormat:@"%i,%li", i, (long)BLE_SEND_MAX_LEN];
            NSData *subData = [msgData subdataWithRange:NSRangeFromString(rangeStr)];
            [[HMBLECenterHandle sharedHMBLECenterHandle] wrietPeripheral:nil
                                                          characteristic:nil
                                                                   value:subData];
            //根据接收模块的处理能力做相应延时
            usleep(20 * 1000);
        }
        else {
            NSString *rangeStr = [NSString stringWithFormat:@"%i,%i", i, (int)([msgData length] - i)];
            NSData *subData = [msgData subdataWithRange:NSRangeFromString(rangeStr)];
       
            [[HMBLECenterHandle sharedHMBLECenterHandle] wrietPeripheral:nil
                                                          characteristic:nil
                                                                   value:subData];
            usleep(20 * 1000);
            
            //发送结束标识
            NSData *exoData = [@"exo" dataUsingEncoding:NSUTF8StringEncoding];
            [[HMBLECenterHandle sharedHMBLECenterHandle] wrietPeripheral:nil
                                                          characteristic:nil
                                                                   value:exoData];

        }
    }
}

外设:首先要导入蓝牙库
#import <CoreBluetooth/CoreBluetooth.h>

遵守协议
<CBPeripheralManagerDelegate>

1. 建立外设角色 创建CBPeripheralManager对象
_peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];

2. 本地Peripheral设置服务,特性,描述,权限等等
//1 查看设备是否支持 ,若支持则创建 服务和特征
-(void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral{

    //一组特征值
    _upCharacteristic = [[CBMutableCharacteristic alloc] initWithType:[CBUUID UUIDWithString:TRANSFER_CHARACTERISTIC_UP_UUID] properties:CBCharacteristicPropertyNotify | CBCharacteristicPropertyRead | CBCharacteristicPropertyWrite | CBCharacteristicPropertyWriteWithoutResponse value:nil permissions:CBAttributePermissionsReadable | CBAttributePermissionsWriteable];

    //一个服务中添加多个特征值
    transferService = [[CBMutableService alloc] initWithType:[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID] primary:YES];
    NSArray *characters = [[NSArray alloc] initWithObjects:_upCharacteristic, nil];
    [transferService setCharacteristics:characters];

    // 2 添加一个服务
    [self.peripheralManager addService:transferService];  
}

3. Peripheral发送广告
// 3 会监听didAddService
-(void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error{
    NSLog(@"添加服务");
    if (error != nil) {
        NSLog(@"添加服务失败: %@",error.localizedDescription);
    } else {        
        // 开始广播
        [self.peripheralManager startAdvertising:@{ CBAdvertisementDataLocalNameKey : @"Service_name", CBAdvertisementDataServiceUUIDsKey :@[[CBUUID UUIDWithString:TRANSFER_SERVICE_UUID]] }];
    }
}

4. 设置处理订阅、取消订阅、
//当中央端连接上了此设备并订阅了特征时会回调 didSubscribeToCharacteristic
-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic {
NSLog(@"中心已经预定了特征 --- %@",characteristic);
}

//当中央端取消订阅时会调用didUnsubscribeFromCharacteristic
-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic {
NSLog(@"中心没有从特征预定 -- %@",characteristic);
}

 4. 读characteristic、写characteristic的委托方法
//当接收到中央端读的请求时会调用didReceiveReadRequest
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request {
    if (request.characteristic.properties & CBCharacteristicPropertyRead) {
        NSData *data = request.characteristic.value;        
        [request setValue:data];
        [self.peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
    } else {
        [self.peripheralManager respondToRequest:request withResult:CBATTErrorReadNotPermitted];
    }
}

//当接收到中央端写的请求时会调用didReceiveWriteRequest
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray<CBATTRequest *> *)requests {
CBATTRequest *request = requests[0];
    if (request.characteristic.properties & CBCharacteristicPropertyWrite) {
        CBMutableCharacteristic *c = (CBMutableCharacteristic *)request.characteristic;
        c.value = request.value;
        NSData *data = c.value;
       [self.peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
    } else {
        [self.peripheralManager respondToRequest:request withResult:CBATTErrorWriteNotPermitted];
    }
}

4. 往中心发送数据
- (BOOL)updateValue:(NSData *)value characteristic:(CBMutableCharacteristic *)characteristic {

    if (value && characteristic) {
       return [self.peripheralManager updateValue:value forCharacteristic:characteristic onSubscribedCentrals:nil];
    }
    return NO;
}

项目Demo:蓝牙外设蓝牙中心

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

推荐阅读更多精彩内容