一、首先大致介绍下蓝牙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 ;
} - (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 one
if(cell == nil) cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"cell"];
// Add a detail view accessory
BLEInfo *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 cell
return 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.properties
typedef 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]%!=) {
return nil;
}
Byte tempbyt[]={};
NSMutableData* bytes=[NSMutableData data];
for(int i=;i<[hexString length];i++)
{
unichar hex_char1 = [hexString characterAtIndex:i]; ////两位16进制数中的第一位(高位*16)
int int_ch1;
if(hex_char1 >= '' && hex_char1 <='')
int_ch1 = (hex_char1-)*; //// 0 的Ascll - 48
else if(hex_char1 >= 'A' && hex_char1 <='F')
int_ch1 = (hex_char1-)*; //// A 的Ascll - 65
else
return nil;
i++; unichar hex_char2 = [hexString characterAtIndex:i]; ///两位16进制数中的第二位(低位)
int int_ch2;
if(hex_char2 >= '' && hex_char2 <='')
int_ch2 = (hex_char2-); //// 0 的Ascll - 48
else if(hex_char2 >= 'A' && hex_char2 <='F')
int_ch2 = hex_char2-; //// A 的Ascll - 65
else
return nil; tempbyt[] = int_ch1+int_ch2; ///将转化后的数放入Byte数组里
[bytes appendBytes:tempbyt length:];
}
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 * )];
for(int i = ; 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:@""];
}else{
[self writeToPeripheral:@""]; }
} @end

完整代码地址:https://github.com/rayshen/ShenBLETest

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

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

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

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

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

[iOS 基于CoreBluetooth的蓝牙4.0通讯]的更多相关文章

  1. CoreBluetooth——IOS蓝牙4.0使用心得

    原文链接:http://m.blog.csdn.net/article/details?plg_nld=1&id=51014318&plg_auth=1&plg_uin=1&a ...

  2. IOS学习之蓝牙4.0 BLE

    IOS学习也一段时间了,该上点干货了.前段时间研究了一下IOS蓝牙通讯相关的东西,把研究的一个成果给大家分享一下. 一 项目背景 简单介绍一下做的东西,设备是一个金融刷卡器,通过蓝牙与iphone手机 ...

  3. https://github.com/coolnameismy/BabyBluetooth github上的一个ios 蓝牙4.0的库并带文档和教程

    The easiest way to use Bluetooth (BLE )in ios,even bady can use. 简单易用的蓝牙库,基于CoreBluetooth的封装,并兼容ios和 ...

  4. IOS BLE蓝牙4.0

    前言: 自己做的项目里面有这么一个功能,总结归纳一下. 先导入必要的框架  CoreBluetooth.framework 在要用到蓝牙的文件里面导入以下头文件 #import <CoreBlu ...

  5. iOS蓝牙4.0

    iOS的蓝牙用到了  CoreBluetooth 框架 首先导入框架 #import <CoreBluetooth/CoreBluetooth.h> 我们需要一个管理者来管理蓝牙设备,CB ...

  6. iOS 蓝牙4.0开发

    背景: 1.iOS的蓝牙不能用来传输文件.2.iOS与iOS设备之间进行数据通信,使用gameKit.framework3.iOS与其他非iOS设备进行数据通信,使用coreBluetooth.fra ...

  7. iOS蓝牙4.0协议简单介绍

    iOS开发蓝牙4.0的框架是CoreBluetooth,本文主要介绍CoreBluetooth的使用,关于本文中的代码片段大多来自github上的一个demo,地址是myz1104/Bluetooth ...

  8. iOS蓝牙BLE4.0通信功能

    概述 iOS蓝牙BLE4.0通信功能,最近刚学的苹果,为了实现蓝牙门锁的项目,找了一天学习了下蓝牙的原理,亲手测试了一次蓝牙的通信功能,结果成功了,那么就把我学习的东西分享一下. 详细 代码下载:ht ...

  9. iOS蓝牙4.0开发

    文/starfox寒流(简书作者)原文链接:http://www.jianshu.com/p/974d165f78b5著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”. iOS 蓝牙4.0 ...

随机推荐

  1. SQL Server 用SSMS查看依赖关系有时候不准确,改用代码查

    SQL Server 用SSMS查看依赖关系有时候不准确,明明某个sp中有用到表tohen,查看表tohen的依赖关系的时候,却看不到这个sp 用代码查看方式如下: --依赖于表tohen的对象 SE ...

  2. ubuntu-12.10-server中打开终端的方式

    ubuntu-12.10-server系统在图形界面的任务栏上找不到终端的踪影,可以使用以下两种方式调出 1.在图形界面中点击Dash Home 点击后搜索terminal即可 2.可以通过快捷键CT ...

  3. 安装Ubuntu时的硬盘分区方案

    如果你准备在硬盘里只安装Ubuntu一个操作系统的话,建议你采用一个“/”.一个“swap”和一个“/home”的三分区方案:/ :10GB-15GB.swap:物理内存小于或等于 512MB,建议分 ...

  4. 如何拿到半数面试公司Offer——我的Python求职之路

    从八月底开始找工作,短短的一星期多一些,面试了9家公司,拿到5份Offer,可能是因为我所面试的公司都是些创业性的公司吧,不过还是感触良多,因为学习Python的时间还很短,没想到还算比较容易的找到了 ...

  5. 如何禁止IE自动生成链接

    今天一位园友反馈,他用的是IE浏览器,在博客后台编辑器中输入下面的URL: http://www.windowsphone.com/zh-cn/store/app/博客园-uap/500f08f0-5 ...

  6. nios II--实验5——定时器软件部分

    软件开发 首先,在硬件工程文件夹里面新建一个software的文件夹用于放置软件部分:打开toolsàNios II 11.0 Software Build Tools for Eclipse,需要进 ...

  7. jaccard similarity coefficient 相似度计算

    Jaccard index From Wikipedia, the free encyclopedia     The Jaccard index, also known as the Jaccard ...

  8. 清空KindEditor富文本编辑器里面的内容方法

    //清空KindEditorKindEditor.instances[0].html(""); 0表示第一个KindEditor编辑器对象 详情见链接:http://www.new ...

  9. hdu5481 Desiderium

    链接 Desiderium 题意 给定n条线段,从中选取若干条,共有2n种选法(因为每一条线段有两种方法:选或者不选). 每一种选法都对应一个长度,也就是所选线段的并集长度. 求这2n种选法长度之和. ...

  10. Java网络编程——UDP实例

    UDPSendDemo import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamRea ...