[iOS 基于CoreBluetooth的蓝牙4.0通讯]

编程入门 行业动态 更新时间:2024-10-24 12:22:21

[iOS 基于CoreBluetooth的<a href=https://www.elefans.com/category/jswz/34/1768306.html style=蓝牙4.0通讯]"/>

[iOS 基于CoreBluetooth的蓝牙4.0通讯]

一、首先大致介绍下蓝牙4.0的模式,中心和周边:

一般情况下,iPhone作为中心,接收来自周边传感器(比如手环等)采集的数据。

二、那整一个数据通讯的协议是怎样的呢?

为什么要一层层搞这么复杂呢?据我的理解是这样的:

  (1)蓝牙2.0的通讯非常简单,只有数据接收和发送,这样产生的问题就是:假如我有2个传感器的数据,但传输通道就一个,就发送时必须自己切割字符串等。

      但4.0根据不同的功能,有点像传输分了很多“通道”,比如传感器1传输温度,服务的UUID是FFF0,然后特征字节发送的UUID为FFF1;传感器2传输距离,服务的UUID也是FFF0,但是特征字节发送的UUID为FFF2,这样就可以各取所需了,而不是蓝牙2.0那样一股脑儿收进来再切割。

  (2)蓝牙4.0的每个“通道”都可以定义为发送或者接收字节,所以可以把发送和接收区分开。

  补充:一般情况下,服务(Service)的UUID根据功能来区分(假如有FFF0和FFFE0两种服务),比如FFF0的服务ID里的特征字节UUID通道用来作为传感器通讯,FFE0里放蓝牙设备的信息,名称啊电池啊等等。。

三、代码:

1.BLEInfo.h(存放周边蓝牙设备的信息)

#import <Foundation/Foundation.h>
#import <CoreBluetooth/CoreBluetooth.h>
@interface BLEInfo : NSObject@property (nonatomic, strong) CBPeripheral *discoveredPeripheral;
@property (nonatomic, strong) NSNumber *rssi;@end

  BLEInfo.m

#import "BLEInfo.h"@implementation BLEInfo@end

 

2.RootTableViewController.h(显示周边蓝牙信息,主要用scan函数就行了)

#import <UIKit/UIKit.h>
#import <CoreBluetooth/CoreBluetooth.h>
#import "BLEInfo.h"
#import "DetailViewController.h"
@interface RootTableViewController : UITableViewController<CBCentralManagerDelegate>@property (nonatomic, strong) CBCentralManager *centralMgr;
@property (nonatomic, strong) NSMutableArray *arrayBLE;@end

  RootTableViewController.m

#import "RootTableViewController.h"@interface RootTableViewController ()@end@implementation RootTableViewController- (void)viewDidLoad {[super viewDidLoad];self.navigationItem.title=@"蓝牙搜索";self.centralMgr = [[CBCentralManager alloc] initWithDelegate:self queue:nil];self.arrayBLE = [[NSMutableArray alloc] init];
}- (void)didReceiveMemoryWarning {[super didReceiveMemoryWarning];// Dispose of any resources that can be recreated.
}//蓝牙状态delegate
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{switch (central.state){case CBCentralManagerStatePoweredOn:[self.centralMgr scanForPeripheralsWithServices:nil options:nil];NSLog(@"start scan Peripherals");break;default:NSLog(@"Central Manager did change state");break;}
}//发现设备delegate
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{BLEInfo *discoveredBLEInfo = [[BLEInfo alloc] init];discoveredBLEInfo.discoveredPeripheral = peripheral;discoveredBLEInfo.rssi = RSSI;// update tableview
    [self saveBLE:discoveredBLEInfo];}//保存设备信息
