2014-05-15 10:01:33 mzy202 阅读数 32386
  • iOS开发-全面解析iOS蓝牙BLE4.0开发

    只要你会OC基础,会写HelloWorld,你就可以实现iOS的蓝牙通信功能,实现蓝牙小项目也不在话下,作者会带领大家详细分析BLE4.0原理,通过分析xcode使用的蓝牙API,让学者能够得心应手的实现蓝牙BLE4.0的开发

    3680 人正在学习 去看看 许英俊

       蓝牙4.0BLE 手机控制 cc2540 CC2541 的串口透传功能已实现

       虽然蓝牙4.0 BLE芯片CC2540 是单芯片(即用户可以对它进行芯片级代码编写), 是8051增强型主控, 带蓝牙4.0功能, 但很多时候很多客户都只需要他的透传功能, 现在随着Android4.3 和IOS 的兴起, 支持蓝牙4.0BLE的手机越来越多,所以如何实现手机控制CC2540的透传是一个非常实用的功能,有了这个功能, 我们可以用手机来连接CC2540通过串口来连接的设备,纵观淘宝上有销售的几种模块, 小部分研发实力不足的,都只是提供裸模块,有两家提供了串口透传的模块的, 售价比裸片要高不少, AmoMcu 根据客户需求以及网友的需要, 现在已经实现了此商业模块的功能, 即手机通过蓝牙4.0BLE发送串字符cc2540中, cc2540把该字符串透传出去,同时外面送进来的串口数据也能透传到手机上来,HC-08 模块就是这样的功能, 现在我们这里也实现了, 并且以源码的形式公开给大家, 用于交流和学习。

      由于时间关系, 尚未整理出文档, 但源码都经过验证, ok了。

      目前用ios 测试成功老, Android的源码在进一步整理中。请等待。谢谢。

      先mark, 后面会增加图文介绍。




    如果需要cc2540 BLE开发板, 请看这里  http://amomcu.taobao.com/ , 这里能提供基于cc2540cc2541的蓝牙4.0BLE开发的详细硬件和软件资料, QQ群257318688

2016-06-20 15:34:44 zzfenglin 阅读数 20503
  • iOS开发-全面解析iOS蓝牙BLE4.0开发

    只要你会OC基础,会写HelloWorld,你就可以实现iOS的蓝牙通信功能,实现蓝牙小项目也不在话下,作者会带领大家详细分析BLE4.0原理,通过分析xcode使用的蓝牙API,让学者能够得心应手的实现蓝牙BLE4.0的开发

    3680 人正在学习 去看看 许英俊

蓝牙4.0BLE协议与协议栈的关系


协议定义的是一系列的通信标准,通信双方需要共同按照这一标准进行正常的数据收发。

协议栈是协议的具体实现形式,通俗的理解为用代码实现的函数库,以便于开发人员调用。


蓝牙4.0BLE协议栈就是将各个层定义的协议都集合在一起,以函数的形式实现,并提供一些应用层API,供用户调用。


注意:虽然协议是统一的,但是协议的具体实现形式是变化的,即不同厂商提供的协议栈是有区别的,例如:函数名称和参数列表可能有区别,选择协议栈以后,需要学习具体的例子,查看厂商提供的Demo演示程序、说明文档(通常,实现协议栈的厂商会提供一些API手册供用户查询)来学习各个函数的使用方式,进而快速地使用协议栈进行应用程序的开发工作。




如何使用蓝牙4.0BLE协议栈


既然蓝牙4.0BLE协议栈已经实现了蓝牙4.0BLE协议,那么用户就可以使用协议栈提供的API进行应用程序的开发,在开发过程中不必过多的关注蓝牙4.0BLE协议的具体实现细节,只需要关注一个核心的问题:应用程序数据从哪里来到哪里去。

 

至于调用协议栈中函数后,如何初始化应用进行数据发送等工作,蓝牙4.0BLE协议栈已经完成了所需要的初始化。

 

