最近由于项目需要, 一直在研究蓝牙4.0,在这儿分享给大家, 望共同进步.

一、关于蓝牙开发的一些重要的理论概念:

1.当前ios中开发蓝牙所运用的系统库是<CoreBluetooth/CoreBluetooth.h>。

2.蓝牙外设必须为4.0及以上(2.0需要MFI认证),否则无法开发,蓝牙4.0设备因为低耗电,所以也叫做BLE。

3.CoreBluetooth框架的核心其实是两个东西,peripheral和central, 可以理解成外设和中心,就是你的苹果手机就是中心,外部蓝牙称为外设。

4.服务和特征(service and characteristic):简而言之,外部蓝牙中它有若干个服务service(服务你可以理解为蓝牙所拥有的能力),而每个服务service下拥有若干个特征characteristic(特征你可以理解为解释这个服务的属性)。

5.Descriptor(描述)用来描述characteristic变量的属性。例如,一个descriptor可以规定一个可读的描述,或者一个characteristic变量可接受的范围,或者一个characteristic变量特定的单位。

6.我们使用的蓝牙模块是在淘宝买的, 大概十多元一个, ios大概每次可以接受90个字节, 安卓大概每次可以接收20个字节, 具体数字可能会浮动, 应该是与蓝牙模块有关。

二、蓝牙连接的主要步骤

     1、创建一个CBCentralManager实例来进行蓝牙管理;

     2、搜索扫描外围设备;

     3、连接外围设备;

     4、获得外围设备的服务;

     5、获得服务的特征;

     6、从外围设备读取数据;

     7、给外围设备发送(写入)数据。

三、代码

// 加入权限访问, 否则上传AppStore会因为权限不足失败

1. 初始化

#import <CoreBluetooth/CoreBluetooth.h>
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];

2. 搜索扫描外围设备

/**
* -- 初始化成功自动调用
* -- 必须实现的代理,用来返回创建的centralManager的状态。
* -- 注意:必须确认当前是CBCentralManagerStatePoweredOn状态才可以调用扫描外设的方法:
scanForPeripheralsWithServices
*/
- (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");
// 开始扫描周围的外设。
/*
-- 两个参数为Nil表示默认扫描所有可见蓝牙设备。
-- 注意:第一个参数是用来扫描有指定服务的外设。然后有些外设的服务是相同的,比如都有FFF5服务,那么都会发现;而有些外设的服务是不可见的,就会扫描不到设备。
-- 成功扫描到外设后调用didDiscoverPeripheral
*/
[self.centralManager scanForPeripheralsWithServices:nil options:nil];
}
break;
default:
break;
}
}
#pragma mark 发现外设
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI{
NSLog(@"Find device:%@", [peripheral name]);
if (![_deviceDic objectForKey:[peripheral name]]) {
NSLog(@"Find device:%@", [peripheral name]);
if (peripheral!=nil) {
if ([peripheral name]!=nil) {
if ([[peripheral name] hasPrefix:@"根据设备名过滤"]) {
[_deviceDic setObject:peripheral forKey:[peripheral name]];
// 停止扫描, 看需求决定要不要加
// [_centralManager stopScan];
// 将设备信息传到外面的页面(VC), 构成扫描到的设备列表
if ([self.delegate respondsToSelector:@selector(dataWithBluetoothDic:)]) {
[self.delegate dataWithBluetoothDic:_deviceDic];
}
}
}
}
}
}

3.连接外围设备

// 连接设备(.h中声明出去的接口, 一般在点击设备列表连接时调用)
- (void)connectDeviceWithPeripheral:(CBPeripheral *)peripheral
{
[self.centralManager connectPeripheral:peripheral options:nil];
}
#pragma mark 连接外设--成功
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
//连接成功后停止扫描,节省内存
[central stopScan];
peripheral.delegate = self;
self.peripheral = peripheral;
//4.扫描外设的服务
/**
-- 外设的服务、特征、描述等方法是CBPeripheralDelegate的内容,所以要先设置代理peripheral.delegate = self
-- 参数表示你关心的服务的UUID,比如我关心的是"FFE0",参数就可以为@[[CBUUID UUIDWithString:@"FFE0"]].那么didDiscoverServices方法回调内容就只有这两个UUID的服务,不会有其他多余的内容,提高效率。nil表示扫描所有服务
-- 成功发现服务,回调didDiscoverServices
*/
[peripheral discoverServices:@[[CBUUID UUIDWithString:@"你要用的服务UUID"]]];
if ([self.delegate respondsToSelector:@selector(didConnectBle)]) {
// 已经连接
[self.delegate didConnectBle];
}
}
#pragma mark 连接外设——失败
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
NSLog(@"%@", error);
}
#pragma mark 取消与外设的连接回调
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
NSLog(@"%@", peripheral);
}