- (BOOL)saveBLE:(BLEInfo *)discoveredBLEInfo
{for (BLEInfo *info in self.arrayBLE){if ([info.discoveredPeripheral.identifier.UUIDString isEqualToString:discoveredBLEInfo.discoveredPeripheral.identifier.UUIDString]){return NO;}}NSLog(@"\nDiscover New Devices!\n");NSLog(@"BLEInfo\n UUID:%@\n RSSI:%@\n\n",discoveredBLEInfo.discoveredPeripheral.identifier.UUIDString,discoveredBLEInfo.rssi);[self.arrayBLE addObject:discoveredBLEInfo];[self.tableView reloadData];return YES;
}#pragma mark - Table view data source- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {// Return the number of sections.return 1;
}- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {// Return the number of rows in the section.return _arrayBLE.count;
}- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:@"cell"];// Step 2: If there are no cells to reuse, create a new oneif(cell == nil) cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"cell"];// Add a detail view accessoryBLEInfo *thisBLEInfo=[self.arrayBLE objectAtIndex:indexPath.row];cell.textLabel.text=[NSString stringWithFormat:@"%@ %@",thisBLEInfo.discoveredPeripheral.name,thisBLEInfo.rssi];cell.detailTextLabel.text=[NSString stringWithFormat:@"UUID:%@",thisBLEInfo.discoveredPeripheral.identifier.UUIDString];// Step 3: Set the cell text// Step 4: Return the cellreturn cell;
}- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{BLEInfo *thisBLEInfo=[self.arrayBLE objectAtIndex:indexPath.row];DetailViewController* dtvc=[self.storyboard instantiateViewControllerWithIdentifier:@"DetailViewController"];dtvc.centralMgr=self.centralMgr;dtvc.discoveredPeripheral=thisBLEInfo.discoveredPeripheral;[self.navigationController pushViewController:dtvc animated:YES];}
@end

 

3.DetailViewController.h(连接具体的蓝牙,发现其内部信息)

#import <UIKit/UIKit.h>
#import <CoreBluetooth/CoreBluetooth.h>@interface DetailViewController : UIViewController<
CBPeripheralManagerDelegate,
CBCentralManagerDelegate,
CBPeripheralDelegate
>@property (nonatomic, strong) CBCentralManager *centralMgr;
@property (nonatomic, strong) CBPeripheral *discoveredPeripheral;
@property (strong, nonatomic) CBCharacteristic* writeCharacteristic;@property int current_humitidy;
@property int current_temperature;- (IBAction)led1control:(id)sender;@end

DetailViewController.m

