蓝牙开发说简单也简单,说不简单也有点难,开发人员在首次开发蓝牙前首先需要搞清楚蓝牙开发的概念,还要了解掌握蓝牙开发的一整套流程,这样才能快速上手开发蓝牙。 蓝牙开发分为两种模式:管理者模式和中心者模式。管理者模式基本很少用到,相当于iPhone手机作为外设,自己创建服务和特性,然后用其他设备连接iPhone手机;中心者模式一般是大部分情况下都会使用的,使用中心者模式开发相当于iPhone手机作为主机,连接蓝牙外设,下面介绍蓝牙开发的例子就是使用的中心者模式来讲解的。 在这里我还是要推荐下我自己建的iOS开发学习群:680565220,群里都是学ios开发的,如果你正在学习ios ,我欢迎你加入,今天分享的这个案例已经上传到群文件,大家都是软件开发党,不定期分享干货(只有iOS软件开发相关的),包括我自己整理的一份2018最新的iOS进阶资料和高级开发教程 一、关于蓝牙开发的一些重要的理论概念: 1、服务(services):蓝牙外设对外广播的时候一定会有一个服务,有些时候也可以是有多个服务,服务下面包含一些特性,服务可以理解成一个模块的窗口; 2、特征(characteristic):特征存在于服务下面的,一个服务下面可以有多个特征,特征可以理解成具体实现功能的窗口,一般的特性都会有value,也就是特征值,是特征和外界交互的最小单位; 3、UUID:蓝牙上的唯一标示符,为了区分不同服务和特征,就用UUID来表示。 二、蓝牙连接的主要步骤 1、创建一个CBCentralManager实例来进行蓝牙管理; 2、搜索扫描外围设备; 3、连接外围设备; 4、获得外围设备的服务; 5、获得服务的特征; 6、从外围设备读取数据; 7、给外围设备发送(写入)数据。 三、蓝牙连接和数据读写的具体步骤 1、导入苹果系统蓝牙框架 #import 2、遵循两个蓝牙框架相关的协议 3、新建两个实例属性,一个特征属性 @property (nonatomic, strong) CBCentralManager *centralManager; //中心管理者 @property (nonatomic, strong) CBPeripheral *peripheral; //连接到的外设 @property (nonatomic, strong) CBCharacteristic *characteristic; //特征 4、初始化CBCentralManager,进行蓝牙管理 - (void)viewDidLoad { [super viewDidLoad]; self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue()]; //创建实例进行蓝牙管理 } //若中心管理者初始化之后 就会触发下面这个代理方法 该代理方法是用来判断手机蓝牙的状态的 - (void)centralManagerDidUpdateState:(CBCentralManager *)central { // 蓝牙可用,开始扫描外设 if (central.state == CBManagerStatePoweredOn) { NSLog(@"蓝牙可用"); //在中心管理者成功开启之后再进行一些操作 //搜索扫描外设 // 根据SERVICE_UUID来扫描外设,如果不设置SERVICE_UUID,则扫描所有蓝牙设备 // [self.centralManager startAdvertising:@{CBAdvertisementDataServiceUUIDsKey:@[[CBUUID UUIDWithString:SERVICE_UUID]]}]; [central scanForPeripheralsWithServices:nil options:nil]; } if(central.state == CBManagerStateUnsupported) { NSLog(@"该设备不支持蓝牙"); } if (central.state == CBManagerStatePoweredOff) { NSLog(@"蓝牙已关闭"); } if (central.state == CBManagerStateUnknown) { NSLog(@"蓝牙当前状态不明确"); } if (central.state == CBManagerStateUnauthorized) { NSLog(@"蓝牙未被授权"); } } 5、搜索外围设备 //执行扫描动作之后,如果扫描到外设了,就会自动回调下面的协议方法 /** 发现符合要求的外设,回调 */ - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI { NSLog(@"%@====",peripheral.name); //根据外设名字有选择性的筛选连接蓝牙设备 if ([peripheral.name hasPrefix:@"TEAMOSA"]) { //在这里对外设携带的广播数据进行进一步的处理 if ([self.peripheraNames containsObject:peripheral.name]) { //如果数组中包含了就不再添加 return; } //添加到外设名字数组中 [self.peripheraNames addObject:peripheral.name]; //标记外设,让它的生命周期与控制器的一致 self.peripheral = peripheral; // 可以根据外设名字来过滤外设 // [central connectPeripheral:peripheral options:nil]; } // 连接外设 // [central connectPeripheral:peripheral options:nil]; } 6、连接外围设备 //连接外围设备,中心管理者连接外设成功,如果连接成功就会回调这个协议方法 /** 连接成功 */ - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{ //连接成功之后,可以进行服务和特性的发现。 停止中心管理设备的扫描动作,要不然在你和已经连接好的外设进行数据沟通时,如果又有一个外设进行广播且符合你的连接条件,那么你的iOS设备也会去连接这个设备(因为iOS BLE4.0是支持一对多连接的),导致数据的混乱。 //停止扫描动作 [self.centralManager stopScan]; // 设置外设的代理 peripheral.delegate = self; // 根据UUID来寻找服务 // [peripheral discoverServices:@[[CBUUID UUIDWithString:SERVICE_UUID]]]; //外设发现服务,传nil代表不过滤,一次性读出外设的所有服务 [peripheral discoverServices:nil]; NSLog(@"%s, line = %d, %@=连接成功", __FUNCTION__, __LINE__, peripheral.name); } //外设连接失败 /** 连接失败的回调 */ - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error { NSLog(@"%s, line = %d, %@=连接失败", __FUNCTION__, __LINE__, peripheral.name); } //丢失连接 掉线 /** 断开连接 */ - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error { NSLog(@"%s, line = %d, %@=断开连接", __FUNCTION__, __LINE__, peripheral.name); // 断开连接可以设置重新连接 [central connectPeripheral:peripheral options:nil]; } 7、获取外围设备服务和特征 /** 发现服务 */ - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error { // 遍历出外设中所有的服务 for (CBService *service in peripheral.services) { // NSLog(@"所有的服务:%@",service); } // 这里仅有一个服务,所以直接获取 CBService *service = peripheral.services.lastObject; // 根据UUID寻找服务中的特征 // [peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:CHARACTERISTIC_UUID]] forService:service]; // [peripheral discoverCharacteristics:@[service.UUID] forService:service]; [peripheral discoverCharacteristics:nil forService:service]; } 8、从外围设备读取数据 // 更新特征的value的时候会调用 (凡是从蓝牙传过来的数据都要经过这个回调,简单的说这个方法就是你拿数据的唯一方法) 你可以判断是否 从外围设备读数据 /** 接收到数据回调 */ - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { // if (characteristic == @"你要的特征的UUID或者是你已经找到的特征") { // //characteristic.value就是你要的数据 // } if ([peripheral.name hasPrefix:@"TEAMOSA"]){ NSData *data = characteristic.value; NSString *value = [self hexadecimalString:data]; // NSLog(@"characteristic(读取到的): %@, data : %@, value : %@", characteristic, data, value); } // 拿到外设发送过来的数据 // NSData *data = characteristic.value; // self.textFild.text = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; } 9、向外围设备发送(写入)数据 //这个方法你可以放在button的响应里面,也可以在找到特征的时候就写入,具体看你业务需求怎么用 //[self.peripherale writeValue:_batteryData forCharacteristic:self.characteristic type:CBCharacteristicWriteWithResponse];//第一个参数是已连接的蓝牙设备; 第二个参数是要写入到哪个特征; 第三个参数是通过此响应记录是否成功写入 需要注意的是特征的属性是否支持写数据 /** 写入数据回调 */ - (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(nonnull CBCharacteristic *)characteristic error:(nullable NSError *)error { /* typedef NS_OPTIONS(NSUInteger, CBCharacteristicProperties) { CBCharacteristicPropertyBroadcast = 0x01, CBCharacteristicPropertyRead = 0x02, CBCharacteristicPropertyWriteWithoutResponse = 0x04, CBCharacteristicPropertyWrite = 0x08, CBCharacteristicPropertyNotify = 0x10, CBCharacteristicPropertyIndicate = 0x20, CBCharacteristicPropertyAuthenticatedSignedWrites = 0x40, CBCharacteristicPropertyExtendedProperties = 0x80, CBCharacteristicPropertyNotifyEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0) = 0x100, CBCharacteristicPropertyIndicateEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0) = 0x200 }; 打印出特征的权限(characteristic.properties),可以看到有很多种,这是一个NS_OPTIONS的枚举,可以是多个值 常见的又read,write,noitfy,indicate.知道这几个基本够用了,前俩是读写权限,后俩都是通知,俩不同的通知方式 */ // NSLog(@"%s, line = %d, char.pro = %d", __FUNCTION__, __LINE__, characteristic.properties); // 此时由于枚举属性是NS_OPTIONS,所以一个枚举可能对应多个类型,所以判断不能用 = ,而应该用包含& NSLog(@"write value success(写入成功) : %@", characteristic); } 10、具体调用给蓝牙外设写入数据方法,这里的例子是以按钮点击事件里面来调用处理 //发送按钮点击事件 - (void)sendClick { if (!self.characteristic) { return; } _tempValue = [NSString stringWithFormat:@"%.0f", progressView.centigradeDegree]; _timeValue = [NSString stringWithFormat:@"%.0ld", (long)progressView1.timeDegree]; NSString *ttData = [NSString stringWithFormat:@"%@,%@U", _tempValue, _timeValue]; // NSString *aaa = [DataCoverTool coverFromStringToHexStr:ttData]; // 用NSData类型来写入 // NSData *data = [NSKeyedArchiver archivedDataWithRootObject:arry]; NSData *data = [ttData dataUsingEncoding:NSUTF8StringEncoding]; // NSData *data = [self dataWithString:ttData]; // 根据上面的特征self.characteristic来写入数据 [self.peripheral writeValue:data forCharacteristic:self.characteristic type:CBCharacteristicWriteWithResponse];