如果开发过程中确实需要或者是想要了解蓝牙4.0BLE协议,可以查看SIG提供的标准协议规范。




深入理解蓝牙4.0BLE协议栈


协议栈概述


我们以TI的CC254X系列BLE芯片为例来深入了解下蓝牙4.0BLE协议栈。TI的蓝牙4.0BLE协议栈包含两部分:主机和控制器。主机和控制器的分离要追溯到蓝牙BR/EDR设备时期,控制器和主机通常会分开实现。

 

协议栈的实现方式采用分层的思想,控制器部分包括:物理层、链路层、主机控制接口层;主机部分包括:逻辑链路控制及自适应协议层、安全管理层、属性协议层、通用访问配置文件层、通用属性配置文件层;上层可以调用下层提供的函数来实现需要的功能。


蓝牙技术联盟提供的标准规范下载链接:

https://www.bluetooth.com/zh-cn/specifications/adopted-specifications


蓝牙4.0标准规范CSDN下载链接:

点击打开下载页链接



协议栈基础


蓝牙4.0BLE协议栈的结构图如下:



详细介绍如下:

1.物理层(Physical Layer,简写 PHY):

是1Mbps自适应跳频的GFSK射频,工作于免许可证的2.4GHz ISM(工业、科学与医疗)频段。


2.链路层(Link Layer,简写 LL):

用于控制设备的射频状态,设备将处于五种状态之一:等待、广告、扫描、初始化、连接。广播设备不需要建立连接就可以发送数据,而扫描设备接收广播设备发送的数据;发起连接的设备通过发送连接请求来回应广播设备,如果广播设备接受连接请求,那么广播设备与发起连接的设备将会进入连接状态。发起连接的设备称为主机,接受连接请求的设备称为从机。


3.主机控制接口层(Host Controller Interface,简写 HCI):

为主机和控制器之间提供标准通信接口。这一层可以是软件或者硬件接口,如UART、SPI、USB等。


4.逻辑链路控制及自适应协议层(Logical Link Control and Adaptation Protocol,简写 L2CAP):

为上层提供数据封装服务,允许逻辑上的点对点数据通信。


5.安全管理层(Security Manager,简写 SM):

定义了配对和秘钥分配方式,并为协议栈其他层与另一个设备之间的安全连接和数据交换提供服务。


6.属性协议层(Attribute protocol,简写 ATT):

允许设备向另外一个设备展示一块特定的数据,称之为属性。在ATT环境中,展示属性的设备称为服务器,与之配对的设备称为客户端。链路层状态(主机和从机)与设备的ATT角色是相互独立的。例如:主机设备既可以是ATT服务器,也可以是ATT客户端;从机设备既可以是ATT服务器,也可以是ATT客户端。


7.通用属性配置文件层(Generic Attribute profile,简写 GATT):

定义了使用ATT的服务框架。GATT规定配置文件(profile)的结构。在BLE中,所有被profile或者服务用到的数据块称为特性,两个建立连接的设备之间的所有数据通信都是通过GATT子程序处理。GATT层用于已连接的蓝牙设备之间的数据通信,应用程序和profile直接使用GATT层。


当两个设备建立连接之后,它们就处于下面两种角色之一:

GATT服务器:为GATT客户端提供数据服务的设备。

GATT客户端:从GATT服务器读写应用数据的设备。


注意:GATT角色中的客户端和服务器的概念与链路层的主机和从机的概念完全独立,与GAP层角色中的外设和集中器的概念也是完全独立。 主机既可以是GATT客户端也可以是GATT服务器;从机既可以是GATT客户端也可以是GATT服务器。


一个GATT服务器中可包含一个或多个GATT服务,GATT服务是完成特定功能的一系列数据的集合。每一个应用工程大致包含下列三种服务:

(1)强制的GAP服务。这一服务包含了设备和访问信息。例如,设备、设备供应商和产品标示。它是协议栈的一部分,是BLE规范对每一个BLE设备的强制要求。这部分没有提供源代码,而是直接编译到协议栈库文件中了。

