首页 > 其他分享 >蓝牙的扫描、连接、读写

蓝牙的扫描、连接、读写

时间:2023-04-27 15:35:41浏览次数:32  
标签:DCLog 蓝牙 读写 扫描 self void error peripheral

步骤:

  1. 在info.plist中加入蓝牙的权限 NSBluetoothAlwaysUsageDescription :
  2. 创建蓝牙管理者对象,创建后,首先会执行系统蓝牙是否打开的协议方法centralManagerDidUpdateState,如果系统蓝牙未打开,会有系统的弹框提示打开蓝牙,如下:
  3. 打开系统蓝牙后,开始扫描设备,扫描到设备后会执行didDiscoverPeripheral这个发现外设的方法;在这里判断是自己需要连接的设备后,停止设备扫描并进行外设的连接,如下:
  4. 外设连接成功后,会执行didConnectPeripheral方法,在该方法中设置外设的代理并设置发现服务的方法:
  5. 发现服务之后会执行didDiscoverServices方法,这里可能会有多个服务,针对自己要进行读写的服务进行判断,当是自己要进行操作的服务后,执行发现特征的方法:
  6. 发现特征后会执行didUpdateValueForCharacteristic方法,有特征的结果如下:
  7. 当该特征是可读的特征时,即可进行读操作,成功之后会走didUpdateValueForCharcteristic方法,如下:
  8. 当该特征是可写的时候,写入成功会执行didWriteValueForCharcteristic方法,如下:
  9. 可以主动断开设备的连接,如下:

源码如下:

  1. EasyWriteViewController.h 这里用到了:pod 'QMUIKit'
#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface EasyWriteViewController : QMUICommonViewController

@end

NS_ASSUME_NONNULL_END
  1. EasyWriteViewController.m
#import "EasyWriteViewController.h"
#import <CoreBluetooth/CoreBluetooth.h>

#define kPeripheralName            @"Hi-PMA58-12HOL22"//硬件设备蓝牙名称
#define kServiceUUID               @"0000FFE0-3C17-D293-8E48-14FE2E4DA212" //服务的UUID
#define kWriteCharacteristicUUID   @"FFE1" //特征的UUID
#define kNotifyCharacteristicUUID  @"FFE2" //特征的UUID
#define kReadCharacteristicUUID    @"FFE3" //特征的UUID

// 输出语句
#ifdef DEBUG
#define DCLog(...) NSLog(@"%s %@", __PRETTY_FUNCTION__, [NSString stringWithFormat:__VA_ARGS__])
#else
#define DCLog(...) do { } while (0)
#endif

@interface EasyWriteViewController ()<CBCentralManagerDelegate, CBPeripheralDelegate>

@property (nonatomic, strong) CBCentralManager *manager;
@property (nonatomic, strong) CBPeripheral *peripheral;
@property (nonatomic, strong) CBCharacteristic *character;
@property (nonatomic, strong) CBCharacteristic *characterRead;
@property (nonatomic, strong) CBCharacteristic *characterNofity;

@end

@implementation EasyWriteViewController

- (void)setupNavigationItems {
    [super setupNavigationItems];
    self.title = @"蓝牙数据读写";
    self.navigationItem.rightBarButtonItem = [UIBarButtonItem qmui_itemWithTitle:@"断开" target:self action:@selector(disConnectClick)];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 创建蓝牙管理者对象  第二个参数:nil默认为主线程
    self.manager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
    
    QMUIButton *blueWriteBtn = [DCUIHelper generateDarkFilledButton];
    blueWriteBtn.frame = CGRectMake(20, DC_STATUS_NAV_HEIGHT + 10, SCREEN_WIDTH - 40, 40);
    [blueWriteBtn setTitle:@"蓝牙写数据" forState:UIControlStateNormal];
    blueWriteBtn.tag = 1;
    [blueWriteBtn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:blueWriteBtn];
    
    QMUIButton *blueReadBtn = [DCUIHelper generateDarkFilledButton];
    blueReadBtn.frame = CGRectMake(20, DC_STATUS_NAV_HEIGHT + 60, SCREEN_WIDTH - 40, 40);
    [blueReadBtn setTitle:@"蓝牙读数据" forState:UIControlStateNormal];
    blueReadBtn.tag = 2;
    [blueReadBtn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:blueReadBtn];
    
    QMUIButton *blueNotifyBtn = [DCUIHelper generateDarkFilledButton];
    blueNotifyBtn.frame = CGRectMake(20, DC_STATUS_NAV_HEIGHT + 110, SCREEN_WIDTH - 40, 40);
    [blueNotifyBtn setTitle:@"蓝牙通知数据" forState:UIControlStateNormal];
    blueNotifyBtn.tag = 3;
    [blueNotifyBtn addTarget:self action:@selector(btnClick:) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:blueNotifyBtn];
}

