转载自:http://www.cnblogs.com/zyjzyj/p/6029968.html ,感谢英杰

前言

最近一直在开发关于蓝牙的功能,本来是不想写这一篇文章,因为网上关于ios蓝牙开发的文章实在太多了,成吨成吨的文章出现,但是很遗憾都只是一些皮毛,或者只是简单的介绍一下基本概念而已,对于一些小白可能还有很多很多疑惑,所以萌生了写一篇文章,并附上实际例子的demo,供即将项目中准备开发的伙伴参考。

正文

首先

我们得明确一下很重要的几个概念
1.当前ios中开发蓝牙所运用的系统库是<CoreBluetooth/CoreBluetooth.h>
2.蓝牙外设必须为4.0及以上,否则无法开发,蓝牙4.0设备因为低耗电,所以也叫做BLE。
3.CoreBluetooth框架的核心其实是两个东西,peripheral和central, 可以理解成外设和中心,就是你的苹果手机就是中心,外部蓝牙称为外设。
4.服务和特征(service
and
characteristic):简而言之,外部蓝牙中它有若干个服务service(服务你可以理解为蓝牙所拥有的能力),而每个服务service下拥有若干个特征characteristic(特征你可以理解为解释这个服务的属性)。
5.Descriptor(描述)用来描述characteristic变量的属性。例如,一个descriptor可以规定一个可读的描述,或者一个characteristic变量可接受的范围,或者一个characteristic变量特定的单位。
6.跟硬件亲测,Ios蓝牙每次最多接收155字节的数据,安卓5.0以下最大接收20字节,5.0以上可以更改最大接收量,能达到500多字节。

其次

通过以上关键信息的解释,然后看一下蓝牙的开发流程:

  1. 建立中心管理者
  2. 扫描外设(discover)
  3. 连接外设(connect)
  4. 扫描外设中的服务和特征(discover)
    4.1 获取外设的services
    4.2 获取外设的Characteristics,获取Characteristics的值,
    获取Characteristics的Descriptor和Descriptor的值
  5. 与外设做数据交互(explore and interact)
  6. 断开连接(disconnect)

具体代码

1.创建一个中心管理者