(2)强制的GATT服务。这一服务包含了GATT服务器的信息,是协议栈的一部分,同样也是BLE规范对每一个BLE设备的要求。这部分同样没有提供源代码而是直接编译到协议栈库文件中了。

(3)自定义服务。这部分服务包含应用数据的信息,与应用数据的传递密切相关,我们可以按照特定的格式编写自己的GATT服务。


特性(Characteristic)是服务用到的值,以及其内容和配置信息。GATT定义了在BLE连接中发现、读取和写入属性的子过程。GATT服务器上的特性值及其内容和配置信息(称为描述符)存储于属性表中。属性表是一个数据库,包含了成为属性的小块数据,除了值本身,每个属性都包含下列属性:  

(1)句柄:属性在表中的地址,每个属性有唯一的句柄。

(2)类型:表示数据代表的事物,通常是蓝牙技术联盟规定或用户自定义的UUID(Universally Unique Identifier)。

(3)权限:规定了GATT客户端设备对属性的访问权限,包括是否能访问和怎样访问。


GATT定义了若干在GATT服务器和客户端之间的通信的子过程:

(1)读特性值:客户端设备请求读取句柄处的特性值,服务器将此值回应给客户端(假定属性有读权限)。

(2)使用特性的UUID读:客户端请求读基于一个特定类型的所有特性值,服务器将所有与指定类型匹配的特性的句柄和值回应给客户端设备(假设属性有读权限)。  

(3)读多个特性值:客户端一次请求中读取几个句柄的特性值,服务器将这些特性值回应给客户端(假设属性有读权限),客户端需要知道如何解析这些不同的特性值数据。

(4)读特性描述符:客户端请求读特定句柄处的特性描述符,服务器将特性描述符的值回应给客户端设备(假设属性有读权限)。  

(5)使用UUID发现特性:客户端通过发送特性的类型UUID来请求发现这个特性的句柄。服务器将这个”特性”的声明回应给客户端设备,其中包括特性值的句柄以及特性的权限。

(6)写特性值:客户端设备请求向服务器特定的句柄处写入特性值,服务器将数据是否写入成功的信息反馈给客户端(假设特性有写权限,另外有一种特殊的写类型是不需要服务器来反馈是否写入成功的信息的,使用的时候根据具体应用来具体分析使用)。

(7)写特性描述符:客户端设备请求向服务器特定的句柄处写入特性描述符,服务器将特性描述符是否写入成功的信息反馈给客户端(假设特性描述有写权限)。  

(8)特性值通知:服务器将一个特性值通知给客户端,客户端设备不需要向服务器请求这个数据,客户端收到这个数据时,不需要属性协议层确认特性值是否被成功接收。

(9)特性值指示:服务器将一个特性值指示给客户端,客户端设备同样不需要向服务器请求这个数据,但是跟通知不一样的是,客户端收到这个数据之后,属性协议层必须确认特性值被成功接收。


通知与指示功能的流程如下:





何时发送通知或指示的条件可以在配置文件中设置,也可以通过应用来设置。要想使能通知和指示功能,需要分别在相应的句柄特性描述符写入0x00010x0002,如下表所示:



每个Profile初始化其相应的服务并内在的通过设备的GATT服务器来注册服务。GATT服务器将整个服务加到属性表中,并为每个属性分配唯一句柄。GATT属性表中有一些特殊的属性类型,其值由蓝牙技术联盟定义:

(1)GATT_PRIMARY_SERVICE_UUID:表示新服务的起始和提供的服务类型。

(2)GATT_CHARACTER_UUID:称为特性声明,紧随其后的是GATT特性值。

(3)GATT_CLIENT_CHAR_CFG_UUID:这一属性代表特性描述符,它与属性表中它前面最近的句柄处的特性值相关,它允许GATT客户端设备使能特性值通知或者指示。

(4)GATT_CHAR_USER_DESC_UUID:这一属性代表描述符,它与属性表中它前面最近的句柄处的特性值相关,包含一个ASCII字符串,是对相关特性的描述。