- (void)btnClick:(UIButton *)btn {
    switch (btn.tag) {
        case 1: {
            [_peripheral readRSSI];
            [self writeDataToBle:@"ling123"];
        }
            break;
            
        case 2: {
            if (_characterRead.properties & CBCharacteristicPropertyRead) {
                [_peripheral readValueForCharacteristic:_characterRead];
            } else {
                DCLog(@"该字段不可读");
            }
        }
            break;
            
        case 3: {
            if (_characterNofity.properties & CBCharacteristicPropertyNotify) {
                [_peripheral setNotifyValue:YES forCharacteristic:_characterNofity];
            } else {
                DCLog(@"该字段不可通知");
            }
        }
            break;
    }
}

- (void)disConnectClick {
    [_manager cancelPeripheralConnection:self.peripheral]; // 主动断开连接
}

#pragma mark - 定时器时间事件
- (void)timered:(NSTimer *)timer {
    // 读信号强度
    [_peripheral readRSSI];
    // 实时读数
//    [self writeDataToBle:@"*APP.VALUE#$"];
    [self writeDataToBle:@"ling123"];
}

// 向蓝牙写入数
- (void)writeDataToBle:(NSString *)aStr {
    if (_character.properties & CBCharacteristicPropertyWrite) {
        // BLE设备接收的数据需要转为NSData类型
        NSData *data = [aStr dataUsingEncoding:NSUTF8StringEncoding];
        // 第一个参数:你要写入的数据,为data类型。
        // 第二个参数:你要向哪个特征写入,一般就是属性为写入的那个特征。
        // 第三个参数:你写入的方式,这个参数有两个枚举分别是:
        // CBCharacteristicWriteWithResponse//写入之后有回应
        // CBCharacteristicWriteWithoutResponse//写入之后不需要回应
        // 使用WithResponse的成功后就会调用:-(void)peripheral:(CBPeripheral )peripheral didWriteValueForCharacteristic:(CBCharacteristic )characteristic error:(NSError )error;
        [_peripheral writeValue:data forCharacteristic:_character type:CBCharacteristicWriteWithResponse];
    } else {
        DCLog(@"该字段不可写!");
    }
}

- (void)scanBlueTooth {
//    [self.manager scanForPeripheralsWithServices:nil options:nil]; // 扫描所有蓝牙设备
    [self.manager scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey:@YES}]; // 搜索附近设备
//    [self.manager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:@"D3A97ECF-C939-C85C-9923-BA2E9684E6B6"]] options:@{CBCentralManagerScanOptionAllowDuplicatesKey:@YES}]; // 扫描具体蓝牙设备
}

#pragma mark - CBCentralManagerDelegate
// 该方法当蓝牙状态改变(打开或者关闭)的时候就会调用
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
    switch (central.state) {
        case CBManagerStateUnknown: {
            DCLog(@"未知蓝牙状态");
        } break;
        case CBManagerStateResetting: {
            DCLog(@"系统服务连接暂时丢失");
        } break;
        case CBManagerStateUnsupported: {
            DCLog(@"该设备不支持蓝牙");
        } break;
        case CBManagerStateUnauthorized: {
            DCLog(@"该设备蓝牙为被授权");
        } break;
        case CBManagerStatePoweredOn: {
            DCLog(@"该设备蓝牙已打开");
            [QMUITips showSucceed:@"该设备蓝牙已打开"];
            // 打开后,开始扫描
            [self scanBlueTooth];
        } break;
        case CBManagerStatePoweredOff: {
            DCLog(@"该设备蓝牙没有打开");
        } break;
    }
}

