查阅了好多资料,看了好多东西,感觉VC上的蓝牙编程好乱,看得头都大了,下面把看到的和学到的整理一下(如有错误,请指正):
VC蓝牙编程貌似方式不止一种,网上比较流行的是IVT的BlueSoleil_SDK,用windows上的两种bluetooth的开放接口,一种是以熟悉的windows sockets方式,另外一种是新加入的BlueTooth APIs方式。在SDK的samples中只提到了windows sockets的方式。还有就是BlueZ的方式。
BlueSoleil_SDK相关资料:http://msdn.microsoft.com/en-us/library/aa362901(VS.85).aspx
下面具体说一下BlueZ(在下只是略懂皮毛,如有误解,望大虾们指点)
简单的说,BlueZ是什么呢??
答:BlueZ是一款强大的蓝牙通信协议栈,它扩展的API使得用户方便操纵大量的蓝牙资源。
下面是网上的一个实例,希望大家看过实例后稍微理解一些:
这个例子是一个查找周边蓝牙设备的简单应用程序。程序首先获取系统的蓝牙设备号,扫描周边的蓝牙设备,然后查找每一个被搜索到的蓝牙设备的名称。后边有对数据结构和函数的详细描述。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
int main(int argc, char **argv)
{
inquiry_info *ii = NULL;
int max_rsp, num_rsp;
int dev_id, sock, len, flags;
int i;
char addr[19] = { 0 };
char name[248] = { 0 };
dev_id = hci_get_route(NULL);
sock = hci_open_dev( dev_id );
if (dev_id < 0 || sock < 0) {
perror("opening socket");
exit(1);
}
len = 8;
max_rsp = 255;
flags = IREQ_CACHE_FLUSH;
ii = (inquiry_info*)malloc(max_rsp * sizeof(inquiry_info));
num_rsp = hci_inquiry(dev_id, len, max_rsp, NULL, &ii, flags);
if( num_rsp < 0 ) perror("hci_inquiry");
for (i = 0; i < num_rsp; i++) {
ba2str(&(ii+i)->bdaddr, addr);
memset(name, 0, sizeof(name));
if (hci_read_remote_name(sock, &(ii+i)->bdaddr, sizeof(name),
name, 0) < 0)
strcpy(name, "[unknown]");
printf("%s %s\n", addr, name);
}
free( ii );
close( sock );
return 0;
}
编译需要使用gcc链接libbluetooth这个库。
# gcc -o simplescan simplescan.c -lbluetooth
下面是相关解释:
typedef struct {
uint8_t b[6];
} __attribute__((packed)) bdaddr_t;
蓝牙设备的地址采用结构体bdaddr_t来描述,BlueZ中队蓝牙地址的存储和操纵都使用bdaddr_t结构体,BlueZ提供两个函数来进行字符串到蓝牙地址的转换。
int str2ba( const char *str, bdaddr_t *ba );
int ba2str( const bdaddr_t *ba, char *str );
str2ba把形如XX:XX:XX:XX:XX:XX(XX标识48位蓝牙地址的16进制的一个字节)的字符串转化6字节的bdaddr_t结构, ba2str完成相反的功能。
本地蓝牙适配器被分配一个从0开始的识别号码。程序在分配系统资源时必须指定使用那一个蓝牙适配器,通常的话系统只有一个蓝牙适配器,把参数NULL传给hci_get_route可以获得第一个有效的蓝牙适配器识别号。
int hci_get_route( bdaddr_t *bdaddr );
int hci_open_dev( int dev_id );
[note]将适配器的设备号指定为0是不恰当的,因为它并不总代表第一个可用的蓝牙适配器。例如系统有两个蓝牙适配器,第一个被disable掉了,那么第一个有效的设备号就是2。
如果存在多个蓝牙适配器,选择"01:23:45:67:89:AB"作为蓝牙适配器的地址, 将指示这个地址的指针char *representation传给hci_devid函数,用这个函数替代hci_get_route。
很多蓝牙操作都需要打开一个套接口, hci_open_dev函数可以打开特定资源号的一个套接口,确切的说hci_open_dev打开的套接字建立了一条和本地蓝牙适配器控制器的连接,而不是和远端蓝牙设备的连接。使用这个套接口发送命令到蓝牙控制器可以实现底层的蓝牙操作,这部分在4.5中有详细的讨论。
选择好本地蓝牙适配器并进行系统资源分配后,程序就可以开始扫描周边的蓝牙设备了,在这个例程中,hci_inquiry函数完成对蓝牙设备的搜寻,并将返回的设备信息数据记录在变量ii中。遇到错误时,它将返回-1并设置errno变量。
int hci_inquiry(int dev_id, int len, int max_rsp, const uint8_t *lap,
inquiry_info **ii, long flags);
hci_inquiry 的参数需要使用设备资源号而非套接口,所以我们使用hci_get_route函数的返回值dev_id传递给它。查询时间最长持续1.28 * len秒。max_rsp个设别返回的信息都被存储在变量ii中,这个变量必须有足够的空间来存储max_rsp返回的结果。我们推荐max_rsp取值255来完成标准10.24秒的查询工作。
如果标志位flag设置为IREQ_CACHE_FLUSH,那么在进行查询操作时会把先前一次查询记录的cache刷新,否则flag设置为0的话,即便先前查询的设备已经不处于有效范围内,先前查询的记录也将被返回。
inquiry_info结构体定义如下
typedef struct {
bdaddr_t bdaddr;
uint8_t pscan_rep_mode;
uint8_t pscan_period_mode;
uint8_t pscan_mode;
uint8_t dev_class[3];
uint16_t clock_offset;
} __attribute__ ((packed)) inquiry_info;
在大多数场合,我们仅用到成员bdaddr,它标识了设备的蓝牙地址。有些场合我们也会用到成员dev_class, 它标识了被检测到的蓝牙设备的一些信息(例如,识别这个设备是打印设备,电话,个人电脑等),详细地对应关系可以参见蓝牙设备分配号[3]。其余的成员在用于底层通信,一般情况并不常用。感兴趣的读者可以阅读蓝牙内核规范[4]获取更多的信息。一旦周围的蓝牙设备和其蓝牙地址被检测到,程序可以将此设备的名称提供给用户,hci_read_remote_name函数可以完成这个功能。
int hci_read_remote_name(int sock, const bdaddr_t *ba, int len,
char *name, int timeout)
hci_read_remote_name函数在规定的超时时间内使用套接口通过蓝牙地址ba去获取蓝牙设备的名称,成功返回0,并将获取的蓝牙设备名称存入name中;失败时返回-1并设置相应的errno。
VC蓝牙编程貌似方式不止一种,网上比较流行的是IVT的BlueSoleil_SDK,用windows上的两种bluetooth的开放接口,一种是以熟悉的windows sockets方式,另外一种是新加入的BlueTooth APIs方式。在SDK的samples中只提到了windows sockets的方式。还有就是BlueZ的方式。
BlueSoleil_SDK相关资料:http://msdn.microsoft.com/en-us/library/aa362901(VS.85).aspx
下面具体说一下BlueZ(在下只是略懂皮毛,如有误解,望大虾们指点)
简单的说,BlueZ是什么呢??
答:BlueZ是一款强大的蓝牙通信协议栈,它扩展的API使得用户方便操纵大量的蓝牙资源。
下面是网上的一个实例,希望大家看过实例后稍微理解一些:
这个例子是一个查找周边蓝牙设备的简单应用程序。程序首先获取系统的蓝牙设备号,扫描周边的蓝牙设备,然后查找每一个被搜索到的蓝牙设备的名称。后边有对数据结构和函数的详细描述。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
int main(int argc, char **argv)
{
inquiry_info *ii = NULL;
int max_rsp, num_rsp;
int dev_id, sock, len, flags;
int i;
char addr[19] = { 0 };
char name[248] = { 0 };
dev_id = hci_get_route(NULL);
sock = hci_open_dev( dev_id );
if (dev_id < 0 || sock < 0) {
perror("opening socket");
exit(1);
}
len = 8;
max_rsp = 255;
flags = IREQ_CACHE_FLUSH;
ii = (inquiry_info*)malloc(max_rsp * sizeof(inquiry_info));
num_rsp = hci_inquiry(dev_id, len, max_rsp, NULL, &ii, flags);
if( num_rsp < 0 ) perror("hci_inquiry");
for (i = 0; i < num_rsp; i++) {
ba2str(&(ii+i)->bdaddr, addr);
memset(name, 0, sizeof(name));
if (hci_read_remote_name(sock, &(ii+i)->bdaddr, sizeof(name),
name, 0) < 0)
strcpy(name, "[unknown]");
printf("%s %s\n", addr, name);
}
free( ii );
close( sock );
return 0;
}
编译需要使用gcc链接libbluetooth这个库。
# gcc -o simplescan simplescan.c -lbluetooth
下面是相关解释:
typedef struct {
uint8_t b[6];
} __attribute__((packed)) bdaddr_t;
蓝牙设备的地址采用结构体bdaddr_t来描述,BlueZ中队蓝牙地址的存储和操纵都使用bdaddr_t结构体,BlueZ提供两个函数来进行字符串到蓝牙地址的转换。
int str2ba( const char *str, bdaddr_t *ba );
int ba2str( const bdaddr_t *ba, char *str );
str2ba把形如XX:XX:XX:XX:XX:XX(XX标识48位蓝牙地址的16进制的一个字节)的字符串转化6字节的bdaddr_t结构, ba2str完成相反的功能。
本地蓝牙适配器被分配一个从0开始的识别号码。程序在分配系统资源时必须指定使用那一个蓝牙适配器,通常的话系统只有一个蓝牙适配器,把参数NULL传给hci_get_route可以获得第一个有效的蓝牙适配器识别号。
int hci_get_route( bdaddr_t *bdaddr );
int hci_open_dev( int dev_id );
[note]将适配器的设备号指定为0是不恰当的,因为它并不总代表第一个可用的蓝牙适配器。例如系统有两个蓝牙适配器,第一个被disable掉了,那么第一个有效的设备号就是2。
如果存在多个蓝牙适配器,选择"01:23:45:67:89:AB"作为蓝牙适配器的地址, 将指示这个地址的指针char *representation传给hci_devid函数,用这个函数替代hci_get_route。
很多蓝牙操作都需要打开一个套接口, hci_open_dev函数可以打开特定资源号的一个套接口,确切的说hci_open_dev打开的套接字建立了一条和本地蓝牙适配器控制器的连接,而不是和远端蓝牙设备的连接。使用这个套接口发送命令到蓝牙控制器可以实现底层的蓝牙操作,这部分在4.5中有详细的讨论。
选择好本地蓝牙适配器并进行系统资源分配后,程序就可以开始扫描周边的蓝牙设备了,在这个例程中,hci_inquiry函数完成对蓝牙设备的搜寻,并将返回的设备信息数据记录在变量ii中。遇到错误时,它将返回-1并设置errno变量。
int hci_inquiry(int dev_id, int len, int max_rsp, const uint8_t *lap,
inquiry_info **ii, long flags);
hci_inquiry 的参数需要使用设备资源号而非套接口,所以我们使用hci_get_route函数的返回值dev_id传递给它。查询时间最长持续1.28 * len秒。max_rsp个设别返回的信息都被存储在变量ii中,这个变量必须有足够的空间来存储max_rsp返回的结果。我们推荐max_rsp取值255来完成标准10.24秒的查询工作。
如果标志位flag设置为IREQ_CACHE_FLUSH,那么在进行查询操作时会把先前一次查询记录的cache刷新,否则flag设置为0的话,即便先前查询的设备已经不处于有效范围内,先前查询的记录也将被返回。
inquiry_info结构体定义如下
typedef struct {
bdaddr_t bdaddr;
uint8_t pscan_rep_mode;
uint8_t pscan_period_mode;
uint8_t pscan_mode;
uint8_t dev_class[3];
uint16_t clock_offset;
} __attribute__ ((packed)) inquiry_info;
在大多数场合,我们仅用到成员bdaddr,它标识了设备的蓝牙地址。有些场合我们也会用到成员dev_class, 它标识了被检测到的蓝牙设备的一些信息(例如,识别这个设备是打印设备,电话,个人电脑等),详细地对应关系可以参见蓝牙设备分配号[3]。其余的成员在用于底层通信,一般情况并不常用。感兴趣的读者可以阅读蓝牙内核规范[4]获取更多的信息。一旦周围的蓝牙设备和其蓝牙地址被检测到,程序可以将此设备的名称提供给用户,hci_read_remote_name函数可以完成这个功能。
int hci_read_remote_name(int sock, const bdaddr_t *ba, int len,
char *name, int timeout)
hci_read_remote_name函数在规定的超时时间内使用套接口通过蓝牙地址ba去获取蓝牙设备的名称,成功返回0,并将获取的蓝牙设备名称存入name中;失败时返回-1并设置相应的errno。