//只要一触发这句代码系统会自动检测手机蓝牙状态,你必须实现其代理方法,当然得添加<CBCentralManagerDelegate>
CBCentralManager *theManager = [[CBCentralManager alloc]initWithDelegate:self queue:nil]; //从这个代理方法中你可以看到所有的状态,其实我们需要的只有on和off连个状态
- (void)centralManagerDidUpdateState:(CBCentralManager *)central{
switch (central.state) {
case CBCentralManagerStateUnknown:
NSLog(@">>>CBCentralManagerStateUnknown");
break;
case CBCentralManagerStateResetting:
NSLog(@">>>CBCentralManagerStateResetting");
break;
case CBCentralManagerStateUnsupported:
NSLog(@">>>CBCentralManagerStateUnsupported");
break;
case CBCentralManagerStateUnauthorized:
NSLog(@">>>CBCentralManagerStateUnauthorized");
break;
case CBCentralManagerStatePoweredOff:
NSLog(@">>>CBCentralManagerStatePoweredOff");
break;
case CBCentralManagerStatePoweredOn:
NSLog(@">>>CBCentralManagerStatePoweredOn");
break;
default:
break;
}

当发现蓝牙状态是开启状态,你就可以利用中央设备进行扫描外设,如果为关闭状态,系统会自动弹出让用户去设置蓝牙,这个不需要我们开发者关心。

2.利用中心去扫描外设

//两个参数为nil,默认扫描所有的外设,可以设置一些服务,进行过滤搜索
[theManager scanForPeripheralsWithServices:nil options:nil];

2.1当扫描到外设,触发以下代理方法

在这里需要说明的是,
一.当扫描到外设,我们可以读到相应外设广播信息,RSSI信号强度(可以利用RSSI计算中心和外设的距离)。
二.我们可以根据一定的规则进行连接,一般是默认名字或者名字和信号强度的规则来连接。
三.像我现在做的无钥匙启动车辆锁定车辆,就需要加密通讯,不能谁来连接都可以操作车辆,需要和外设进行加密通讯,但是一切的通过算法的校验都是在和外设连接上的基础上进行,例如连接上了,你发送一种和硬件约定好的算法数据,硬件接收到校验通过了就正常操作,无法通过则由硬件(外设)主动断开。

//这里默认扫到MI,主动连接,当然也可以手动触发连接
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{
NSLog(@"扫描连接外设:%@ %@",peripheral.name,RSSI); if ([peripheral.name hasSuffix:@"MI"]) {
//保存外设,并停止扫描,达到节电效果
thePerpher = peripheral;
[central stopScan];
//进行连接
[central connectPeripheral:peripheral options:nil];
} }

3.当连接到外设,会调用以下代理方法

这里需要说明的是
当成功连接到外设,需要设置外设的代理,为了扫描服务调用相应代理方法

- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{

    NSLog(@"连接外设成功!%@",peripheral.name);
[peripheral setDelegate:self];
[peripheral discoverServices:nil];
} //连接外设失败
-(void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
NSLog(@"连接到外设 失败!%@ %@",[peripheral name],[error localizedDescription]);
}

4.扫描外设中的服务和特征

//扫描到服务
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
if (error)
{
NSLog(@"扫描外设服务出错:%@-> %@", peripheral.name, [error localizedDescription]);
return;
}
NSLog(@"扫描到外设服务:%@ -> %@",peripheral.name,peripheral.services);
for (CBService *service in peripheral.services) {
[peripheral discoverCharacteristics:nil forService:service];
}
NSLog(@"开始扫描外设服务的特征 %@...",peripheral.name); }
//扫描到特征
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{
if (error)
{
NSLog(@"扫描外设的特征失败!%@->%@-> %@",peripheral.name,service.UUID, [error localizedDescription]);
return;
} NSLog(@"扫描到外设服务特征有:%@->%@->%@",peripheral.name,service.UUID,service.characteristics);
//获取Characteristic的值
for (CBCharacteristic *characteristic in service.characteristics){ //这里外设需要订阅特征的通知,否则无法收到外设发送过来的数据
[peripheral setNotifyValue:YES forCharacteristic:characteristic]; //这里以小米手环为例,当我们定义好每个特征是干什么用的,我们需要读取这个特征的值,当特征值更新了会调用
//- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error方法
//需要说明的是UUID是硬件定义好给你,如果硬件也是个新手,那你可以先打印出所有的UUID,找出有用的
//步数
if ([characteristic.UUID.UUIDString isEqualToString:@"FF06"])
{
[peripheral readValueForCharacteristic:characteristic];
} //电池电量
else if ([characteristic.UUID.UUIDString isEqualToString:@"FF0C"])
{
[peripheral readValueForCharacteristic:characteristic];
} else if ([characteristic.UUID.UUIDString isEqualToString:@"2A06"])
{
//震动
theSakeCC = characteristic;
} }
} } //扫描到具体的值->通讯主要的获取数据的方法
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error
{
if (error) {
NSLog(@"扫描外设的特征失败!%@-> %@",peripheral.name, [error localizedDescription]);
return;
}
NSLog(@"%@ %@",characteristic.UUID.UUIDString,characteristic.value);
if ([characteristic.UUID.UUIDString isEqualToString:@"FF06"]) {
Byte *steBytes = (Byte *)characteristic.value.bytes;
int steps = bytesValueToInt(steBytes); }
else if ([characteristic.UUID.UUIDString isEqualToString: @"FF0C"])
{
Byte *bufferBytes = (Byte *)characteristic.value.bytes;
int buterys = bytesValueToInt(bufferBytes)&0xff;
NSLog(@"电池:%d%%",buterys); }
else if ([characteristic.UUID.UUIDString isEqualToString:@"2A06"])
{
Byte *infoByts = (Byte *)characteristic.value.bytes;
//这里解析infoByts得到设备信息 } }

5.与外设做数据交互

需要说明的是苹果官方提供发送数据的方法很简单,只需要调用下面的方法

- (void)writeValue:(NSData *)data forCharacteristic:(CBCharacteristic *)characteristic type:(CBCharacteristicWriteType)type

我们只需要在搜索每个服务的特征,记录这个特征,然后向这个特征发送数据就可以了。

6.断开连接

调用以下代码,需要说明的是中心断开与外设的连接。

- (void)cancelPeripheralConnection:(CBPeripheral *)peripheral;

以上呢是整个蓝牙的开发过程,系统提供的框架api就这么多,开发起来也不是很难,要是你认为这篇文章到这里就结束了,你就大错特错了,这篇文章的精华内容将从这里开始,由于公司项目的保密性,我不能以它为例,那我就以小米手环为实例,主要分享一下数据解析。

精华部分

1.当调用了以下代理方法的时候,我们需要处理接收到的数据

- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error

小米手环所定义的几个UUID如下:

@"FF06" 这个UUID定义的是步数
@"FF0C" 这个UUID定义的是电量
@"2A06"这个UUID定义的是震动
@"FF01"这个UUID定义的是相关的设备信息

通过以上的UUID,我们可以读取到步数,电量,操作手环震动,并读取手环相应设备的信息,这里需要说明的是我并不清楚设备信息的具体协议,所以这里没法解析。

if ([characteristic.UUID.UUIDString isEqualToString:STEP]) {
Byte *steBytes = (Byte *)characteristic.value.bytes;
int steps = bytesValueToInt(steBytes);
NSLog(@"步数:%d",steps);
}

当我们读到步数这个UUID时,我们可以拿到value,小米手环所定义的协议是4个字节,我们需要将4个字节转换为int 类型即可
方法如下

//4个字节Bytes 转 int
unsigned int bytesValueToInt(Byte *bytesValue) { unsigned int intV;
intV = (unsigned int ) ( ((bytesValue[3] & 0xff)<<24)
|((bytesValue[2] & 0xff)<<16)
|((bytesValue[1] & 0xff)<<8)
|(bytesValue[0] & 0xff));
return intV;
}

需要说明的是这个方法是C语言的方法,采用位与运算,当然如果项目中需要另一种方式的转换,如:发过来两字节需要你转换为int,如果你不会转换,可以去网上搜索,我会在文章后附一些常用的转换方法。
这里重点说明的是步数读取,剩余类似。

2.当我们给外设发送数据时,我们需要跟硬件定协议,当然这是在开始项目之前做好的事情。

小米手环协议中震动命令的触发,是向硬件发送一个10进制的 2
这里需要说明的是我们发送数据给硬件一般是字节数组,然后将他转换为NSData发送。

      //这里为了严谨起见,需要判断外设和特征是否存在,如果存在发送数据
if (thePerpher && theSakeCC) {
Byte zd[1] = {2};
NSData *theData = [NSData dataWithBytes:zd length:1];
[thePerpher writeValue:theData forCharacteristic:theSakeCC type:CBCharacteristicWriteWithoutResponse]; }

这里需要再添加一点,如果协议要求你发ASCII码,例如‘SHAKE’,只需要这么处理

 if (thePerpher && theSakeCC) {
Byte zd[] = {'S','H','A','K','E'};
NSData *theData = [NSData dataWithBytes:zd length:1];
[thePerpher writeValue:theData forCharacteristic:theSakeCC type:CBCharacteristicWriteWithoutResponse]; }

3.项目中实际开发的运用
当我们面对实际开发时,我们不可能这么直接去在一个控制器中去写这么多代码,如果你说这没多少啊,那我无话可说了

iOS开发之--蓝牙开发实战的更多相关文章

  1. iOS 蓝牙开发详解

    目前iOS智能硬件的开发交互方式主要分为两种,一种是基于低功耗的蓝牙4.0技术(由于耗电低,也称作为BLE(Bluetooth Low Energy))对应iOS的框架为CoreBluetooth,另 ...

  2. iOS蓝牙开发(二)蓝牙相关基础知识

    原文链接: http://liuyanwei.jumppo.com/2015/07/17/ios-BLE-1.html iOS蓝牙开发(一)蓝牙相关基础知识: 蓝牙常见名称和缩写 MFI ====== ...

  3. iOS开发之蓝牙通讯

    iOS开发之蓝牙通讯 一.引言 蓝牙是设备近距离通信的一种方便手段,在iPhone引入蓝牙4.0后,设备之间的通讯变得更加简单.相关的蓝牙操作由专门的CoreBluetooth.framework进行 ...

  4. iOS 蓝牙开发资料记录

    一.蓝牙基础认识:   1.iOS蓝牙开发:  iOS蓝牙开发:蓝牙连接和数据读写   iOS蓝牙后台运行  iOS关于app连接已配对设备的问题(ancs协议的锅)          iOS蓝牙空中 ...

  5. iOS蓝牙开发(4.0)详解

    最近由于项目需要, 一直在研究蓝牙4.0,在这儿分享给大家, 望共同进步. 一.关于蓝牙开发的一些重要的理论概念: 1.当前ios中开发蓝牙所运用的系统库是<CoreBluetooth/Core ...

  6. iOS之蓝牙开发—CoreBluetooth详解

    CoreBluetooth的API是基于BLE4.0的标准的.这个框架涵盖了BLE标准的所有细节.仅仅只有新的iOS设备和Mac是和BLE标准兼容.在CoreBluetooth框架中,有两个主要的角色 ...

  7. iOS cocos2d 2游戏开发实战(第3版)书评

    2013是游戏爆发的一年,手游用户也是飞速暴增.虽然自己不做游戏,但也是时刻了解手机应用开发的新动向.看到CSDN的"写书评得技术图书赢下载分"活动,就申请了一本<iOS c ...

  8. iOS,蓝牙开发!!--By帮雷

    iOS的蓝牙开发大致有以下几种方式. 1 GameKit.framework [只能存在于iOS设备之间,多用于游戏 能搜索到的demo比较多,不确切说名字了,code4app里面就有] 2 Core ...

  9. iOS蓝牙开发总结-4

    蓝牙开发总结 只要熟悉蓝牙的流程,和蓝牙中每一个角色的作用,其实蓝牙通讯并没有想象中的难 1.蓝牙中心CBCentralManager:一般指得是iPhone手机 2.设备(外设)CBPeripher ...

随机推荐

  1. Intellij IDEA 10.5 语言设置

    适应于:英文操作系统,但是语言和区域设置为中文的环境. Mac: /Applications/IntelliJ IDEA CE.app/Contents/bin/idea.vmoptions 增加 - ...

  2. VBA验证工作表是否存在

    使用VBA验证工作表是否存在 ============================================================= 代码区域 ================== ...

  3. PHP-PHP程序员的技术成长规划(By黑夜路人)

    按照了解的很多PHP/LNMP程序员的发展轨迹,结合个人经验体会,抽象出很多程序员对未来的迷漫,特别对技术学习的盲目和慌乱,简单梳理了这个每个阶段PHP程序员的技术要求,来帮助很多PHP程序做对照设定 ...

  4. 用GDB命令PO(print-object)打印UIView的视图层级

    UIView有一个私有方法: recursiveDescription 这个方法可以显示出当前视图的详细层级,可以在代码中直接调用,也可以在GDB中调用,在GDB中调用时需要借助另一个GDB命令:pr ...

  5. 【HTML+CSS】(2)CSS Sprite雪碧图

    1. 雪碧图的使用场景 (1). 静态图片.不随用户信息的变化而变化 (2). 小图片.图片容量比較小 (3). 载入量比較大 一些大图不建议拼成雪碧图,比如淘宝站点的导航图片都是使用的雪碧图. 2. ...

  6. xml布局内容总结(三)--Android

    关于xml中经经常使用到边框及边框效果,在此进行一下总结. 3.border(边框及边框效果) (1)直角边框线 <LinearLayout         android:layout_wid ...

  7. List与String的相互转换

    List转字符串,用逗号隔开 List<string> list = new List<string>(); list.Add("a"); list.Add ...

  8. Groovy小结:java调用Groovy方法并传递参数

    Groovy小结:java调用Groovy方法并传递参数 @(JAVA总结) 1. 场景描述 在网上查了资料发现,java有三种方式调用groovy脚本.但是真正在实际的服务器环境中,嵌入groovy ...

  9. PHP REST架构简单设计

    REST是什么? REST(Representational State Transfer表述性状态转移)是一种针对网络应用的设计和开发方式,可以降低开发的复杂性,提高系统的可伸缩性. REST的特点 ...

  10. Apollo配置中心转

    尊重原创,本文转自:https://www.cnblogs.com/FlyAway2013/p/8811385.html 前我们项目,所有的配置基本都是通过本地properties 文件进行配置的,比 ...