4. 获得外围设备的服务

#pragma mark 发现服务回调
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{ //NSLog(@"didDiscoverServices,Error:%@",error);
CBService * __nullable findService = nil;
// 遍历服务
for (CBService *service in peripheral.services)
{
//NSLog(@"UUID:%@",service.UUID);
if ([[service UUID] isEqual:[CBUUID UUIDWithString:@"你要用的服务UUID"]])
{
findService = service;
}
}
NSLog(@"Find Service:%@",findService);
if (findService)
[peripheral discoverCharacteristics:NULL forService:findService];
}

5、获得服务的特征

#pragma mark 发现特征回调
/**
-- 发现特征后,可以根据特征的properties进行:读readValueForCharacteristic、写writeValue、订阅通知setNotifyValue、扫描特征的描述discoverDescriptorsForCharacteristic。
**/
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{
for (CBCharacteristic *characteristic in service.characteristics) {
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"你要用的特征UUID"]]) { /**
-- 读取成功回调didUpdateValueForCharacteristic
*/
self.characteristic = characteristic;
// 接收一次(是读一次信息还是数据经常变实时接收视情况而定, 再决定使用哪个)
// [peripheral readValueForCharacteristic:characteristic];
// 订阅, 实时接收
[peripheral setNotifyValue:YES forCharacteristic:characteristic]; // 发送下行指令(发送一条)
NSData *data = [@"硬件工程师给我的指令, 发送给蓝牙该指令, 蓝牙会给我返回一条数据" dataUsingEncoding:NSUTF8StringEncoding];
// 将指令写入蓝牙
[self.peripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
} /**
-- 当发现characteristic有descriptor,回调didDiscoverDescriptorsForCharacteristic
*/
[peripheral discoverDescriptorsForCharacteristic:characteristic];
}
}

6.从外围设备读取数据

#pragma mark - 获取值
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
// characteristic.value就是蓝牙给我们的值(我这里是json格式字符串)
NSData *jsonData = [characteristic.value dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *dataDic = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:nil];
// 将字典传出去就可以使用了
} #pragma mark - 中心读取外设实时数据
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
if (characteristic.isNotifying) {
[peripheral readValueForCharacteristic:characteristic];
} else {
NSLog(@"Notification stopped on %@. Disconnecting", characteristic);
NSLog(@"%@", characteristic);
[self.centralManager cancelPeripheralConnection:peripheral];
}
}

7. 给外围设备发送(写入)数据

// 上文中发现特征之后, 发送下行指令的时候其实就是向蓝牙中写入数据
// 例:
// 发送检查蓝牙命令
- (void)writeCheckBleWithBle
{
_style = ;
// 发送下行指令(发送一条)
NSData *data = [@"硬件工程师提供给你的指令, 类似于5E16010203...这种很长一串" dataUsingEncoding:NSUTF8StringEncoding];
[self.peripheral writeValue:data forCharacteristic:self.characteristic type:CBCharacteristicWriteWithResponse];
}
#pragma mark 数据写入成功回调
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
NSLog(@"写入成功");
if ([self.delegate respondsToSelector:@selector(didWriteSucessWithStyle:)]) {
[self.delegate didWriteSucessWithStyle:_style];
}
}

8. 另外

// 扫描设备
- (void)scanDevice
{
if (_centralManager == nil) {
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
[_deviceDic removeAllObjects];
}
}
#pragma mark - 断开连接
- (void)disConnectPeripheral{
/**
-- 断开连接后回调didDisconnectPeripheral
-- 注意断开后如果要重新扫描这个外设,需要重新调用[self.centralManager scanForPeripheralsWithServices:nil options:nil];
*/
[self.centralManager cancelPeripheralConnection:self.peripheral];
}
#pragma mark - 停止扫描外设
- (void)stopScanPeripheral{
[self.centralManager stopScan];
}

由于硬件方面刚开始用蓝牙2.0跟我对接, 导致程序一直搜索不到设备.希望小伙伴们注意一下这个问题. 如果有不清楚的地方欢迎留言探讨.

