博客> iOS蓝牙开发
iOS蓝牙开发
2017-10-19 14:14 评论:0 阅读:144 wzt

iOS的蓝牙数据接收以及发送

名词:Central(中心设备)、Peripheral(外围设备)、advertising(广告)、Services(服务)、Characteristic(特征) 新建Central Manager实例进行蓝牙管理 搜索外围设备 连接外围设备 获得外围设备的服务 获得服务的特征 给外围设备发送数据 从外围设备读数据 蓝牙介绍

本文要介绍的CoreBluetooth,专门用于与BLE设备通讯。并且现在很多蓝牙设备都支持4.0,4.0以其低功耗著称,所以一般也叫BLE(Bluetoothlow energy),所以也是在iOS比较推荐的一种开发方法。

CoreBluetooth介绍

在CoreBluetooth中有两个主要的部分,Central和Peripheral,CBPeripheralManager 作为周边设备。CBCentralManager作为中心设备。所有可用的iOS设备可以作为周边(Peripheral)也可以作为中央(Central),但不可以同时既是周边也是中央。

周边设备(Peripheral)设备是广播设备的数据,中央设备(Central)是管理并且使用这些数据的设备。 也就是说周边(Peripheral)向周围发送广播,告诉周围的中央设备(Central)它(周边(Peripheral)这里有数据,并且说明了能提供的服务和特征值(连接之后才能获取), 其实蓝牙传值相当于网络接口,硬件的service的UUID加上characteristic的UUID, 打一个比喻:service的UUID相当于主地址,characteristic的UUID相当于短链接,短链接必须是主地址的分支,拼在一起的是接口,你和硬件设定的蓝牙传输格式类似于json,双方可识别的数据,因为蓝牙只能支持16进制,而且每次传输只能20个字节,所以要把信息流转成双方可识别的16进制 实现方法介绍

.h 导入头文件

import

自定义设置枚举状态 typedef NS_ENUM(NSInteger, BluetoothState){ BluetoothStateDisconnect = 0, BluetoothStateScanSuccess, BluetoothStateScaning, BluetoothStateConnected, BluetoothStateConnecting };

typedef NS_ENUM(NSInteger, BluetoothFailState){ BluetoothFailStateUnExit = 0, BluetoothFailStateUnKnow, BluetoothFailStateByHW, BluetoothFailStateByOff, BluetoothFailStateUnauthorized, BluetoothFailStateByTimeout }; 设置代理

添加属性 @property (strong , nonatomic) UITableView *tableView; @property (strong , nonatomic) CBCentralManager *manager;//中央设备 @property (assign , nonatomic) BluetoothFailState bluetoothFailState; @property (assign , nonatomic) BluetoothState bluetoothState; @property (strong , nonatomic) CBPeripheral * discoveredPeripheral;//周边设备 @property (strong , nonatomic) CBCharacteristic *characteristic1;//周边设备服务特性 @property (strong , nonatomic) NSMutableArray *BleViewPerArr; 创建UITableView -(void)setTableView{ _tableView = [[UITableView alloc]initWithFrame:CGRectMake(0, 20, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height) style:UITableViewStyleGrouped]; _tableView.delegate = self; _tableView.dataSource = self; _tableView.backgroundColor = [UIColor whiteColor]; [self.view addSubview:_tableView]; } 接下来写tableView的dalegate -(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ UITableViewCell *cell = (UITableViewCell *)[tableView dequeueReusableCellWithIdentifier:@"IsConnect"]; if (cell == nil) { cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"IsConnect"]; } // 将蓝牙外设对象接出,取出name,显示 //蓝牙对象在下面环节会查找出来,被放进BleViewPerArr数组里面,是CBPeripheral对象 CBPeripheral *per=(CBPeripheral *)_BleViewPerArr[indexPath.row]; NSString *bleName=[per.name substringWithRange:NSMakeRange(0, 9)]; cell.textLabel.text = per.name; return cell; } -(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{ return 44; } -(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ return _BleViewPerArr.count; } 创建实例,设置代理,创建数组管理外设, self.manager = [[CBCentralManager alloc]initWithDelegate:self queue:dispatch_get_main_queue()]; self.manager.delegate = self; self.BleViewPerArr = [[NSMutableArray alloc]initWithCapacity:1]; 开始扫描 -(void)scan{ //判断状态开始扫瞄周围设备 第一个参数为空则会扫瞄所有的可连接设备 你可以 //指定一个CBUUID对象 从而只扫瞄注册用指定服务的设备 //scanForPeripheralsWithServices方法调用完后会调用代理CBCentralManagerDelegate的 //- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI方法 [self.manager scanForPeripheralsWithServices:nil options:@{ CBCentralManagerScanOptionAllowDuplicatesKey : @NO }]; //记录目前是扫描状态 _bluetoothState = BluetoothStateScaning; //清空所有外设数组 [self.BleViewPerArr removeAllObjects]; //如果蓝牙状态未开启,提示开启蓝牙 if(_bluetoothFailState==BluetoothFailStateByOff) { NSLog(@"%@",@"检查您的蓝牙是否开启后重试"); } } 接下来会检测蓝牙状态 - (void)centralManagerDidUpdateState:(CBCentralManager *)central { if (central.state != CBCentralManagerStatePoweredOn) { NSLog(@"fail, state is off."); switch (central.state) { case CBCentralManagerStatePoweredOff: NSLog(@"连接失败了\n请您再检查一下您的手机蓝牙是否开启,\n然后再试一次吧"); _bluetoothFailState = BluetoothFailStateByOff; break; case CBCentralManagerStateResetting: _bluetoothFailState=BluetoothFailStateByTimeout; break; case CBCentralManagerStateUnsupported: NSLog(@"检测到您的手机不支持蓝牙4.0\n所以建立不了连接.建议更换您\n的手机再试试。"); _bluetoothFailState = BluetoothFailStateByHW; break; case CBCentralManagerStateUnauthorized: NSLog(@"连接失败了\n请您再检查一下您的手机蓝牙是否开启,\n然后再试一次吧"); _bluetoothFailState = BluetoothFailStateUnauthorized; break; case CBCentralManagerStateUnknown: _bluetoothFailState = BluetoothFailStateUnKnow; break; default: break; } return; } _bluetoothFailState = BluetoothFailStateUnExit; // ... so start scanning } 中央设备开始扫描之后,我们需要实现 centralManager:didDiscoverPeripheral:advertisementData:RSSI: 通过该回调来获取发现设备。 这个回调说明着广播数据和信号质量(RSSI-Received Signal Strength Indicator)的周边设备被发现。通过信号质量,可以用判断周边设备离中央设备的远近。 - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI { if (peripheral == nil||peripheral.identifier == nil/*||peripheral.name == nil*/) { return; } NSString *pername=[NSString stringWithFormat:@"%@",peripheral.name]; //判断是否存在@"你的设备名" NSRange range=[pername rangeOfString:@"你的设备名"]; //如果从搜索到的设备中找到指定设备名,和BleViewPerArr数组没有它的地址 //加入BleViewPerArr数组 if(range.location!=NSNotFound&&[_BleViewPerArr containsObject:peripheral]==NO){ [_BleViewPerArr addObject:peripheral]; } _bluetoothFailState = BluetoothFailStateUnExit; _bluetoothState = BluetoothStateScanSuccess; [_tableView reloadData]; } 扫描设备输出台log: 蓝牙广播中可以携带一些信息,最好将mac 地址也一并在这里广播出来!!! 扫描设备advertisementData 输出台log : Printing description of advertisementData: { kCBAdvDataIsConnectable = 1; kCBAdvDataLocalName = "brand"; kCBAdvDataTxPowerLevel = 2; } 扫描到设备之后当然是链接设备了,这里会有一个UITableView,在tableview点击方法里写连接方法 -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{ CBPeripheral *peripheral=(CBPeripheral *)_BleViewPerArr[indexPath.row]; //设定周边设备,指定代理者 _discoveredPeripheral = peripheral; _discoveredPeripheral.delegate = self; //连接设备 [_manager connectPeripheral:peripheral options:@{CBConnectPeripheralOptionNotifyOnConnectionKey:@YES}]; } 说明 : 点击单元格连接对应的设备,连接该设备 调用完该方法后会调用代理- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral表示连接上了设备 连接失败会调用- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error 已经连接上该设备,就可以获取当前设备的信息 // 获取当前设备 - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral { NSLog(@"%@",peripheral); // 设置设备代理 [peripheral setDelegate:self]; // 大概获取服务和特征 [peripheral discoverServices:nil]; //或许只获取你的设备蓝牙服务的uuid数组,一个或者多个 //[peripheral discoverServices:@[[CBUUID UUIDWithString:@""],[CBUUID UUIDWithString:@""]]]; NSLog(@"Peripheral Connected"); [_manager stopScan]; NSLog(@"Scanning stopped"); _bluetoothState=BluetoothStateConnected; } 各种服务 说明:在这个方法中我们要查找到我们需要的服务 然后调用discoverCharacteristics方法查找我们需要的特性 该discoverCharacteristics方法调用完后会调用代理CBPeripheralDelegate的- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error // 获取当前设备服务services - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error { if (error) { NSLog(@"Error discovering services: %@", [error localizedDescription]); return; } NSLog(@"所有的servicesUUID%@",peripheral.services); //遍历所有service for (CBService *service in peripheral.services) { NSLog(@"服务%@",service.UUID); //找到你需要的servicesuuid if ([service.UUID isEqual:[CBUUID UUIDWithString:@"你的设备服务的uuid"]]) { //监听它 [peripheral discoverCharacteristics:nil forService:service]; } } NSLog(@"此时链接的peripheral:%@",peripheral); } 特征获取 说明:在这个方法中我们要找到我们所需的服务的特性 然后调用setNotifyValue方法告知我们要监测这个服务特性的状态变化 当setNotifyValue方法调用后调用代理CBPeripheralDelegate的- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error { if (error) { NSLog(@"Discovered characteristics for %@ with error: %@", service.UUID, [error localizedDescription]); return; } NSLog(@"服务:%@",service.UUID); // 特征 for (CBCharacteristic *characteristic in service.characteristics) { NSLog(@"%@",characteristic.UUID); //发现特征 //注意:uuid 分为可读,可写,要区别对待!!! if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"你的特征uuid"]]) { NSLog(@"监听:%@",characteristic);//监听特征 //保存characteristic特征值对象 //以后发信息也是用这个uuid _characteristic1 = characteristic; [_discoveredPeripheral setNotifyValue:YES forCharacteristic:characteristic]; } //当然,你也可以监听多个characteristic特征值对象 if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"你的特征uuid"]]) { //同样用一个变量保存,demo里面没有声明变量,要去声明 // _characteristic2 = characteristic; // [peripheral setNotifyValue:YES forCharacteristic:_characteristic2]; // NSLog(@"监听:%@",characteristic);//监听特征 } } } 读 - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { if (error) { NSLog(@"Error updating value for characteristic %@ error: %@", characteristic.UUID, [error localizedDescription]); return; } NSLog(@"收到的数据:%@",characteristic.value); } 蓝牙传值 蓝牙写到这里,基本用法已经说明,代码千变万变,思路不变,接下来介绍传值,因为我的项目是手环,我就以我们硬件工程师的蓝牙接口文档来介绍如何和硬件交互, 其实蓝牙传值相当于网络接口,硬件的service的UUID加上characteristic的UUID, 打一个比喻:service的UUID相当于主地址,characteristic的UUID相当于短链接,短链接必须是主地址的分支,拼在一起的是接口,你和硬件设定的蓝牙传输格式类似于json,双方可识别的数据,因为蓝牙只能支持16进制,而且每次传输只能20个字节,所以要把信息流转成双方可识别的16进制 下面是手环的接口文档 APP请求运动模式基础数据传输时拆分的总包数 字节序号 参数值 0 0xa5 1 0x06 2 0x03 3~4 2字节的时间,如1月2日用0x0102表示。 5 异或校验和 由此看出:0、1、2字节都是固定的,3字节是月(16进制),4字节是日(16进制),5字节是异或校验和 那么: // APP请求运动模式基础数据传输时拆分的总包数 - (NSData *)sportBao { Byte reg[6]; reg[0]=0xa5; reg[1]=0x06; reg[2]=0x03; reg[3]=0x01; reg[4]=0x02; reg[5]=(Byte)(reg[0]^reg[1]^reg[2]^reg[3]^reg[4]); NSData *data=[NSData dataWithBytes:reg length:6]; return data; } 这时候,要把请求命令发送给手环,发送给刚才纪录的discoveredPeripheral的蓝牙设备的characteristic1的特征值 // 获取总包数 - (void)getAltogether { //生成总包数data NSData *d1 = [self sportBao]; NSLog(@"写%@",d1); NSLog(@"%@",discoveredPeripheral); [discoveredPeripheral writeValue:d1 forCharacteristic:characteristic1 type:CBCharacteristicWriteWithResponse]; } 发送完成,手环会返回数据 数据是以下格式: 手环返回运动模式基础数据传输时拆分的总包数 字节序号 参数值 0 0xa5 1 0x06 2 0x83 3~4 该运动模式数据传输时拆分的总包数 5 异或校验和 由此看出:0、1、2字节都是固定的,3-4字节是总包数(16进制),5字节是异或校验和 那么:我们需要转换3-4字节的16进制,得到总包数 - (void)SetAltogether:(NSData *)DayData { Byte *testByte = (Byte *)[DayData bytes]; if (DayData.length == 6) { //收到数据之后,要异或校验,看数据是否完整以及正确 if (testByte[5]==(testByte[0]^testByte[1]^testByte[2]^testByte[3]^testByte[4])) { // 这里记录总包数 int totalBao = 0; totalBao = testByte[4]*256+testByte[3]; } } } 解析数据的格式已经有了,那么在接收数据的时候 - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{ if (error) { NSLog(@"读失败"); return; } NSLog(@"收到的数据 :%@",characteristic.value); NSString *str = [NSString stringWithFormat:@"%@",characteristic.value]; // 运动总包数 if (str.length>7&&[[str substringWithRange:NSMakeRange(1, 2)]isEqualToString:@"a5"]&&[[str substringWithRange:NSMakeRange(5, 2)]isEqualToString:@"83"]) { //调用解析总包数方法 [self SetAltogether:characteristic.value]; } } 数据传输介绍完毕,基本用法已经说明
收藏
0
sina weixin mail 回到顶部