一、首先大致介绍下蓝牙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. U3D杂记

    1,点击UI上的登录按钮,从脚本发出ioo.netmanager.SendConnet->进入CS->soketclient.sendconnet...->netmanager调用 ...

  2. Entity Framework6 with Oracle(可实现code first)

    Oracle 与2个月前刚提供对EF6的支持.以前只支持到EF5.EF6有很多有用的功能 值得升级.这里介绍下如何支持Oracle   一.Oracle 对.net支持的一些基础知识了解介绍. 1.早 ...

  3. Adblock Plus for firefox

    关于 Adblock Plus for firefox(以下简称 ABP)的一些笔记. 安装好 ABP,将如下代码保存为 html 文件,然后在 firefox 中打开: <p id=" ...

  4. html文本标准模式,首行空两格,两端对齐,行高

    font-size: 13px; line-height: 1.6; text-align: justify; text-indent: 2em;

  5. .NET MVC AjaxHelper

    我们首先必须开启 非入侵式 Ajax:导入Jquery和unobtrusiveAjax文件 已经默认开启客户端验证 和 非侵入式js <add key="ClientValidatio ...

  6. 开源免费的HTML5游戏引擎——青瓷引擎(QICI Engine) 1.0正式版发布了!

    青瓷引擎的成长 青瓷引擎自2015年4月项目启动开始,7月首次亮相2015年ChinaJoy,便得到业界的极大关注,随后开启限量测试,收到数百个开发者团队的试用申请及反馈,期间经历了18个内测版本,完 ...

  7. ASP.NET mvc异常处理的方法

    第一种:全局异常处理 1.首先常见保存异常的类(就是将异常信息写入到文件中去) public class LogManager { private string logFilePath = strin ...

  8. redis+Keepalived主从热备秒级切换

    一 简介 安装使用centos 5.10 Master 192.168.235.135 Slave 192.168.235.152 Vip 192.168.235.200 编译环境 yum -y in ...

  9. 屠龙之路_任生活如何虐你,屠龙之路还得继续_SeventhDay

    摘要 :屠龙少年在"罢工"了一天,在周末客栈补给和放纵之后,突然想起来说好的和公主私奔呢?(此处出现了为何上篇随笔不见公主)咋想之下,貌似公主还在恶龙Alpha的手中.为此,屠龙少 ...

  10. SpringMVC学习--异常处理器

    简介 springmvc在处理请求过程中出现异常信息交由异常处理器进行处理,自定义异常处理器可以实现一个系统的异常处理逻辑. 异常处理思路 系统中异常包括两类:预期异常和运行时异常RuntimeExc ...