8.通用访问配置文件层(Generic Access Profile,简写 GAP):

负责处理设备访问模式和程序,包括设备发现、建立连接、终止连接、初始化安全特性和设备配置。


GAP层总是作为下面四种角色之一:

(1)广播者:不可连接的广播设备。

(2)观察者:扫描设备,但不发起建立连接。

(3)外部设备:可连接的广播设备,可以在单个链路层连接中作为从机。

(4)集中器:扫描广播设备并发起连接,可以在单个链路层连接中作为主机。


外部设备广播特定的数据使集中器知道它是一个可以连接的设备。广播内容包括设备地址以及一些额外的数据,如设备名等,当然也可以是自定义的数据,只要满足广播数据中广告的格式即可。集中器收到广播数据后向外部设备发送扫描请求,然后外部设备将特定的数据回应给集中器,称为扫描回应。集中器收到扫描回应后便知道这是一个可以建立连接的外部设备。这就是设备发现的全过程。此时集中器可以向外部设备发起建立连接的请求。连接请求包括一些链接参数。关于蓝牙4.0BLE的广播和连接,请参看下面几篇博文:


蓝牙BLE报文:

http://blog.csdn.net/zzfenglin/article/details/51165093


蓝牙BLE广播:

http://blog.csdn.net/zzfenglin/article/details/51165543

http://blog.csdn.net/zzfenglin/article/details/51166830


蓝牙BLE连接:

http://blog.csdn.net/zzfenglin/article/details/51303211

http://blog.csdn.net/zzfenglin/article/details/51304084



GAP层也处理BLE连接中安全特征的初始化。只有在已认证的连接中特定的数据才能被读写,一旦连接建立,两个设备进行配对,当配对完成后,形成加密链接的密钥。典型应用中外设请求集中器提供密钥来完成配对工作,密钥可以是一个固定的值,如000000,也可以随机生成一个数据提供给使用者,集中器设备发送正确的密钥后,两设备交换安全密钥并加密认证链接。


在许多情况下,同一对外设和集中器会不定时地连接和断开,BLE的安全机制中有一项特性允许两个设备之间建立长期的安全密钥信息,这种特性称为绑定,它允许两设备重新连接时快速地完成加密认证,而不需要每次连接时执行配对的完整过程。




蓝牙4.0BLE协议栈分层思想的优点


蓝牙4.0BLE协议栈采用分层思路的最大优点是:将服务、接口和协议这三个概念明确的区分开来。服务说明某一层为上一层提供了一些什么样的功能;接口说明上一层如何使用下一层的服务;而协议涉及到如何实现本层的服务。这样,各层之间就具有很强的独立性,当协议的一部分发送变化时,只需对与此相关的分层进行修改即可,其他分层不需要改变。






2014-08-13 16:15:48 xhalone 阅读数 1789
  • iOS开发-全面解析iOS蓝牙BLE4.0开发

    只要你会OC基础,会写HelloWorld,你就可以实现iOS的蓝牙通信功能,实现蓝牙小项目也不在话下,作者会带领大家详细分析BLE4.0原理,通过分析xcode使用的蓝牙API,让学者能够得心应手的实现蓝牙BLE4.0的开发

    3680 人正在学习 去看看 许英俊

蓝牙4.0 BLE 入门

1. 手机需要android 4.3 及以上版本

2. 权限:

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>

只允许支持BLE 的手机安装还需要添加uses-feature

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>

3. BLE 设备扫描

3.1 获取蓝牙适配器:  mBluetoothAdapter = bluetoothManager.getAdapter();  

3.2 开始扫描: mBluetoothAdapter.startLeScan(mLeScanCallback);

在调用扫描时, 需要实现LeScanCallback 回调接口, 用于扫描结果的返回: 