#import "DetailViewController.h"@interface DetailViewController ()@end@implementation DetailViewController#define SECTION_NAME @"Serviceinfo"- (void)viewDidLoad
{[super viewDidLoad];[_centralMgr setDelegate:self];if (_discoveredPeripheral){NSLog(@"connectPeripheral");[_centralMgr connectPeripheral:_discoveredPeripheral options:nil];}
}//界面退出
-(void)viewWillDisappear:(BOOL)animated{[self.centralMgr cancelPeripheralConnection:_discoveredPeripheral];
}- (void)didReceiveMemoryWarning {[super didReceiveMemoryWarning];// Dispose of any resources that can be recreated.
}/*
//========================================================================================
//0.假设蓝牙关闭、掉线什么的,重新搜索
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{switch (central.state){case CBCentralManagerStatePoweredOn://[self.centralMgr scanForPeripheralsWithServices:nil options:nil];NSLog(@"start scan Peripherals");break;default:NSLog(@"Central Manager did change state");break;}
}//1.搜索后重连
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{//[_centralMgr connectPeripheral:_discoveredPeripheral options:nil];
}
//========================================================================================
*///2.连接的Delegate 连接若成功则搜索服务
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{NSLog(@"didFailToConnectPeripheral : %@", error.localizedDescription);
}- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{[_discoveredPeripheral setDelegate:self];//查找服务
    [_discoveredPeripheral discoverServices:nil];
}//========================================================================================
//3.搜索服务的Delegate 若发现服务,然后搜索其内的特征服务- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{if (error){NSLog(@"didDiscoverServices : %@", [error localizedDescription]);//        [self cleanup];return;}for (CBService *s in peripheral.services){NSLog(@"\n>>>服务UUID found with UUID : %@ des:%@", s.UUID,s.UUID.description);//查找特征字节
        [s.peripheral discoverCharacteristics:nil forService:s];}
}
//========================================================================================
//4.搜索特征的Delegate 若发现特征,则看看这个“通道是发送的还是接收”,接收就read,发送就把这个writeCharacteristic记录下
//注意:不是所有的特性值都是可读的(readable)。通过访问 CBCharacteristicPropertyRead 可以知道特性值是否可读。如果一个特性的值不可读,使用 peripheral:didUpdateValueForCharacteristic:error:就会返回一个错误。
//Subscribing to a Characteristic’s Value(订制一个特性值) 尽管通过 readValueForCharacteristic:方法能够得到特性值,但是对于一个变化的特性值就不是很 有效了。大多数的特性值是变化的,比如一个心率监测应用,如果需要得到特性值,就需要 通过预定的方法获得。当预定了一个特性值,当值改变时,就会收到设备发出的通知。/*特征值的属性:c.propertiestypedef NS_OPTIONS(NSInteger, CBCharacteristicProperties) {// 标识这个characteristic的属性是广播CBCharacteristicPropertyBroadcast= 0x01,// 标识这个characteristic的属性是读CBCharacteristicPropertyRead= 0x02,// 标识这个characteristic的属性是写-没有响应CBCharacteristicPropertyWriteWithoutResponse= 0x04,// 标识这个characteristic的属性是写CBCharacteristicPropertyWrite= 0x08,// 标识这个characteristic的属性是通知CBCharacteristicPropertyNotify= 0x10,// 标识这个characteristic的属性是声明CBCharacteristicPropertyIndicate= 0x20,// 标识这个characteristic的属性是通过验证的CBCharacteristicPropertyAuthenticatedSignedWrites= 0x40,// 标识这个characteristic的属性是拓展CBCharacteristicPropertyExtendedProperties= 0x80,// 标识这个characteristic的属性是需要加密的通知CBCharacteristicPropertyNotifyEncryptionRequiredNS_ENUM_AVAILABLE(NA, 6_0)= 0x100,// 标识这个characteristic的属性是需要加密的申明CBCharacteristicPropertyIndicateEncryptionRequiredNS_ENUM_AVAILABLE(NA, 6_0)= 0x200};*/
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{if (error){NSLog(@"didDiscoverCharacteristicsForService error : %@", [error localizedDescription]);return;}for (CBCharacteristic *c in service.characteristics){NSLog(@"\n>>>\t特征UUID FOUND(in 服务UUID:%@): %@ (data:%@)",service.UUID.description,c.UUID,c.UUID.data);/*根据特征不同属性去读取或者写if (c.properties==CBCharacteristicPropertyRead) {}if (c.properties==CBCharacteristicPropertyWrite) {}if (c.properties==CBCharacteristicPropertyNotify) {}*///假如你和硬件商量好了,某个UUID时写,某个读的,那就不用判断啦/*if([c.UUID isEqual:[CBUUID UUIDWithString:@"FFF1"]]){self.writeCharacteristic = c;}if([c.UUID isEqual:[CBUUID UUIDWithString:@"FFF6"]]){[peripheral readValueForCharacteristic:c];}*/if (c.properties==CBCharacteristicPropertyRead) {[peripheral readValueForCharacteristic:c];}}
}
//========================================================================================- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{if (error){NSLog(@"didUpdateValueForCharacteristic error : %@", error.localizedDescription);return;}NSLog(@"\nFindtheValueis (UUID:%@):%@ ",characteristic.UUID,characteristic.value);/*if([characteristic.UUID.description isEqualToString:@"FFF6"]){//我这里采用的是16进制数据,如<34001a00>,3400代表湿度十进制52.0,1a00代表温度26.0//当然你也可以有自己定义传输字符的意义NSData *datavalue=characteristic.value;NSData *shidudata=[datavalue subdataWithRange:NSMakeRange(0, 1)];NSData *wendudata=[datavalue subdataWithRange:NSMakeRange(2, 1)];NSLog(@"\nFind---theValueis:%@   %@-%@",characteristic.value,shidudata,wendudata);int i=0,j=0;[shidudata getBytes: &i length: sizeof(i)];[wendudata getBytes: &j length: sizeof(j)];self.current_humitidy=i;self.current_temperature=j;NSLog(@"\n室内温度为:%d℃,室内湿度为:%d%%",_current_temperature,_current_humitidy);}*/
}- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {if (error) {NSLog(@"Error changing notification state: %@", [error localizedDescription]);}
}//向peripheral中写入数据后的回调函数
- (void)peripheral:(CBPeripheral*)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{NSLog(@"write value success : %@", characteristic);
}//自定义的写入数据的函数
- (void)writeToPeripheral:(NSString *)string{if(!_writeCharacteristic){NSLog(@"writeCharacteristic is nil!");return;}NSData* value = [self stringToByte:string];NSLog(@"Witedata: %@",value);[_discoveredPeripheral writeValue:value forCharacteristic:_writeCharacteristic type:CBCharacteristicWriteWithResponse];
}//====================================================================================================
//一些转换函数,本例中只用到stringToByte
-(NSData*)stringToByte:(NSString*)string
{NSString *hexString=[[string uppercaseString] stringByReplacingOccurrencesOfString:@" " withString:@""];if ([hexString length]%2!=0) {return nil;}Byte tempbyt[1]={0};NSMutableData* bytes=[NSMutableData data];for(int i=0;i<[hexString length];i++){unichar hex_char1 = [hexString characterAtIndex:i]; ////两位16进制数中的第一位(高位*16)int int_ch1;if(hex_char1 >= '0' && hex_char1 <='9')int_ch1 = (hex_char1-48)*16;   //// 0 的Ascll - 48else if(hex_char1 >= 'A' && hex_char1 <='F')int_ch1 = (hex_char1-55)*16; //// A 的Ascll - 65elsereturn nil;i++;unichar hex_char2 = [hexString characterAtIndex:i]; ///两位16进制数中的第二位(低位)int int_ch2;if(hex_char2 >= '0' && hex_char2 <='9')int_ch2 = (hex_char2-48); //// 0 的Ascll - 48else if(hex_char2 >= 'A' && hex_char2 <='F')int_ch2 = hex_char2-55; //// A 的Ascll - 65elsereturn nil;tempbyt[0] = int_ch1+int_ch2;  ///将转化后的数放入Byte数组里[bytes appendBytes:tempbyt length:1];}return bytes;
}//NSData类型转换成NSString
- (NSString*)hexadecimalString:(NSData *)data{NSString* result;const unsigned char* dataBuffer = (const unsigned char*)[data bytes];if(!dataBuffer){return nil;}NSUInteger dataLength = [data length];NSMutableString* hexString = [NSMutableString stringWithCapacity:(dataLength * 2)];for(int i = 0; i < dataLength; i++){[hexString appendString:[NSString stringWithFormat:@"%02lx", (unsigned long)dataBuffer[i]]];}result = [NSString stringWithString:hexString];return result;
}
//====================================================================================================//假设有个swt1,开一下发送一个开关信号,控制LED灯
- (IBAction)led1control:(id)sender {UISwitch *swt1= (UISwitch*)sender;if (swt1.on) {[self writeToPeripheral:@"11"];}else{[self writeToPeripheral:@"10"];}
}@end

 

完整代码地址:

四、示例:基于蓝牙4.0的温湿度采集控制器

这是我自己做的湿度和温度的收集器和控制器。

iPhone收集来自单片机(蓝牙CC2541芯片)的温度和湿度信息,并且可以控制LED灯和继电器。

继电器就是一个开关,外围可以连接其他带电源和电器的大电路。

 

欢迎大家和我交流,我的微博是:weibo/rayshen1012

转载于:.html

更多推荐

[iOS 基于CoreBluetooth的蓝牙4.0通讯]

本文发布于:2024-02-11 22:43:25,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1684017.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:蓝牙   通讯   iOS   CoreBluetooth

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!