/**
 发现外设后调用的方法 查到外设后,连接设备,停止扫描
 param:peripheral 外设(即蓝牙设备),连接,写入数据的时候需要用到。
 param:advertisementData  蓝牙传输过来的数据,包括:设备名称,自定义数据等。    自定义数据有长度限制,自己想要什么数据要和嵌入式商量好,例如MAC地址,传感器数据等
 param:RSSI  侧面反映距离的远近
 */
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI {
    // 可在该方法内部区分扫描到的蓝牙设备
    DCLog(@"发现外设peripheral:%@ =====RSSI:%@ =====UUID:%@", peripheral, RSSI, peripheral.identifier);
    // 判断是否为你要连接的设备(我这里用设备名称判断的,扫描到我要的外设蓝牙名称后,停止扫描、连接外设)
    if ([peripheral.name isEqualToString:kPeripheralName]) {
        // 扫描到设备之后停止扫描
        [_manager stopScan];
        // 开始连接外设
        [_manager connectPeripheral:peripheral options:nil];
        _peripheral = peripheral;
    }
}

// 连接失败会被调用
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
    DCLog(@"=====>连接失败");
}

// 断开连接会被调用
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
    DCLog(@"=====>断开连接");
    // 断开连接后,可能是因为信号不好,所以我们的需求是继续连接我们的设备
    [QMUITips showSucceed:@"设备已断开"];
//    [self scanBlueTooth];
}

// 连接成功会被调用
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
    DCLog(@"=====>%@连接成功 =====>UUID: %@", peripheral.name, peripheral.identifier);
    
    [QMUITips showSucceed:@"设备连接成功"];
    
    // 连接设备之后设置蓝牙对象的代理,扫描服务
    [self.peripheral setDelegate:self];

    // 外设发现服务,传nil代表不过滤
    // 这里会触发外设的代理 didDiscoverServices 方法
    [self.peripheral discoverServices:nil];

    // 这是我们业务需求:0.2s给我们的采集卡设备发一次命令来读数
//    [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(timered:) userInfo:nil repeats:YES];
}

#pragma mark - CBPeripheralDelegate
// 返回的蓝牙服务通知通过代理实现(已经发现服务)
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
    DCLog(@"=====>发现服务");
    // 一个设备可能为多个服务,所以要取你需要读写的那个服务(我们的外设就一个服务)
    for (CBService *service in peripheral.services) {
        if ([service.UUID isEqual:[CBUUID UUIDWithString:kServiceUUID]]) {
            // 根据你要的那个服务去发现特性
            [self.peripheral discoverCharacteristics:nil forService:service];
        }
    }
}

// 读取每个特征值 获得外围设备的服务、获得服务的特征
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
    // 首先读到外设特征服务,写到宏定义
    DCLog(@"发现特征服务:%@", service.UUID);

    for (CBCharacteristic *c in service.characteristics) {
        // 其次读到两个特征值,写到宏定义
        DCLog(@"特征UUID: %@", c.UUID);

        // 哪个特征值是读的,哪个特征值是写的,一般硬件兄弟都会告诉你的
        // (即使没有告诉你,也可以自己在AppStore下载LightBlue或UsrBleAssistent蓝牙调试工具看到你需要读写的特征值)
        if ([c.UUID.UUIDString isEqualToString:kWriteCharacteristicUUID]) {
            // 找到可写特征值D2
            _character = c;

        } else if ([c.UUID.UUIDString isEqualToString:kNotifyCharacteristicUUID]) {
            // 设置通知
            _characterNofity = c;
//            [_peripheral setNotifyValue:YES forCharacteristic:c];
//            [QMUITips showSucceed:@"设置通知"];
            
        } else if ([c.UUID.UUIDString isEqualToString:kReadCharacteristicUUID]) {
            _characterRead = c;
//            [_peripheral readValueForCharacteristic:c];
//            [QMUITips showSucceed:@"读取数据"];
        }
    }
}

// 读取指定特征的特征值 获取外设发来的数据,不论是read和notify,获取数据都是从这个方法中读取
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
    if (!error) {
//        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kNotifyCharacteristicUUID]]) {
            // 此处的byte数组就是接收到的数据
            NSString *str = [[NSString alloc] initWithData:characteristic.value encoding:NSUTF8StringEncoding];
            DCLog(@"原始数据: %@ ===> 字符串: %@ ===>uuid: %@", characteristic.value, str, characteristic.UUID);
//        }
    } else {
        DCLog(@"error:%@", error);
    }
}