protected LeScanCallback mLeScanCallback = new  BluetoothAdapter.LeScanCallback() {

		@Override
		public void onLeScan(final BluetoothDevice device, final int rssi, byte[] scanRecord) {
			Log.d(TAG, "find device -> "+ device.getName()+"mac:"+device.getAddress());
			runOnUiThread(new Runnable() {
				public void run() {
					String mac = device.getAddress();
					String name = device.getName();
//					deviceAdapter.addDevice(new BluetoothItem(name, mac));
					deviceAdapter.addDevice(new BluetoothItem(name, mac, rssi));
					deviceAdapter.notifyDataSetChanged();
				}
			});
		}
	};
3.3 获取扫描结果后,可以通过得到的设备地址, 来对设备进行连接, 连接上将得到一个BluetoothGatt 用于操作, 其中需要实现BluetoothGattCallback 回调, 其用于结果的返回, 如连接的状态,以及后面的读写等操作

	final BluetoothDevice device = mBluetoothAdapter
				.getRemoteDevice(address);
		if (device == null) {
			Log.w(TAG, "Device not found.  Unable to connect.");
			return false;
		}
		// We want to directly connect to the device, so we are setting the
		// autoConnect
		// parameter to false.
		mBluetoothGatt = device.connectGatt(this, true, mGattCallback);
		
mGattCallback 回调函数:

	private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {

		@Override
		public void onConnectionStateChange(BluetoothGatt gatt, int status,
				int newState) {
			if (newState == BluetoothProfile.STATE_CONNECTED) {
				broadcastUpdate(ACTION_GATT_CONNECTED);
				Log.i(TAG, "Connected to GATT server.");
				// Attempts to discover services after successful connection.
				Log.i(TAG, "Attempting to start service discovery:"
						+ mBluetoothGatt.discoverServices());
				isConnectd = true;
			} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
				Log.i(TAG, "Disconnected from GATT server.");
				isConnectd = false;
				broadcastUpdate(ACTION_GATT_DISCONNECTED, gatt.getDevice().getAddress());
			}
		}
		//...略
	}

3.4 在蓝牙设备中, 其包含有多个BluetoothGattService, 而每个BluetoothGattService中又包含有多个BluetoothGattCharacteristic

在连接上蓝牙后, 通过调用 mBluetoothGatt.discoverServices(), 来发现蓝牙设备中的服务, 接下来就可以获取到设备中的服务列表  mBluetoothGatt.getServices(); 或者通过uuid 来获取某一个服务 BluetoothGattService gattService = mBluetoothGatt.getService(uuid); 

服务中, 获得Characteristic 集合则调用 gattService.getCharacteristics(); 

在 Characteristic中, 可以通过 mBluetoothGatt.readCharacteristic(characteristic); 来读取其里面的数据, 其结果在mGattCallback 回调函数中获取.

写如数据: characteristic.setValue(data); 

mBluetoothGatt.wirteCharacteristic(mCurrentcharacteristic);

4. 关闭连接 mBluetoothGatt.close();



2017-06-19 13:29:47 weilexuexi12 阅读数 452
  • iOS开发-全面解析iOS蓝牙BLE4.0开发

    只要你会OC基础,会写HelloWorld,你就可以实现iOS的蓝牙通信功能,实现蓝牙小项目也不在话下,作者会带领大家详细分析BLE4.0原理,通过分析xcode使用的蓝牙API,让学者能够得心应手的实现蓝牙BLE4.0的开发

    3680 人正在学习 去看看 许英俊

在使用蓝牙4.0BLE协议栈进行应用程序开发时,如何在应用程序中添加一个新任务

下面结合工程示例SimpleBLEPeripheral来讲解,打开OSAL_SimpleBLEPeripheral.c文件可以找到数组tasksArr[]和函数osalInitTasks()。tasksArr[]数组里存放了所有任务的事件处理函数的地址;osalInitTasks()是OSAL的任务初始化函数,所有任务的初始化工作都在这里面完成,并且自动给每个任务分配一个任务ID。

      要添加新任务,只需要编写两个函数:

(1)新任务的初始化函数;