iOS蓝牙开发(4.0)详解的更多相关文章

  1. iOS 证书与签名 解惑详解

    iOS 证书与签名 解惑详解 分类: iPhone2012-06-06 19:57 9426人阅读 评论(1) 收藏 举报 iosxcodecryptographyappleiphone测试   目录 ...

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

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

  3. iOS 视图控制器转场详解

    iOS 视图控制器转场详解 前言的前言 唐巧前辈在微信公众号「iOSDevTips」以及其博客上推送了我的文章后,我的 Github 各项指标有了大幅度的增长,多谢唐巧前辈的推荐.有些人问我相关的问题 ...

  4. Spark Streaming揭秘 Day28 在集成开发环境中详解Spark Streaming的运行日志内幕

    Spark Streaming揭秘 Day28 在集成开发环境中详解Spark Streaming的运行日志内幕 今天会逐行解析一下SparkStreaming运行的日志,运行的是WordCountO ...

  5. iOS中MVC等设计模式详解

    iOS中MVC等设计模式详解 在iOS编程,利用设计模式可以大大提高你的开发效率,虽然在编写代码之初你需要花费较大时间把各种业务逻辑封装起来.(事实证明这是值得的!) 模型-视图-控制器(MVC)设计 ...

  6. 【转】【Android UI设计与开发】之详解ActionBar的使用,androidactionbar

    原文网址:http://www.bkjia.com/Androidjc/895966.html [Android UI设计与开发]之详解ActionBar的使用,androidactionbar 详解 ...

  7. IOS数据库操作SQLite3使用详解(转)

    iPhone中支持通过sqlite3来访问iPhone本地的数据库.具体使用方法如下1:添加开发包libsqlite3.0.dylib首先是设置项目文件,在项目中添加iPhone版的sqlite3的数 ...

  8. SpringMVC【开发Controller】详解

    前言 本文主要是讲解在Controller中的开发,主要的知识点有如下: 编码过滤器 使用注解开发 注解@RequestMapping详解 业务方法接收参数 字符串转日期 重定向和转发 返回JSON ...

  9. 【Android 应用开发】Ubuntu 下 Android Studio 开发工具使用详解 (旧版本 | 仅作参考)

    . 基本上可以导入项目开始使用了 ... . 作者 : 万境绝尘 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/21035637 ...

随机推荐

  1. C语言各类型大小,结构体大小 sizeof(struct A)

    C语言类型大小总览 编译器pack指令 #pragma pack(n)——定义n字节对齐 C++固有类型的对齐取编译器对齐与自身大小中较小的一个 32位C++默认8字节对齐.gcc编译器默认4字节对齐 ...

  2. redis 适用场景、缓存选择、java实现

    redis适用场景 查询多,修改少:如国家地区信息.商品分类.数据字典 缓存选择 hibernate二级缓存.mybatis二级缓存.redishibernate二级缓存.mybatis二级缓存默认不 ...

  3. Hive是读时模式

    Hive处理的数据是大数据,在保存表数据时不对数据进行校验,而是在读数据时校验,不符合格式的数据设置为NULL: 读时模式的优点是,加载数据库快. 传统的数据库如mysql.oracle是写时模式,不 ...

  4. web项目启动时,自动执行代码的几种方式

    在项目开发过程中,往往需要一些功能随着项目启动而优先启动,下面我总结几种方式(非spring boot) spring boot的参考 spring boot 学习之路9 (项目启动后就执行特定方法) ...

  5. ES6中的import与export对class操作相关用法举例

    两种用法: 一.指定输出类名 // export 输出 export class App extends React.Componet { // ..code } // import 引入 impor ...

  6. leetcode题解之Find the Duplicate Number

    1.题目描述 2.分析 利用C++的 标准模板库 set 对数组进行读取,然后插入,如果检测到元素已经在set内部,则返回该元素值即可.时间复杂度为 O(n),空间复杂度为 O(n); 3.代码 in ...

  7. Oracle 数据库执行慢SQL

    ) hou, - ))) mini, c.sql_address, c.inst_id,f.full_name,u.user_name, b.user_concurrent_program_name, ...

  8. .NET Core Web 文件分片上传,带进度条实用插件

    话不多说,上源码连接: 链接:https://pan.baidu.com/s/1_u15zqAjhH0aVpeoyVMfUA 提取码:z209

  9. session更换存储,实现在多台服务器共享

    场景 web服务器有多台,每台服务器都会存贮自己的session,session无法在多台服务器共享.所以就需要更换session的存贮空间,存贮在一个共用的空间.通常为了读写速度,我们会选择存贮在内 ...

  10. 【FLEX教程】#007 如何让JS调用SWF里的方法

    HTML中,JS如何调用SWF里面已经封装好的代码呢? 有一些事情Flex没办法实现的,需要通过调用JS来实现. eg: 当浏览器窗体关闭的时候,弹出一个对话框,提示用户是否退出?或者是否保存当前的操 ...