第一篇:蓝牙知识小结
蓝牙协议知识总结
蓝牙设备 和 主机进行连接和数据通信的 流程如下:外部设备发出广告(带有UUID信息等其他信息); 主机(集中器设备)收到广告信息,进而发送扫描请求;表示我扫描到你的信息; 3 外部设备收到扫描请求后,返回扫描回应,表示我知道你扫描到我的信息; 4 主机进而发送连接请求信息,表示主机要跟设备建立无线连接; 5 设备收到连接请求后,发送相应请求回应;表示已经建立连接;
数据读写流程如下进一步(在建立连接的基础上): 主机发送主服务UUID(设备的广告UUID)给设备;
服务发现 7 设备收到后回应服务信息; 主机发送特性UUID;
特性发现 9 设备收到后回应特性值句柄;(类似于存储设备的地址)主机发送特性值句柄;
读信息 11 设备收到后回应特性值; 主机发送特性值句柄和要写入值;
写信息 13 设备回应写入成功响应;
在睡眠状态,耗电只有1微安(uA),而在连接事件中最高的是10几个毫安
连接建立之后,再进行安全密钥的交换配对,进而进行数据的读写;
主机和从机绑定之后,断开连接后,可以快速的建立连接并进行加密读写,而不需要再次配对; 特点 低功耗蓝牙速度只有100bps,传统蓝牙有3Mbps 2 低功耗蓝牙不需要IOS 的MFI 认证,传统蓝牙必须; 3 低功耗蓝牙能纽扣电池能用1年多,传统蓝牙不行;
频道:
2.4G – 2.48G 总共40个频段,每2M 一个频段;
其中 37(2.40G),38(2.426G),39(2.48G)为 3个广播频道;这3个频道避开了wifi 常用的频道,与wifi可以共存; 其他37个为连接频道;
1、BLE中主从机建立连接,到配对和绑定的过程如下图。
正如上图所示,最简单一次蓝牙通信需要以上相关步骤,包括discovery device,connect,pairing,bond等4个主要部分。
1)广播:广播包可以包含广播数据,广播包可以无指定或者对指定的设备发送。可以声明该器件是可连接的还是不可连接的。在一次广播中,广播包可以在三个广播通道中同时发送。
广播类型 :1 未指定可连接 2 指定可连接 3 未指定 不可见 4 未指定不可连接 #define GAP_ADTYPE_ADV_IND
0x00 //!< Connectable undirected advertisement #define GAP_ADTYPE_ADV_DIRECT_IND 0x01 //!< Connectable directed advertisement #define GAP_ADTYPE_ADV_DISCOVER_IND
0x02 //!< Discoverable undirected advertisement #define GAP_ADTYPE_ADV_NONCONN_IND
0x03 //!< Non-Connectable undirected advertisement #define GAP_ADTYPE_SCAN_RSP_IND
0x04 //!< Only used in gapDeviceInfoEvent_t 在peripheral.c中
GAPRole_Init(taskID++);进行了初始化设置
还有有以下函数bStatus_t GAPRole_SetParameter(uint16 param, uint8 len, void *pValue)GAPRole_GetParameter(…..)可以调用进行设置。2)scanning 1)被动扫描: 扫描者监听广播频道的广播包,收到后将其上传到host层
2)主动扫描:扫描者监听广播频道的广播包,当收到广播包后扫描者发送一个scan Request包,广播设备回应一个scan reponse包
3)Connection 在扫描设备扫描到一个可连接的广播消息后,扫描设备可以通过发送 connection reequst 包给广播设备从而成为连接的发起者
Connection resqust 包含从机链路层一系列的参数,这些参数声明连接时的通道及时序要求。建立连接
GAPCentralRole_EstablishLink(DEFAULT_LINK_HIGH_DUTY_CYCLE,DEFAULT_LINK_WHITE_LIST,addrType, peerAddr);广播设备接收了连接请求,就进入了连接状态,发起者成了主机,广播设备成了从机。两个已连接的设备的所有通信发生在连接事件中,连接事件周期性的发生,周期由连接间隔参数决定。
连接间隔:使用调频的间隔;两个连接事件之间的时间间隔,蓝牙传数据是在一个频段发送数据后,然后跳到另一个频段再传数据,从一个频道另一个频段的时间间隔就是连接间隔;即使没有数据发送,也要调频切换测试包是否连接断开;所以,连接间隔是定时的存在;可以认为是一个固定的时序;每隔一段时间就自动调到另一个频道的去建立连接;这个时间中,是很少功耗的,基本没有;
单位是1.25毫秒;范围是 6----3200个单位;也就是1.25ms到4s的范围 不同的应用 时间间隔不一样,时间间隔长,功耗就低,传输数据慢; 时间间隔短,功耗就高,传输数据就快。
从机延时: 从机如果没有数据发送,可以跳过连接间隔,不用频繁的定时去建立连接,从而过一段较长时间再去建立连接;这个时间就是从机延时时间;从而功耗降低很多;单位是和连接间隔一样;范围是 0---499
管理超时
超过这个时间,还没有建立连接,则认为是连接丢失,断开。回到未连接状态;
单位是10ms,范围是 10(100ms)-----3200(32s)。超时值必须比有效连接间隔大;有效连接间隔= 连接间隔×(1+从机延时)
如果从机不想使用当前的连接参数,可以向主机发送连接更新请求,从机设备可以在任何时候发送连接更新请求,使得从机可以动态的调整连接参数。
GAPCentralRole_UpdateLink(simpleBLEConnHandle,DEFAULT_UPDATE_MIN_CONN_INTERVAL,DEFAULT_UPDATE_MAX_CONN_INTERVAL,DEFAULT_UPDATE_SLAVE_LATENCY,DEFAULT_UPDATE_CONN_TIMEOUT);无论主机还是从机,都可以无条件的终止当前连接,一方请求终止,另一方必须在断开连接状态之前响应。
连接还可以由超时而终止。超时时间小于32s,大于有效连接间隔(连接间隔×(1+从机延时))
终止连接函数:
GAPCentralRole_TerminateLink(simpleBLEConnHandle);主机和从机保存各自的超时计时器,每次收到数据包就清零,一旦达到超时数值,就认为连接已经丢失就会断开连接。
连接超时判断,终止连接在程序中还没找到。
2.BLE中的GAP和GATT GAP个人认为就是监控上图中的交互状态,比如从广播变成连接,到配对等。
GATT通俗理解为用于主从机之间的客户端和服务器端的数据交互,以Attribute Table来体现。
GAP Role Profile:在GAP所处的4个角色:广播Advertise,主机central,从机Peripheral,观察者Observer。GATT Attribute:通用属性配置文件。
GAP作为Peripheral Role需要设置的核心参数如下 GAPROLE_ADVERT_ENABLED:广播使能。GAPROLE_ADVERT_DATA:广播时的参数,GAPROLE_SCAN_RSP_DATA:从机扫描响应,返回的数据包
GAPROLE_MIN_CONN_INTERVAL:处于连接状态后的设备,都会有个hop,一段时间内进行数据交互,以保证两者是连接的。当前后两次交互时,需要等待的最小间隔时间 GAPROLE_MAX_CONN_INTERVAL:...需要等待的最大间隔时间
GAPROLE_SLAVE_LATENCY:处于连接后,从机可以做出不响应连接请求的间隔数目,即跳过n个交互的连接。
GAPROLE_TIMEOUT_MULTIPLIER:从上次成功连接到这次连接成功的最大允许延时。如果规定时间内未成功则认为本次连接失败,丢弃。该值必须比有效连接的间隔大。GAPROLE_PARAM_UPDATE_ENABLE:请求主机更新参数,主机可以接受也可以拒绝。.GATT Server的相关设置函数。
// Initialize GATT attributes GGS_AddService(GATT_ALL_SERVICES);// GAP Service GATTServApp_AddService(GATT_ALL_SERVICES);// GATT attributes DevInfo_AddService();// Device Information Service SimpleProfile_AddService(GATT_ALL_SERVICES);// Simple GATT Profile 通常一个GATT中GAP server和GATT server是必须强制存在的,还有设备信息服务以及自己设计的profile server.SimpleProfile_AddService就是添加自己设计的profile server 在SimpleProfile_AddService函数中调用了如下函数
GATTServApp_RegisterService(simpleProfileAttrTbl, GATT_NUM_ATTRS(simpleProfileAttrTbl),&simpleProfileCBs);
} simpleProfileCBs 的函数定义如下:
CONST gattServiceCBs_t simpleProfileCBs = { simpleProfile_ReadAttrCB,// Read callback function pointer
simpleProfile_WriteAttrCB, // Write callback function pointer
NULL
// Authorization callback function pointer };实际上就是底层读写数据的函数,主机读数据时从机会调用simpleProfile_ReadAttrCB函数,写数据时从机会调用simpleProfile_WriteAttrCB函数。这两个函数在simpleGaatprofile.c 中实现。
注意在simpleProfile_WriteAttrCB,函数中有如下语句 if((notifyApp!= 0xFF)&& simpleProfile_AppCBs && simpleProfile_AppCBs->pfnSimpleProfileChange)
{
simpleProfile_AppCBs->pfnSimpleProfileChange(notifyApp);
} 注意函数指针的用法,实际是在接收到主机数据发过来的数据后调用simpleProfileChangeCB函数来处理接收到的数据。这个函数在初始化时注册,下面会讲到。作为GATT的server和client,主要通过Attribute来进行交互,当client请求server读取数据时,通过如下注册的回调函数来进行访问。
// Register callback with SimpleGATTprofile
VOID SimpleProfile_RegisterAppCBs(&simpleBLEPeripheral_SimpleProfileCBs);//给应用注册回调函数,这个函数非常重要 在回调函数中对数据做出处理。
static simpleProfileCBs_t simpleBLEPeripheral_SimpleProfileCBs = {
simpleProfileChangeCB
// Charactersitic value change callback };
在SimpleProfile_RegisterAppCBs 函数中赋值。simpleProfile_AppCBs = simpleProfileChangeCB
在simpleProfileChangeCB函数中可以启动定时器来给主机发送Notification数据
发送数据函数为GATT_Notification(noti_cHandle,&pReport,FALSE);
如下:
{ static attHandleValueNoti_t pReport;//声明attHandleValueNoti_t这个结构体
uint16 noti_cHandle;//存放handle
pReport.handle = simpleProfileAttrTbl[11].handle;//读取notification对应的handle
GAPRole_GetParameter(0x30E, ¬i_cHandle);//获取Connection Handle
pReport.len = 1;//数据长度
pReport.value[0] = 0x03;//赋值
GATT_Notification(noti_cHandle,&pReport,FALSE);}
主机使能Notification:
这个handle应该是相应的characteristic value的handle的后面一个, 就是characteristic value的handle加 1.例子: { attWriteReq_t writeReq;writeReq.handle =;writeReq.len = 2;writeReq.value[0] = LO_UINT16(GATT_CLIENT_CFG_NOTIFY);
这里是 0x01 writeReq.value[1] = HI_UINT16(GATT_CLIENT_CFG_NOTIFY);
这里是 0x00 writeReq.sig = 0;writeReq.cmd = 0;GATT_WriteCharValue(simpleBLEConnHandle, &writeReq, simpleBLETaskId);}
这两个值目的是打开Notification功能.CCC的参数有两个, 一个Notification, 一个indication.value[0]就是打开关闭notification, value[1]是打开关闭indication.主机数据处理:
simpleBLECentral.c 这个文件, 里面有个函数:simpleBLECentralProcessGATTMsg()这个函数就是处理各种从peripheral过来的数据.但是在示例代码中并没有加入通知, 就是notification的接收, 所以你得自己添加代码.很简单, 类似 if((pMsg->method == ATT_READ_RSP)||........), 添加 else if((pMsg->method == ATT_HANDLE_VALUE_NOTI)||......)就可以处理从机Notification的数据。
value 被写的时候首先 simpleProfile_WriteAttrCB()会被调到.最后才会调用 simpleProfileChangeCB()
GAP通过在启动设备事件的任务处理中启动设备,其实主要是向GAP中注册回调函数,让系统在发现自身运行状态变化时,调用该函数,方便应用层进行相关操作。if(events & SBP_START_DEVICE_EVT)
{
// Start the Device VOID GAPRole_StartDevice(&simpleBLEPeripheral_PeripheralCBs);//启动设备,注册回调函数,用于监督设备的状态变化:广播、连接、配对、绑定等。
// Start Bond Manager VOID GAPBondMgr_Register(&simpleBLEPeripheral_BondMgrCBs);} simpleBLEPeripheral_PeripheralCBs函数定义如下
static gapRolesCBs_t simpleBLEPeripheral_PeripheralCBs = {
peripheralStateNotificationCB, // Profile State Change Callbacks
NULL
// When a valid RSSI is read from controller(not used by application)};static void peripheralStateNotificationCB(gaprole_States_t newState)//传入参数由GPA自己输入,内部调用回调函数给用户,处理连接状态的改变 simpleBLEPeripheral_BondMgrCBs函数定义如下: static gapBondCBs_t simpleBLEPeripheral_BondMgrCBs = {
ProcessPasscodeCB,// 生成配对密码,发送给主机
ProcessPairStateCB
//主机密码的校验处理。配对状态管理 };
一、修改广播功率 { #define LL_EXT_TX_POWER_MINUS_23_DBM
0 //-23dbm 功率 最小 #define LL_EXT_TX_POWER_MINUS_6_DBM
//-6dbm
#define LL_EXT_TX_POWER_0_DBM
// 0dbm
#define LL_EXT_TX_POWER_4_DBM // +dbm 功率 最大
HCI_EXT_SetTxPowerCmd(gTxPower);
更新广播内容
GAP_UpdateAdvertisingData(simpleBLEPeripheral_TaskID, TRUE, sizeof(advertData_Ex), advertData_Ex);}
二、数据加密解密 { uint8 key[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
// 需要加密的数据
uint8 plaintextData[16] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
// 加密后数据存放区
uint8 encryptedData[16];
// 解密后数据存放区
uint8 deccryptedData[16];
// 开始加密
LL_Encrypt(key, plaintextData, encryptedData);
// 开始解密
LL_EXT_Decrypt(key, encryptedData, deccryptedData);}
三、设置从机广播时间 1)、广播模式必须是Limited Discoverable mode 在advertData 中加入此AD string: 0x02, GAP_ADTYPE_FLAGS, GAP_ADTYPE_FLAGS_LIMITED| GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED 2)、设置时间
//Maximum time to remain advertising, when in Limited Discoverable mode.unit is seconds #define USER_DEF_ADV_TIMEOUT
GAP_SetParamValue(TGAP_LIM_ADV_TIMEOUT, USER_DEF_ADV_TIMEOUT);
第二篇:Android蓝牙开发小结
学习之前先了解两个基本概念:
一、RFCOMM协议:
一个基于欧洲电信标准协会ETSI07.10规程的串行线性仿真协议。此协议提供RS232控制和状态信号,如基带上的损坏,CTS以及数据信号等,为上层业务(如传统的串行线缆应用)提供了传送能力。
RFCOMM是一个简单传输协议,其目的是针对如何在两个不同设备上的应用之间保证一条完整的通信路径,并在它们之间保持一通信段。
RFCOMM是为了兼容传统的串口应用,同时取代有线的通信方式,蓝牙协议栈需要提供与有线串口一致的通信接口而开发出的协议。RFCOMM协议提供对基于L2CAP协议的串口仿真,基于ETSI07.10。可支持在两个BT设备之间同时保持高达60路的通信连接。
RFCOMM只针对直接互连设备之间的连接,或者是设备与网络接入设备之间的互连。通信两端设备必须兼容于RFCOMM协议,有两类设备:DTE(Data Terminal Endpoint,通信终端,如PC,PRINTER)和DCE(Data Circuit Endpoint,通信段的一部分,如Modem)。此两类设备不作区分。
二、MAC硬件地址
MAC(Medium/MediaAccess Control, 介质访问控制)MAC地址是烧录在NetworkInterfaceCard(网卡,NIC)里的.MAC地址,也叫硬件地址,是由48比特长(6字节),16进制的数字组成.0-23位叫做组织唯一标志符(organizationally unique,是识别LAN(局域网)节点的标识.24-47位是由厂家自己分配。其中第40位是组播地址标志位。网卡的物理地址通常是由网卡生产厂家烧入网卡的EPROM(一种闪存芯片,通常可以通过程序擦写),它存储的是传输数据时真正赖以标识发出数据的电脑和接收数据的主机的地址。
Android平台提供的蓝牙API去实现蓝牙设备之间的通信,蓝牙设备之间的通信主要包括了四个步骤:设置蓝牙设备、寻找局域网内可能或者匹配的设备、连接设备和设备之间的数据传输。以下是建立蓝牙连接的所需要的一些基本类:
BluetoothAdapter类:代表了一个本地的蓝牙适配器。它是所有蓝牙交互的的入口点。利用它你可以发现其他蓝牙设备,查询绑定了的设备,使用已知的MAC地址实例化一个蓝牙设备和建立一个BluetoothServerSocket(作为服务器端)来监听来自其他设备的连接。
BluetoothDevice类:代表了一个远端的蓝牙设备,使用它请求远端蓝牙设备连接或者获取远端蓝牙设备的名称、地址、种类和绑定状态。(其信息是封装在bluetoothsocket中)。
Bluetoothsocket类:代表了一个蓝牙套接字的接口(类似于tcp中的套接字),它是应用程序通过输入、输出流与其他蓝牙设备通信的连接点。
Blueboothserversocket类:代表打开服务连接来监听可能到来的连接请求(属于server端),为了连接两个蓝牙设备必须有一个设备作为服务器打开一个服务套接字。当远端设备发起连接连接请求的时候,并且已经连接到了的时候,Blueboothserversocket类将会返回一个bluetoothsocket。
Bluetoothclass类:描述了一个蓝牙设备的一般特点和能力。它的只读属性集定义了设备的主、次设备类和一些相关服务。然而,它并没有准确地描述所有该设备所支持的蓝牙文件和服务,而是作为对设备种类来说的一个小小暗示。下面说说具体的编程实现 1.启动蓝牙功能:
首先通过调用静态方法getDefaultAdapter()获取蓝牙适配器BluetoothAdapter,以后你就可以使用该对象了。如果返回为空,the story is over。例如:
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();if(mBluetoothAdapter == null){ // Device does not support Bluetooth } 其次,调用isEnabled()来查询当前蓝牙设备的状态,如果返回为false,则表示蓝牙设备没有开启,接下来你需要封装一个ACTION_REQUEST_ENABLE请求到intent里面,调用startActivityForResult()方法使能蓝牙设备,例如:
if(!mBluetoothAdapter.isEnabled()){ Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);}
2.查找设备:
使用BluetoothAdapter类里的方法,你可以查找远端设备(大概十米以内)或者查询在你手机上已经匹配(或者说绑定)的其他手机了。当然需要确定对方蓝牙设备已经开启或者已经开启了“被发现使能”功能(对方设备是可以被发现的是你能够发起连接的前提条件)。如果该设备是可以被发现的,会反馈回来一些对方的设备信息,比如名字、MAC地址等,利用这些信息,你的设备就可以选择去向对方初始化一个连接。
如果你是第一次与该设备连接,那么一个配对的请求就会自动的显示给用户。当设备配对好之后,他的一些基本信息(主要是名字和MAC)被保存下来并可以使用蓝牙的API来读取。使用已知的MAC地址就可以对远端的蓝牙设备发起连接请求。
匹配好的设备和连接上的设备的不同点:匹配好只是说明对方设备发现了你的存在,并拥有一个共同的识别码,并且可以连接。连接上:表示当前设备共享一个RFCOMM信道并且两者之间可以交换数据。也就是是说蓝牙设备在建立RFCOMM信道之前,必须是已经配对好了的。
3.查询匹配好的设备:
在建立连接之前你必须先查询配对好了的蓝牙设备集(你周围的蓝牙设备可能不止一个),以便你选取哪一个设备进行通信,例如你可以你可以查询所有配对的蓝牙设备,并使用一个数组适配器将其打印显示出来:
Set
4.扫描设备:
扫描设备,只需要简单的调用startDiscovery()方法,这个扫描的过程大概持续是12秒,应用程序为了ACTION_FOUND动作需要注册一个BroadcastReceiver来接受设备扫描到的信息。对于每一个设备,系统都会广播ACTION_FOUND动作。例如: // Create a BroadcastReceiver for ACTION_FOUND private final BroadcastReceiver mReceiver = new BroadcastReceiver(){ public void onReceive(Context context, Intent intent){ String action = intent.getAction();// When discovery finds a device if(BluetoothDevice.ACTION_FOUND.equals(action)){ // Get the BluetoothDevice object from the Intent BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);// Add the name and address to an array adapter to show in a ListView mArrayAdapter.add(device.getName()+ “n” + device.getAddress());} } };// Register the BroadcastReceiver IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);registerReceiver(mReceiver, filter);// Don't forget to unregister during onDestroy 注意:扫描的过程是一个很耗费资源的过程,一旦你找到你需要的设备之后,在发起连接请求之前,确保你的程序调用cancelDiscovery()方法停止扫描。显然,如果你已经连接上一个设备,启动扫描会减少你的通信带宽。
5.使能被发现:Enabling discoverability 如果你想使你的设备能够被其他设备发现,将ACTION_REQUEST_DISCOVERABLE动作封装在intent中并调用startActivityForResult(Intent, int)方法就可以了。他将在不使你应用程序退出的情况下使你的设备能够被发现。缺省情况下的使能时间是120秒,当然你可以可以通过添加EXTRA_DISCOVERABLE_DURATION字段来改变使能时间(最大不超过300秒,这是出于对你设备上的信息安全考虑)。例如: Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);startActivity(discoverableIntent);运行该段代码之后,系统会弹出一个对话框来提示你启动设备使能被发现(此过程中如果你的蓝牙功能没有开启,系统会帮你开启),并且如果你准备对该远端设备发现一个连接,你不需要开启使能设备被发现功能,因为该功能只是在你的应用程序作为服务器端的时候才需要。
6.连接设备:
在应用程序中,想建立两个蓝牙设备之间的连接,必须实现客户端和服务器端的代码(因为任何一个设备都必须可以作为服务端或者客户端)。一个开启服务来监听,一个发起连接请求(使用服务器端设备的MAC地址)。当他们都拥有一个蓝牙套接字在同一RFECOMM信道上的时候,可以认为他们之间已经连接上了。服务端和客户端通过不同的方式或其他们的蓝牙套接字。当一个连接监听到的时候,服务端获取到蓝牙套接字。当客户可打开一个FRCOMM信道给服务器端的时候,客户端获取到蓝牙套接字。
注意:在此过程中,如果两个蓝牙设备还没有配对好的,android系统会通过一个通知或者对话框的形式来通知用户。RFCOMM连接请求会在用户选择之前阻塞。如下图:
7.服务端的连接:
当你想要连接两台设备时,一个必须作为服务端(通过持有一个打开的BluetoothServerSocket),目的是监听外来连接请求,当监听到以后提供一个连接上的BluetoothSocket给客户端,当客户端从BluetoothServerSocket得到BluetoothSocket以后就可以销毁BluetoothServerSocket,除非你还想监听更多的连接请求。
建立服务套接字和监听连接的基本步骤:
首先通过调用listenUsingRfcommWithServiceRecord(String, UUID)方法来获取BluetoothServerSocket对象,参数String代表了该服务的名称,UUID代表了和客户端连接的一个标识(128位格式的字符串ID,相当于PIN码),UUID必须双方匹配才可以建立连接。其次调用accept()方法来监听可能到来的连接请求,当监听到以后,返回一个连接上的蓝牙套接字BluetoothSocket。最后,在监听到一个连接以后,需要调用close()方法来关闭监听程序。(一般蓝牙设备之间是点对点的传输)
注意:accept()方法不应该放在主Acitvity里面,因为它是一种阻塞调用(在没有监听到连接请求之前程序就一直停在那里)。解决方法是新建一个线程来管理。例如: private class AcceptThread extends Thread { private final BluetoothServerSocket mmServerSocket;public AcceptThread(){ // Use a temporary object that is later assigned to mmServerSocket, // because mmServerSocket is final BluetoothServerSocket tmp = null;try { // MY_UUID is the app's UUID string, also used by theclient code tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);} catch(IOException e){ } mmServerSocket = tmp;} public void run(){ BluetoothSocket socket = null;// Keep listening until exception occurs or a socket is returned while(true){ try { socket = mmServerSocket.accept();} catch(IOException e){ break;} // If a connection was accepted if(socket!= null){ // Do work to manage the connection(in a separate thread)manageConnectedSocket(socket);mmServerSocket.close();break;} } } /** Will cancel the listening socket, and cause the thread to finish */ public void cancel(){ try { mmServerSocket.close();} catch(IOException e){ } } }
8.客户端的连接:
为了初始化一个与远端设备的连接,需要先获取代表该设备的一个BluetoothDevice对象。通过BluetoothDevice对象来获取BluetoothSocket并初始化连接,具体步骤:
使用BluetoothDevice对象里的方法createRfcommSocketToServiceRecord(UUID)来获取BluetoothSocket。UUID就是匹配码。然后,调用connect()方法来。如果远端设备接收了该连接,他们将在通信过程中共享RFFCOMM信道,并且connect()方法返回。例如: private class ConnectThread extends Thread { private final BluetoothSocket mmSocket;private final BluetoothDevice mmDevice;public ConnectThread(BluetoothDevice device){ // Use a temporary object that is later assigned to mmSocket, // because mmSocket is final BluetoothSocket tmp = null;mmDevice = device;// Get a BluetoothSocket to connect with the given BluetoothDevice try { // MY_UUID is the app's UUID string, also used by the server code tmp = device.createRfcommSocketToServiceRecord(MY_UUID);} catch(IOException e){ } mmSocket = tmp;}
public void run(){ // Cancel discovery because it will slow down the connection mAdapter.cancelDiscovery();try { // Connect the device through the socket.This will block // until it succeeds or throws an exception mmSocket.connect();} catch(IOException connectException){ // Unable to connect;close the socket and get out try { mmSocket.close();} catch(IOException closeException){ } return;} // Do work to manage the connection(in a separate thread)manageConnectedSocket(mmSocket);}
注意:conncet()方法也是阻塞调用,一般建立一个独立的线程中来调用该方法。在设备discover过程中不应该发起连接connect(),这样会明显减慢速度以至于连接失败。且数据传输完成只有调用close()方法来关闭连接,这样可以节省系统内部资源。
9.管理连接(主要涉及数据的传输):
当设备连接上以后,每个设备都拥有各自的BluetoothSocket。现在你就可以实现设备之间数据的共享了。
1> 首先通过调用getInputStream()和getOutputStream()方法来获取输入输出流。然后通过调用read(byte[])和write(byte[]).方法来读取或者写数据。
2> 实现细节:以为读取和写操作都是阻塞调用,需要建立一个专用现成来管理。3>
private class ConnectedThread extends Thread { private final BluetoothSocket mmSocket;private final InputStream mmInStream;private final OutputStream mmOutStream;public ConnectedThread(BluetoothSocket socket){ mmSocket = socket;InputStream tmpIn = null;OutputStream tmpOut = null;// Get the input and output streams, using temp objects because // member streams are final try { tmpIn = socket.getInputStream();tmpOut = socket.getOutputStream();} catch(IOException e){ } mmInStream = tmpIn;mmOutStream = tmpOut;} public void run(){ byte[] buffer = new byte[1024];// buffer store for the stream int bytes;// bytes returned from read()// Keep listening to the InputStream until an exception occurs while(true){ try { // Read from the InputStream bytes = mmInStream.read(buffer);// Send the obtained bytes to the UI Activity mHandler.obtainMessage(MESSAGE_READ, bytes,-1, buffer).sendToTarget();} catch(IOException e){ break;} } } /* Call this from the main Activity to send data to the remote device */ public void write(byte[] bytes){ try { mmOutStream.write(bytes);} catch(IOException e){ } } /* Call this from the main Activity to shutdown the connection */ public void cancel(){ try { mmSocket.close();} catch(IOException e){ } } }
第三篇:iOS 蓝牙使用小结 bluetooth
iOS 蓝牙使用小结 bluetooth
现将创建蓝牙工程的要点总结一下,由于工程主要涉及中心模式,所以只总结中心模式的用法 1,引入CoreBluetooth.framework 2,实现蓝牙协议,如:.h文件如下
@protocol CBCentralManagerDelegate;
@protocol CBPeripheralDelegate;@interface ViewController : UIViewController.m文件如下
#import “CoreBluetooth/CoreBluetooth.h”
另外还有代理部分请自行添加3,下面是使蓝牙动起来的过程
3.1创建CBCentralManager实例
self.cbCentralMgr = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
设置代理,比如:
self.cbCentralMgr.delegate = self;创建数组管理外设
self.peripheralArray = [NSMutableArray array];3.2扫描周围的蓝牙
实际上周围的蓝牙如果可被发现,则会一直往外发送广告消息,中心设备就是通过接收这些消息来发现周围的蓝牙的
NSDictionary * dic = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:false],CBCentralManagerScanOptionAllowDuplicatesKey, nil];
[self.cbCentralMgr scanForPeripheralsWithServices:nil options:dic];3.3发现一个蓝牙设备
也就是收到了一个周围的蓝牙发来的广告信息,这是CBCentralManager会通知代理来处理
-(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
}
如果周围的蓝牙有多个,则这个方法会被调用多次,你可以通过tableView或其他的控件把这些周围的蓝牙的信息打印出来
3.4连接一个蓝牙
[self.cbCentralMgr connectPeripheral:peripheral options:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:CBConnectPeripheralOptionNotifyOnDisconnectionKey]];
一个中心设备可以同时连接多个周围的蓝牙设备
当连接上某个蓝牙之后,CBCentralManager会通知代理处理-(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral { }因为在后面我们要从外设蓝牙那边再获取一些信息,并与之通讯,这些过程会有一些事件可能要处理,所以要给这个外设设置代理,比如: peripheral.delegate = self;
3.5查询蓝牙服务
[peripheral discoverServices:nil];返回的蓝牙服务通知通过代理实现
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {for(CBService* service in peripheral.services){
} }
3.6查询服务所带的特征值
[peripheral discoverCharacteristics:nil forService:service];
返回的蓝牙特征值通知通过代理实现-(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {for(CBCharacteristic * characteristic in service.characteristics){ } }
3.7给蓝牙发数据
[peripheral writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
这时还会触发一个代理事件
-(void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { }
3.8处理蓝牙发过来的数据
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error { }3.9 retrievePeripheralsWithIdentifiers 使用例子
-(IBAction)Retrieve:(id)Sender { [self.tvLog setText:@“"];
NSMutableArray * Identifiers = [NSMutableArray array];
for(CBPeripheral * peripheral in self.peripheralArray){ [Identifiers addObject:peripheral.identifier];}[self addLog:@”[self.cbCentralMgr retrievePeripheralsWithIdentifiers:self.PeripheralIdentifiers]“];
self.retrievePeripherals = [self.cbCentralMgr retrievePeripheralsWithIdentifiers:Identifiers];
for(CBPeripheral* peripheral in self.retrievePeripherals){ [self addLog:[NSString stringWithFormat: @”%@ name:%@“,peripheral,peripheral.name]];}
[self.tableViewPeripheral reloadData];}3.10 retrieveConnectedPeripheralsWithServices 使用例子-(IBAction)Retrieve:(id)Sender { [self.tvLog setText:@”“];
NSMutableArray * services = [NSMutableArray array];
for(CBPeripheral * peripheral in self.peripheralArray){
if(peripheral.isConnected){
for(CBService *service in peripheral.services){ [services addObject:service.UUID];} } }[self addLog:@”[self.cbCentralMgr retrieveConnectedPeripheralsWithServices:peripheral.services]“];
self.retrievePeripherals = [self.cbCentralMgr retrieveConnectedPeripheralsWithServices:services];
for(CBPeripheral* peripheral in self.retrievePeripherals){ [self addLog:[NSString stringWithFormat: @”%@ name:%@",peripheral,peripheral.name]];}
[self.tableViewPeripheral reloadData];}
第四篇:蓝牙通信原理
蓝牙耳机的工作原理:
关于音频流的蓝牙传输可以通过两个方式:
1)通过PCM接口来传送
2)通过模拟UART来传送
下面分别来讲述:
1)通过PCM接口来传送
通过音频播放器(eg: Media Player)来打开音频文件,调用Audio驱动,音频文件通过解码后,由PCM输出到Host端蓝牙模块的PCM输入端,接着,经过蓝牙模块的处理后,由RF无线模块发送给Client 端蓝牙设备。
Client 端蓝牙设备经由无线接收模块后,滤波,稳压,经微处理芯片处理后,直接由Speaker播放。
2)通过模拟UART来传送
通过设置注册表【HKEY_LOCAL_MACHINEServicesBTAGSVC】IsEnabled =1 使得系统引导时自动加载语音网关(AG)服务。
首先,通过手动配置建立Host端蓝牙设备与Client端蓝牙设备ACL链接(面向无连接的异步链路),接着在Applicaiton或Audio Driver中调用
IOCTL_AG_OPEN_AUDIO,重新建立Host端蓝牙设备与Client端蓝牙设备SCO链接(面向连接的同步链路),接着AG自动发送
waveOutMessage((HWAVEOUT)i, WODM_BT_SCO_AUDIO_CONTROL, 0, TRUE);从而建立了Audio至蓝牙芯片之间的通道,也即,实现了音频流到蓝牙模块的传送。
然后,经由主机端蓝牙模块将音频流打包经由RF模块发送出去。
客户端蓝牙耳机接收到无线音频包后,滤波,稳压,经微处理芯片处理后,由PCM传送给音频编解码器芯片,最后,由Speaker播放。
第五篇:蓝牙工程师岗位职责
1.蓝牙产品原理图,PCB图设计(新产品设计)。
2.样机调试。
3.工程文件的拟定。
4.产品试产、量产的跟进及问题分析处理。