(2)新任务的事件处理函数。

      将事件处理函数的地址加入tasksArr[]数组,代码如下所示。

const pTaskEventHandlerFn tasksArr[] =
{
  LL_ProcessEvent,                                                  // task 0
  Hal_ProcessEvent,                                                 // task 1
  HCI_ProcessEvent,                                                 // task 2
#if defined ( OSAL_CBTIMER_NUM_TASKS )
  OSAL_CBTIMER_PROCESS_EVENT( osal_CbTimerProcessEvent ),           // task 3
#endif
  L2CAP_ProcessEvent,                                               // task 4
  GAP_ProcessEvent,                                                 // task 5
  GATT_ProcessEvent,                                                // task 6
  SM_ProcessEvent,                                                  // task 7
  GAPRole_ProcessEvent,                                             // task 8
  GAPBondMgr_ProcessEvent,                                          // task 9
  GATTServApp_ProcessEvent,                                         // task 10
  SimpleBLEPeripheral_ProcessEvent                                  // task 11
};

     将新任务的初始化函数添加在osalInitTasks()函数的最后,如下代码所示。

void osalInitTasks( void )
{
  uint8 taskID = 0;

  tasksEvents = (uint16 *)osal_mem_alloc( sizeof( uint16 ) * tasksCnt);
  osal_memset( tasksEvents, 0, (sizeof( uint16 ) * tasksCnt));

  /* LL Task */
  LL_Init( taskID++ );

  /* Hal Task */
  Hal_Init( taskID++ );

  /* HCI Task */
  HCI_Init( taskID++ );

#if defined ( OSAL_CBTIMER_NUM_TASKS )
  /* Callback Timer Tasks */
  osal_CbTimerInit( taskID );
  taskID += OSAL_CBTIMER_NUM_TASKS;
#endif

  /* L2CAP Task */
  L2CAP_Init( taskID++ );

  /* GAP Task */
  GAP_Init( taskID++ );

  /* GATT Task */
  GATT_Init( taskID++ );

  /* SM Task */
  SM_Init( taskID++ );

  /* Profiles */
  GAPRole_Init( taskID++ );
  GAPBondMgr_Init( taskID++ );

  GATTServApp_Init( taskID++ );

  /* Application */
  SimpleBLEPeripheral_Init( taskID );
}

在此例中,SimpleBLEPeripheral_ProcessEvent函数添加到了函数的末尾,将SimpleBLEPeripheral_Init()函数添加到osalInitTasks()任务初始化函数中。

需要注意两点:

(1)taskArr[]数组里各事件处理函数的排列顺序要与osalInitTasks()函数中调用各任务初始化函数的顺序保持一致,只有这样才能保持一致,只有这样才能保证每个任务的事件处理函数能够接收到正确的任务ID(在osalInitTasks()函数中分配)

(2)为了保证osalInitTasks()函数所分配的任务ID,需要给每一个任务定义一个全局变量来保存这个ID。如在SimpleBLEPeripheral.c中定义了一个全局变量SimpleBLEPeripheral_TaskID,并且SimpleBLEPeripheral_Init函数中对其进行赋值。



2015-07-22 17:47:41 taonull 阅读数 2968
  • iOS开发-全面解析iOS蓝牙BLE4.0开发

    只要你会OC基础,会写HelloWorld,你就可以实现iOS的蓝牙通信功能,实现蓝牙小项目也不在话下,作者会带领大家详细分析BLE4.0原理,通过分析xcode使用的蓝牙API,让学者能够得心应手的实现蓝牙BLE4.0的开发

    3680 人正在学习 去看看 许英俊

原文链接: http://zh.5long.me/2015/bluetooth-for-iOS-developer/

前言

物联网时代的到来,智能硬件层出不穷。蓝牙低功耗技术(BLE,Bluetooth Low Energy)使得蓝牙4.0的应用越来越广泛,比如小米手环就是使用蓝牙4.0来传输数据。