// 写入数据成功后回调
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error {
    DCLog(@"characteristic===>%@  value===>%@",characteristic, characteristic.value);
}

- (void)peripheral:(CBPeripheral *)peripheral didReadRSSI:(NSNumber *)RSSI error:(nullable NSError *)error {
    DCLog(@"peripheral==%@ RSSI==%@ error==%@",peripheral, RSSI, error);
}

@end

标签:DCLog,蓝牙,读写,扫描,self,void,error,peripheral
From: https://www.cnblogs.com/styCy/p/17359038.html

相关文章

  • Hadoop-HDFS压测】针对HDFS进行读写性能测试
    【Hadoop-HDFS压测】针对HDFS进行读写性能测试1)测试工具2)写入数据测试3)读取数据测试4)清除数据1)测试工具Hadoop自身集成的工具包:hadoop-mapreduce-client-jobclient-3.1.1.jar注意:1、如果是Apache版本安装的Hadoop默认在lib目录下,如果是CDH版本安装的Hadoop需要自己去对......
  • 数据存储与访问——文件存储读写
    本节给大家介绍的是Android数据存储与访问方式中的一个——文件存储与读写,当然除了这种方式外,我们可以存到SharedPreference,数据库,或者Application中。1.Android文件的操作模式学过Java的同学都知道,我们新建文件,然后就可以写入数据了,但是Android却不一样,因为Android是基于Linux的,我......
  • mysql主从,django使用多数据库做读写分离
    mysql主从mysql主从搭建的目的1.读写分离2.单个实例并发量低,提高并发量3.只在主库写,读数据都去从库mysql主从原理步骤一:主库db的更新事件(update,insert,delete)被写道binlog步骤二:从库发起连接,连接到主库步骤三:此时主库创建一个binlogdumpthread线程,把binlog的内容发送到......
  • ue4 ini读写
    以Game.ini为例其他配置的读写看这里CoreGlobals.h,替换下面的GGameIni参数即可,这些字符串保存的是对应配置文件的路径externCORE_APIFStringGEditorIni;externCORE_APIFStringGEditorPerProjectIni;externCORE_APIFStringGCompatIni;externCORE_APIFStringGLigh......
  • 国产蓝牙芯片OM6621P/HS6621系列 应用门锁方案
    在5G、物联网以及互联网家装市场的快速发展等多重因素的作用下,中国智能家居市场展现蓬勃发展态势。作为智能家居“入口”产品以及家庭智能安防产品的核心单品,智能门锁以其区别于传统机械锁更具安全性、便利性、可扩展性的优势,逐渐成为智能家居生态链上不可或缺的核心组成部分,备受......
  • 蓝牙调试工具
    学习蓝牙协议栈可以看其交互数据,所用到的工具如下: airpacketsniffer:如ellisys(是个仪器)这是一个仪器,能抓取两个蓝牙设备之间的数据;将这个仪器和PC相连,在PC上通过ellisys、frontline、wireshark等软件可以看到两设备之间的交互数据。 hwhcisniffer:也是一个仪器......
  • 蓝牙基础
    蓝牙目前已更新的版本(assignednumbers文档中): 各个版本之间的差异:在core_v5.3中的卷1中: ......
  • golang 通过 os 包进行文件读写
    go中os包主要与操作系统打交道,实际底层还是通过相关的系统调用实现文件的读写操作,今天我们就来聊聊通过os包实现文件的读写操作。我们在使用os包进行相关操作时,主要流程:读操作open->read->close写操作open->read->write->close总体来说,读写操作都......
  • 达梦读写分离分发测试(Jmeter 压测)
    1. 测试目的本次测试目的主要是验证达梦读写分离集群是否生效,查询负载请求是否会自动分发给备库执行2. 达梦读写分离部署(一写一读,过程忽略)配置ip地址实例名端口号数据库版本主库192.168.145.66DM6652364-2-98-21.12.16-153423-10040-SEC......
  • 蓝牙认证
    蓝牙认证1. 蓝牙SIG认证:蓝牙SIG认证是蓝牙技术联盟(Bluetooth SIG)进行的认证,用于验证产品的互操作性、符合性和稳定性等,通常是指蓝牙设备的基本认证。2. FCC认证:美国联邦通信委员会(FCC)对所有发射的电子产品进行认证。蓝牙设备也需要通过FCC认证,以确保其符合美国法规并不会对其他......