在蓝牙4.0之前,iOS的蓝牙功能作为私有,开发者只能使用蓝牙开发联机游戏而不能和第三方蓝牙设备通信,当时要开发一个用iPhone通过蓝牙来控制硬件的APP是件非常复杂的事情。好在蓝牙4.0之后,苹果开放了蓝牙4.0开发接口。而TI公司也推出了两款蓝牙芯片:CC2540和CC2541。之后就涌现了一大批基于蓝牙4.0的硬件设备和APP。如Stick-N-Find和耐克数字运动手环 NIKE+。随后中国也出现了一大批类似设备,小米、华为都出了手环。

本人差不多也是在那时接触蓝牙4.0,做一个蓝牙通信的APP。在当时资料还不是那么丰富的情况下,整个开发过程也是个探索过程。由于最近又需要做一个蓝牙通信的APP,于是整理了之前写的代码,重新写了一份蓝牙通信的代码(之前写的他太乱了 : D)。

本文介绍iOS下开发蓝牙4.0通信程序的流程以及使用到相关的库。

相关资料

关键类

蓝牙4.0相关的组件为CoreBluetooth,所以要使用之前先引用’CoreBluetooth/CoreBluetooth.h’,其中有两个重要的类:

  • ‘CBCentralManager’:蓝牙的控制中心,通过它来获知蓝牙状态、扫描周边蓝牙设备、发起连接等。
  • ‘CBPeripheral’:和周边蓝牙设备对应的一个对象,每一个蓝牙设备对应一个CBPeripheral对象。通过它乡对应的蓝牙设备发送数据、读取数据、获取RSSI值等。

‘CBCentralManager’和’CBPeripheral’通过协议来交互,对应的两个协议为:

  • ‘CBCentralManagerDelegate’:CBCentralManager’的协议。
  • ‘CBPeripheralDelegate’:CBPeripheral’的协议。

比如当’CBCentralManager’发起扫描周边设备时,当发现了一个设备后就会调用CBCentralManagerDelegate’的’- (void)centralManager:(CBCentralManager*)central didDiscoverPeripheral:(CBPeripheral*)peripheral advertisementData:(NSDictionary*)advertisementData RSSI:(NSNumber*)RSSI’,通过实现该方法就可做相应地处理。

关于协议,请参考iOS开发入门教程之Objective-C · 协议(Protocols)

以下列出这两个协议常用的方法:

CBCentralManagerDelegate

@required
/*
本机设备(iPhone/iPad)蓝牙状态改变
*/
- (void)centralManagerDidUpdateState:(CBCentralManager *)central;

@optional
/*
扫描到了一个蓝牙设备peripheral
*/
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI;

/*
成功连接蓝牙设备peripheral
*/
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;

/*
连接蓝牙设备peripheral失败
*/
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;

/*
蓝牙设备peripheral已断开
*/
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;

CBPeripheralDelegate

@optional
/*
蓝牙设备peripheral的RSS值改变
*/
- (void)peripheralDidUpdateRSSI:(CBPeripheral *)peripheral error:(NSError *)error NS_DEPRECATED(NA, NA, 5_0, 8_0);

/*
从peripheral成功读取到services
*/
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error;

/*
从peripheral的service成功读取到characteristics
*/
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error;

/*
从蓝牙设备peripheral接收到数据
*/
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error;

/*
乡蓝牙设备peripheral成功写入数据,写数据时需要以CBCharacteristicWriteWithResponse才会调用
*/
 - (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error;

蓝牙通信方式

蓝牙通信方式为service+characteristic,这和网络通信的IP+端口类似,service可理解为IP,characteristic可理解为端口号。servicecharacteristic都是一个数字编号,一般用16进制表示,如0xFFE00xFFE1

需要注意的是发送方和接收方必须是在相同的service+characteristic下才能正常发送和接收数据,如发送方在service0xFFE0characteristic0xFFE1下发送信息,接收方也应该在service0xFFE0characteristic0xFFE1下接收信息,否则是接收不到数据的。

一个蓝牙设备可以有多个service,一个service也可以有多个characteristic。不同的servicecharacteristic组合可形成多个通信通道。

还有一点需要提醒的是,由于苹果的限制,iOS SDK并没有提供获取蓝牙设备MAC地址的方法,所以一般还是不要用MAC,使用CBPeripheralUUIDname代替

蓝牙通信开发流程

在此就已经了解了iOS开发蓝牙通信所需要的库了,下面进入开发流程。蓝牙通信流程一般是:

  1. 初始化
  2. 扫描周边的蓝牙设备。
  3. 连接某一设备。
  4. 发送/接收数据。
  5. 断开连接

当然,所有这些操作都默认iPhone/iPad已开启蓝牙功能:D

初始化

初始化一个CBCentralManager

self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];

扫描周边的蓝牙设备

调用CBCentralManagerscanForPeripheralsWithServices就能扫描周边的设备。

[self.centralManager scanForPeripheralsWithServices:nil options:nil];

当扫描到一个蓝牙设备时,会调用CBCentralManagerDelegatecentralManager:(CBCentralManager *) didDiscoverPeripheral:(CBPeripheral *) advertisementData:(NSDictionary *) RSSI:(NSNumber *)方法,通过实现该协议方法,就可做相应地处理。

- (void)centralManager:(CBCentralManager*)central didDiscoverPeripheral:(CBPeripheral*)peripheral advertisementData:(NSDictionary*)advertisementData RSSI:(NSNumber*)RSSI
{
    NSLog(@"find new device:%@", peripheral.name);
}

连接某一设备

通过调用CBCentralManagerconnectPeripheral: options:方法就可向蓝牙设备发起连接,连接成功或失败会分别调用CBCentralManagerDelegatecentralManager:(CBCentralManager *) didConnectPeripheral:(CBPeripheral *)centralManager:(CBCentralManager *) didFailToConnectPeripheral:(CBPeripheral *) error:(NSError *)方法。

如果连接成功,还应该读取peripheralservicecharacteristic

[self.centralManager connectPeripheral:peripheral options:nil];

- (void)centralManager:(CBCentralManager*)central didConnectPeripheral:(CBPeripheral*)peripheral
{
    peripheral.delegate = self;
    [peripheral discoverServices:nil];
}

- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
    [peripheral discoverCharacteristics:nil forService:service];
}

发送数据

发送数据只需要在指定的servicecharacteristic组合下发送即可,如果是以CBCharacteristicWriteWithResponse模式发送,发送完后还会调用CBPeripheralDelegateperipheral:(CBPeripheral *) didWriteValueForCharacteristic:(CBCharacteristic *) error:(NSError *),实现该协议方法可判断发送是否成功。以CBCharacteristicWriteWithoutResponse模式则不会有回调。

[peripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];

- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
    if (error)
    {
        NSLog(@"%@", error);
    }
}

接收数据

当接收到数据后,CBCentralManager会调用协议CBCentralManagerDelegateperipheral:(CBPeripheral *) didUpdateValueForCharacteristic:(CBCharacteristic *) error:(NSError *)error方法,实现该方法就可获取接收到得数据。

- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
    NSString *msg=[[NSString alloc] initWithData:characteristic.value  encoding:NSUTF8StringEncoding];
    NSLog(@"message:%@", msg);
}

断开连接

通过调用CBCentralManagercancelPeripheralConnection:即可断开蓝牙连接,成功断开后,会调用CBCentralManagerDelegate- (void)centralManager:(CBCentralManager *) didDisconnectPeripheral:(CBPeripheral *) error:(NSError *)方法。

[self.centralManager cancelPeripheralConnection:peripheral];

- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
}

结语

本文介绍了开发iOS下蓝牙通信APP的必要知识及开发流程,给出了一个iOS蓝牙通信的框架。关于各个函数的详细说明,还需要查阅官方文档。结合官方文档,相信读者可以开发出一个蓝牙通信的模型来。

没有更多推荐了,返回首页